extern crate alloc;
use alloc::vec;
use more_asserts::assert_ge;
use super::{assert_all_zero, assert_ct_ne};
use crate::{
aead::{Aead, Nonce, OpenError},
csprng::{Csprng, Random},
};
#[macro_export]
macro_rules! for_each_aead_test {
($callback:ident) => {
$crate::__apply! {
$callback,
test_basic,
test_new_key,
test_round_trip,
test_in_place_round_trip,
test_bad_key,
test_bad_nonce,
test_bad_ciphertext,
test_bad_ad,
test_bad_tag,
}
};
}
pub use for_each_aead_test;
#[macro_export]
macro_rules! test_aead {
($name:ident, $aead:ty $(, AeadTest::$vectors:ident)?) => {
mod $name {
#[allow(unused_imports)]
use super::*;
$crate::test_aead!($aead $(, AeadTest::$vectors)?);
}
};
($aead:ty $(, AeadTest::$vectors:ident)?) => {
macro_rules! __aead_test {
($test:ident) => {
#[test]
fn $test() {
$crate::test_util::aead::$test::<$aead, _>(&mut $crate::default::Rng)
}
}
}
$crate::for_each_aead_test!(__aead_test);
$(
#[test]
fn vectors() {
$crate::test_util::vectors::test_aead::<$aead>(
$crate::test_util::vectors::AeadTest::$vectors,
);
}
)?
};
}
pub use test_aead;
const GOLDEN: &[u8] = b"hello, world!";
const AD: &[u8] = b"some additional data";
pub fn test_basic<A: Aead, R: Csprng>(_rng: &R) {
assert_ge!(A::KEY_SIZE, 16);
assert_ge!(A::MAX_PLAINTEXT_SIZE, u64::from(u32::MAX));
assert_eq!(
A::MAX_CIPHERTEXT_SIZE,
A::MAX_PLAINTEXT_SIZE + A::OVERHEAD as u64
);
assert_ge!(A::MAX_ADDITIONAL_DATA_SIZE, u64::from(u32::MAX));
}
pub fn test_new_key<A: Aead, R: Csprng>(rng: &R) {
let k1 = A::Key::random(rng);
let k2 = A::Key::random(rng);
assert_ct_ne!(k1, k2);
}
pub fn test_round_trip<A: Aead, R: Csprng>(rng: &R) {
let key = A::Key::random(rng);
let nonce = Nonce::<A::NonceSize>::default();
assert_all_zero!(nonce);
let ciphertext = {
let mut dst = vec![0u8; GOLDEN.len() + A::OVERHEAD];
A::new(&key)
.seal(&mut dst[..], nonce.as_ref(), GOLDEN, AD)
.expect("unable to encrypt data");
dst
};
let plaintext = {
let mut dst = vec![0u8; ciphertext.len() - A::OVERHEAD];
A::new(&key)
.open(&mut dst[..], nonce.as_ref(), &ciphertext, AD)
.expect("unable to decrypt data");
dst
};
assert_eq!(&plaintext, GOLDEN, "round trip test failed");
}
pub fn test_in_place_round_trip<A: Aead, R: Csprng>(rng: &R) {
let key = A::Key::random(rng);
let nonce = Nonce::<A::NonceSize>::default();
assert_all_zero!(nonce);
let ciphertext = {
let mut data = vec![0u8; GOLDEN.len() + A::OVERHEAD];
let (out, tag) = data.split_at_mut(GOLDEN.len());
out.clone_from_slice(GOLDEN);
A::new(&key)
.seal_in_place(nonce.as_ref(), out, tag, AD)
.expect("unable to encrypt data in-place");
data
};
let plaintext = {
let mut data = ciphertext.to_vec();
let (out, tag) = data.split_at_mut(GOLDEN.len());
A::new(&key)
.open_in_place(nonce.as_ref(), out, tag, AD)
.expect("unable to decrypt data in-place");
out.to_vec()
};
assert_eq!(&plaintext, GOLDEN, "in-place round trip test failed");
}
pub fn test_bad_key<A: Aead, R: Csprng>(rng: &R) {
let nonce = Nonce::<A::NonceSize>::default();
assert_all_zero!(nonce);
let ciphertext = {
let key = A::Key::random(rng);
let mut dst = vec![0u8; GOLDEN.len() + A::OVERHEAD];
A::new(&key)
.seal(&mut dst[..], nonce.as_ref(), GOLDEN, AD)
.expect("unable to encrypt data");
dst
};
let key = A::Key::random(rng);
let mut dst = vec![0u8; ciphertext.len() - A::OVERHEAD];
let err = A::new(&key)
.open(&mut dst[..], nonce.as_ref(), &ciphertext, AD)
.expect_err("decryption should have failed due to a different key");
assert_eq!(err, OpenError::Authentication);
}
pub fn test_bad_nonce<A: Aead, R: Csprng>(rng: &R) {
let key = A::Key::random(rng);
let ciphertext = {
let mut nonce = Nonce::<A::NonceSize>::default();
assert_all_zero!(nonce);
nonce.as_mut().fill(b'A');
let mut dst = vec![0u8; GOLDEN.len() + A::OVERHEAD];
A::new(&key)
.seal(&mut dst[..], nonce.as_ref(), GOLDEN, AD)
.expect("unable to encrypt data");
dst
};
let mut nonce = Nonce::<A::NonceSize>::default();
assert_all_zero!(nonce);
nonce.as_mut().fill(b'B');
let mut dst = vec![0u8; ciphertext.len() - A::OVERHEAD];
let err = A::new(&key)
.open(&mut dst[..], nonce.as_ref(), &ciphertext, AD)
.expect_err("decryption should have failed due to a modified nonce");
assert_eq!(err, OpenError::Authentication);
}
pub fn test_bad_ad<A: Aead, R: Csprng>(rng: &R) {
let key = A::Key::random(rng);
let nonce = Nonce::<A::NonceSize>::default();
assert_all_zero!(nonce);
let ciphertext = {
let mut dst = vec![0u8; GOLDEN.len() + A::OVERHEAD];
A::new(&key)
.seal(&mut dst[..], nonce.as_ref(), GOLDEN, AD)
.expect("unable to encrypt data");
dst
};
let mut dst = vec![0u8; ciphertext.len() - A::OVERHEAD];
let err = A::new(&key)
.open(&mut dst[..], nonce.as_ref(), &ciphertext, b"some bad AD")
.expect_err("decryption should have failed due to a modified AD");
assert_eq!(err, OpenError::Authentication);
}
pub fn test_bad_ciphertext<A: Aead, R: Csprng>(rng: &R) {
let key = A::Key::random(rng);
let nonce = Nonce::<A::NonceSize>::default();
assert_all_zero!(nonce);
let mut ciphertext = {
let mut dst = vec![0u8; GOLDEN.len() + A::OVERHEAD];
A::new(&key)
.seal(&mut dst[..], nonce.as_ref(), GOLDEN, AD)
.expect("unable to encrypt data");
dst
};
ciphertext[0] = ciphertext[0].wrapping_add(1);
let mut dst = vec![0u8; ciphertext.len() - A::OVERHEAD];
let err = A::new(&key)
.open(&mut dst[..], nonce.as_ref(), &ciphertext, AD)
.expect_err("decryption should have failed due to a modified ciphertext");
assert_eq!(err, OpenError::Authentication);
}
pub fn test_bad_tag<A: Aead, R: Csprng>(rng: &R) {
let key = A::Key::random(rng);
let nonce = Nonce::<A::NonceSize>::default();
assert_all_zero!(nonce);
let mut ciphertext = {
let mut dst = vec![0u8; GOLDEN.len() + A::OVERHEAD];
A::new(&key)
.seal(&mut dst[..], nonce.as_ref(), GOLDEN, AD)
.expect("unable to encrypt data");
dst
};
let n = ciphertext.len() - 1;
ciphertext[n] = ciphertext[n].wrapping_add(1);
let mut dst = vec![0u8; ciphertext.len() - A::OVERHEAD];
let err = A::new(&key)
.open(&mut dst[..], nonce.as_ref(), &ciphertext, AD)
.expect_err("decryption should have failed due to a modified auth tag");
assert_eq!(err, OpenError::Authentication);
}