use crate::{
CipherSuite,
crypto::buffer::{DecryptionBufferView, EncryptionBufferView},
error::{Result, SframeError},
header::Counter,
};
pub trait AeadEncrypt: TryFrom<CipherSuite, Error = SframeError> {
type Secret;
fn encrypt<'a, B>(&self, secret: &Self::Secret, buffer: B, counter: Counter) -> Result<()>
where
B: Into<EncryptionBufferView<'a>>;
}
pub trait AeadDecrypt: TryFrom<CipherSuite, Error = SframeError> {
type Secret;
fn decrypt<'a, B>(&self, secret: &Self::Secret, buffer: B, counter: Counter) -> Result<()>
where
B: Into<DecryptionBufferView<'a>>;
}
#[cfg(all(test, crypto_backend))]
mod test {
use crate::{
CipherSuite,
crypto::buffer::{
DecryptionBufferView, EncryptionBufferView, encryption::EncryptionBuffer,
},
error::SframeError,
header::{KeyId, SframeHeader},
key::{DecryptionKey, EncryptionKey},
util::test::assert_bytes_eq,
};
use rand::{RngExt, rng};
use test_case::test_case;
const KEY_MATERIAL: &str = "THIS_IS_RANDOM";
#[test]
fn encrypt_random_frame() {
let mut data = vec![0u8; 1024];
rng().fill(data.as_mut_slice());
let header = SframeHeader::new(0, 0);
let enc_key = EncryptionKey::derive_from(
CipherSuite::AesGcm256Sha512,
KeyId::default(),
KEY_MATERIAL.as_bytes(),
)
.unwrap();
let mut frame_buffer = Vec::new();
let mut encryption_buffer = EncryptionBuffer::try_allocate(
&mut frame_buffer,
enc_key.cipher_suite(),
&Vec::from(&header),
&data,
)
.unwrap();
enc_key
.encrypt(&mut encryption_buffer, header.counter())
.unwrap();
}
#[test_case(CipherSuite::AesGcm128Sha256; "AesGcm128Sha256")]
#[test_case(CipherSuite::AesGcm256Sha512; "AesGcm256Sha512")]
#[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_80; "AesCtr128HmacSha256_80"))]
#[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_64; "AesCtr128HmacSha256_64"))]
#[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_32; "AesCtr128HmacSha256_32"))]
fn decrypt_cipher_text_shorter_than_the_auth_tag(cipher_suite: CipherSuite) {
let dec_key =
DecryptionKey::derive_from(cipher_suite, KeyId::default(), KEY_MATERIAL.as_bytes())
.unwrap();
let header = SframeHeader::new(0, 0);
let mut aad = Vec::from(&header);
let mut data = vec![0u8; cipher_suite.auth_tag_len() - 1];
let decryption_buffer = DecryptionBufferView {
aad: &mut aad,
data: &mut data,
};
let result = dec_key.decrypt(decryption_buffer, header.counter());
assert!(matches!(
result.unwrap_err(),
SframeError::DecryptionFailure
));
}
#[test_case(CipherSuite::AesGcm128Sha256; "AesGcm128Sha256")]
#[test_case(CipherSuite::AesGcm256Sha512; "AesGcm256Sha512")]
#[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_80; "AesCtr128HmacSha256_80"))]
#[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_64; "AesCtr128HmacSha256_64"))]
#[cfg_attr(aes_ctr, test_case(CipherSuite::AesCtr128HmacSha256_32; "AesCtr128HmacSha256_32"))]
fn encrypt_decrypt_payload_shorter_than_the_auth_tag(cipher_suite: CipherSuite) {
const SHORT_PAYLOAD: &[u8] = b"abc";
assert!(SHORT_PAYLOAD.len() < cipher_suite.auth_tag_len());
let enc_key =
EncryptionKey::derive_from(cipher_suite, KeyId::default(), KEY_MATERIAL.as_bytes())
.unwrap();
let dec_key =
DecryptionKey::derive_from(cipher_suite, KeyId::default(), KEY_MATERIAL.as_bytes())
.unwrap();
let header = SframeHeader::new(0, 0);
let mut aad = Vec::from(&header);
let mut cipher_text = SHORT_PAYLOAD.to_vec();
let mut tag = vec![0u8; cipher_suite.auth_tag_len()];
enc_key
.encrypt(
EncryptionBufferView {
aad: &mut aad,
data: &mut cipher_text,
tag: &mut tag,
},
header.counter(),
)
.unwrap();
cipher_text.append(&mut tag);
let result = dec_key.decrypt(
DecryptionBufferView {
aad: &mut aad,
data: &mut cipher_text,
},
header.counter(),
);
assert!(
result.is_ok(),
"failed to decrypt a short payload: {result:?}"
);
cipher_text.truncate(cipher_text.len() - cipher_suite.auth_tag_len());
assert_bytes_eq(&cipher_text, SHORT_PAYLOAD);
}
}