extern crate alloc;
use alloc::vec;
use core::slice;
use generic_array::GenericArray;
use typenum::U64;
use crate::{
aead::IndCca2,
csprng::{Csprng, Random},
hpke::{Hpke, HpkeAead, HpkeKdf, HpkeKem, Mode, OpenCtx, SealCtx},
kdf::{Expand, Kdf, KdfError, Prk},
kem::DecapKey,
};
#[macro_export]
macro_rules! for_each_hpke_test {
($callback:ident) => {
$crate::__apply! {
$callback,
test_round_trip,
test_export,
}
};
}
pub use for_each_hpke_test;
#[macro_export]
macro_rules! test_hpke {
($name:ident, $kem:ty, $kdf:ty, $aead:ty $(, HpkeTest::$vectors:ident)? $(,)?) => {
mod $name {
#[allow(unused_imports)]
use super::*;
$crate::test_hpke!($kem, $kdf, $aead $(, HpkeTest::$vectors)?);
}
};
($kem:ty, $kdf:ty, $aead:ty $(, HpkeTest::$vectors:ident)? $(,)?) => {
macro_rules! __hpke_test {
($test:ident) => {
#[test]
fn $test() {
$crate::test_util::hpke::$test::<$kem, $kdf, $aead, _>(
&mut $crate::default::Rng,
)
}
};
}
$crate::for_each_hpke_test!(__hpke_test);
$(
#[test]
fn vectors() {
$crate::test_util::vectors::test_hpke::<$kem, $kdf, $aead>(
$crate::test_util::vectors::HpkeTest::$vectors,
);
}
)?
};
}
pub use test_hpke;
#[allow(non_snake_case)]
pub fn test_round_trip<K: HpkeKem, F: HpkeKdf, A: HpkeAead + IndCca2, R: Csprng>(rng: &R) {
const GOLDEN: &[u8] = b"some plaintext";
const AD: &[u8] = b"some additional data";
const INFO: &[u8] = b"some contextual binding";
let skR = K::DecapKey::random(rng);
let pkR = skR.public().expect("encap key should be valid");
let (enc, mut send) = Hpke::<K, F, A>::setup_send(rng, Mode::Base, &pkR, [INFO])
.expect("unable to create send context");
let info = INFO.iter().map(slice::from_ref);
let mut recv = Hpke::<K, F, A>::setup_recv(Mode::Base, &enc, &skR, info)
.expect("unable to create recv context");
let ciphertext = {
let mut dst = vec![0u8; GOLDEN.len() + SealCtx::<A>::OVERHEAD];
send.seal(&mut dst, GOLDEN, AD).expect("encryption failed");
dst
};
let plaintext = {
let mut dst = vec![0u8; ciphertext.len() - OpenCtx::<A>::OVERHEAD];
recv.open(&mut dst, &ciphertext, AD)
.expect("decryption failed");
dst
};
assert_eq!(plaintext, GOLDEN);
}
#[allow(non_snake_case)]
pub fn test_export<K: HpkeKem, F: HpkeKdf, A: HpkeAead + IndCca2, R: Csprng>(rng: &R) {
const INFO: &[u8] = b"some contextual binding";
let skR = K::DecapKey::random(rng);
let pkR = skR.public().expect("encap key should be valid");
let (enc, send) = Hpke::<K, F, A>::setup_send(rng, Mode::Base, &pkR, [INFO])
.expect("unable to create send context");
let recv = Hpke::<K, F, A>::setup_recv(Mode::Base, &enc, &skR, [INFO])
.expect("unable to create recv context");
#[derive(Debug, Default, Eq, PartialEq)]
struct Key(GenericArray<u8, U64>);
impl Expand for Key {
type Size = U64;
fn expand_multi<'a, K, I>(prk: &Prk<K::PrkSize>, info: I) -> Result<Self, KdfError>
where
K: Kdf,
I: IntoIterator<Item = &'a [u8]>,
I::IntoIter: Clone,
{
Ok(Self(Expand::expand_multi::<K, I>(prk, info)?))
}
}
const CTX: &[u8] = b"test_export";
let got1 = send.export::<Key>(CTX).expect("`SendCtx::export` failed");
let got2 = {
let mut key = Key::default();
send.export_into(&mut key.0, CTX)
.expect("`SendCtx::export_into` failed");
key
};
let got3 = recv.export::<Key>(CTX).expect("`RecvCtx::export` failed");
let got4 = {
let mut key = Key::default();
recv.export_into(&mut key.0, CTX)
.expect("`RecvCtx::export_into` failed");
key
};
assert_eq!(
got1, got2,
"`SendCtx::export` and `SendCtx::export_into` mismatch"
);
assert_eq!(
got2, got3,
"`SendCtx::export_into` and `RecvCtx::export` mismatch"
);
assert_eq!(
got3, got4,
"`RecvCtx::export` and `RecvCtx::export_into` mismatch"
);
}