use crate::{
aead::{Aead, AesGcm128, AesGcm256, ChaCha20Poly1305, ExportOnlyAead},
kdf::{HkdfSha256, HkdfSha384, HkdfSha512, Kdf as KdfTrait},
kem::{
self, DhP256HkdfSha256, DhP384HkdfSha384, DhP521HkdfSha512, Kem as KemTrait, SharedSecret,
X25519HkdfSha256,
},
op_mode::{OpModeR, PskBundle},
setup::setup_receiver,
Deserializable, HpkeError, Serializable,
};
extern crate std;
use std::{fs::File, string::String, vec::Vec};
use hex;
use serde::{de::Error as SError, Deserialize, Deserializer};
use serde_json;
trait TestableKem: KemTrait {
type EphemeralKey: Deserializable;
#[doc(hidden)]
fn encap_with_eph(
pk_recip: &Self::PublicKey,
sender_id_keypair: Option<(&Self::PrivateKey, &Self::PublicKey)>,
sk_eph: Self::EphemeralKey,
) -> Result<(SharedSecret<Self>, Self::EncappedKey), HpkeError>;
}
impl TestableKem for X25519HkdfSha256 {
type EphemeralKey = <X25519HkdfSha256 as KemTrait>::PrivateKey;
fn encap_with_eph(
pk_recip: &Self::PublicKey,
sender_id_keypair: Option<(&Self::PrivateKey, &Self::PublicKey)>,
sk_eph: Self::EphemeralKey,
) -> Result<(SharedSecret<Self>, Self::EncappedKey), HpkeError> {
kem::x25519_hkdfsha256::encap_with_eph(pk_recip, sender_id_keypair, sk_eph)
}
}
impl TestableKem for DhP256HkdfSha256 {
type EphemeralKey = <DhP256HkdfSha256 as KemTrait>::PrivateKey;
fn encap_with_eph(
pk_recip: &Self::PublicKey,
sender_id_keypair: Option<(&Self::PrivateKey, &Self::PublicKey)>,
sk_eph: Self::EphemeralKey,
) -> Result<(SharedSecret<Self>, Self::EncappedKey), HpkeError> {
kem::dhp256_hkdfsha256::encap_with_eph(pk_recip, sender_id_keypair, sk_eph)
}
}
impl TestableKem for DhP384HkdfSha384 {
type EphemeralKey = <DhP384HkdfSha384 as KemTrait>::PrivateKey;
fn encap_with_eph(
pk_recip: &Self::PublicKey,
sender_id_keypair: Option<(&Self::PrivateKey, &Self::PublicKey)>,
sk_eph: Self::EphemeralKey,
) -> Result<(SharedSecret<Self>, Self::EncappedKey), HpkeError> {
kem::dhp384_hkdfsha384::encap_with_eph(pk_recip, sender_id_keypair, sk_eph)
}
}
impl TestableKem for DhP521HkdfSha512 {
type EphemeralKey = <DhP521HkdfSha512 as KemTrait>::PrivateKey;
fn encap_with_eph(
pk_recip: &Self::PublicKey,
sender_id_keypair: Option<(&Self::PrivateKey, &Self::PublicKey)>,
sk_eph: Self::EphemeralKey,
) -> Result<(SharedSecret<Self>, Self::EncappedKey), HpkeError> {
kem::dhp521_hkdfsha512::encap_with_eph(pk_recip, sender_id_keypair, sk_eph)
}
}
macro_rules! assert_serializable_eq {
($a:expr, $b:expr, $args:tt) => {
assert_eq!($a.to_bytes(), $b.to_bytes(), $args)
};
}
fn bytes_from_hex<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
let mut hex_str = String::deserialize(deserializer)?;
if hex_str.len() % 2 == 1 {
hex_str.insert(0, '0');
}
hex::decode(hex_str).map_err(|e| SError::custom(format!("{:?}", e)))
}
fn bytes_from_hex_opt<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
where
D: Deserializer<'de>,
{
bytes_from_hex(deserializer).map(|v| Some(v))
}
#[derive(Clone, serde::Deserialize, Debug)]
struct MainTestVector {
mode: u8,
kem_id: u16,
kdf_id: u16,
aead_id: u16,
#[serde(deserialize_with = "bytes_from_hex")]
info: Vec<u8>,
#[serde(rename = "ikmR", deserialize_with = "bytes_from_hex")]
ikm_recip: Vec<u8>,
#[serde(default, rename = "ikmS", deserialize_with = "bytes_from_hex_opt")]
ikm_sender: Option<Vec<u8>>,
#[serde(rename = "ikmE", deserialize_with = "bytes_from_hex")]
_ikm_eph: Vec<u8>,
#[serde(rename = "skRm", deserialize_with = "bytes_from_hex")]
sk_recip: Vec<u8>,
#[serde(default, rename = "skSm", deserialize_with = "bytes_from_hex_opt")]
sk_sender: Option<Vec<u8>>,
#[serde(rename = "skEm", deserialize_with = "bytes_from_hex")]
sk_eph: Vec<u8>,
#[serde(default, deserialize_with = "bytes_from_hex_opt")]
psk: Option<Vec<u8>>,
#[serde(default, rename = "psk_id", deserialize_with = "bytes_from_hex_opt")]
psk_id: Option<Vec<u8>>,
#[serde(rename = "pkRm", deserialize_with = "bytes_from_hex")]
pk_recip: Vec<u8>,
#[serde(default, rename = "pkSm", deserialize_with = "bytes_from_hex_opt")]
pk_sender: Option<Vec<u8>>,
#[serde(rename = "pkEm", deserialize_with = "bytes_from_hex")]
_pk_eph: Vec<u8>,
#[serde(rename = "enc", deserialize_with = "bytes_from_hex")]
encapped_key: Vec<u8>,
#[serde(deserialize_with = "bytes_from_hex")]
shared_secret: Vec<u8>,
#[serde(rename = "key_schedule_context", deserialize_with = "bytes_from_hex")]
_hpke_context: Vec<u8>,
#[serde(rename = "secret", deserialize_with = "bytes_from_hex")]
_key_schedule_secret: Vec<u8>,
#[serde(rename = "key", deserialize_with = "bytes_from_hex")]
_aead_key: Vec<u8>,
#[serde(rename = "base_nonce", deserialize_with = "bytes_from_hex")]
_aead_base_nonce: Vec<u8>,
#[serde(rename = "exporter_secret", deserialize_with = "bytes_from_hex")]
_exporter_secret: Vec<u8>,
encryptions: Vec<EncryptionTestVector>,
exports: Vec<ExporterTestVector>,
}
#[derive(Clone, serde::Deserialize, Debug)]
struct EncryptionTestVector {
#[serde(rename = "pt", deserialize_with = "bytes_from_hex")]
plaintext: Vec<u8>,
#[serde(deserialize_with = "bytes_from_hex")]
aad: Vec<u8>,
#[serde(rename = "nonce", deserialize_with = "bytes_from_hex")]
_nonce: Vec<u8>,
#[serde(rename = "ct", deserialize_with = "bytes_from_hex")]
ciphertext: Vec<u8>,
}
#[derive(Clone, serde::Deserialize, Debug)]
struct ExporterTestVector {
#[serde(rename = "exporter_context", deserialize_with = "bytes_from_hex")]
export_ctx: Vec<u8>,
#[serde(rename = "L")]
export_len: usize,
#[serde(rename = "exported_value", deserialize_with = "bytes_from_hex")]
export_val: Vec<u8>,
}
fn deser_keypair<Kem: KemTrait>(
sk_bytes: &[u8],
pk_bytes: &[u8],
) -> (Kem::PrivateKey, Kem::PublicKey) {
let sk = <Kem as KemTrait>::PrivateKey::from_bytes(sk_bytes).unwrap();
let pk = <Kem as KemTrait>::PublicKey::from_bytes(pk_bytes).unwrap();
(sk, pk)
}
fn make_op_mode_r<'a, Kem: KemTrait>(
mode_id: u8,
pk: Option<Kem::PublicKey>,
psk: Option<&'a [u8]>,
psk_id: Option<&'a [u8]>,
) -> OpModeR<'a, Kem> {
let bundle = psk.map(|bytes| PskBundle::new(bytes, psk_id.unwrap()).unwrap());
match mode_id {
0 => OpModeR::Base,
1 => OpModeR::Psk(bundle.unwrap()),
2 => OpModeR::Auth(pk.unwrap()),
3 => OpModeR::AuthPsk(pk.unwrap(), bundle.unwrap()),
_ => panic!("Invalid mode ID: {}", mode_id),
}
}
fn test_case<A: Aead, Kdf: KdfTrait, Kem: TestableKem>(tv: MainTestVector) {
let recip_keypair = deser_keypair::<Kem>(&tv.sk_recip, &tv.pk_recip);
let sk_eph = <Kem as TestableKem>::EphemeralKey::from_bytes(&tv.sk_eph).unwrap();
let sender_keypair = {
let pk_sender = &tv.pk_sender.as_ref();
tv.sk_sender
.as_ref()
.map(|sk| deser_keypair::<Kem>(sk, pk_sender.unwrap()))
};
{
let derived_kp = Kem::derive_keypair(&tv.ikm_recip);
assert_serializable_eq!(recip_keypair.0, derived_kp.0, "sk recip doesn't match");
assert_serializable_eq!(recip_keypair.1, derived_kp.1, "pk recip doesn't match");
}
if let Some(sks) = sender_keypair.as_ref() {
let derived_kp = Kem::derive_keypair(&tv.ikm_sender.unwrap());
assert_serializable_eq!(sks.0, derived_kp.0, "sk sender doesn't match");
assert_serializable_eq!(sks.1, derived_kp.1, "pk sender doesn't match");
}
let (sk_recip, pk_recip) = recip_keypair;
let (shared_secret, encapped_key) = {
let sender_keypair_ref = sender_keypair.as_ref().map(|&(ref sk, ref pk)| (sk, pk));
Kem::encap_with_eph(&pk_recip, sender_keypair_ref, sk_eph).expect("encap failed")
};
assert_eq!(
shared_secret.0.as_slice(),
tv.shared_secret.as_slice(),
"shared_secret doesn't match"
);
{
let provided_encapped_key =
<Kem as KemTrait>::EncappedKey::from_bytes(&tv.encapped_key).unwrap();
assert_serializable_eq!(
encapped_key,
provided_encapped_key,
"encapped keys don't match"
);
}
let mode = make_op_mode_r(
tv.mode,
sender_keypair.map(|(_, pk)| pk),
tv.psk.as_ref().map(Vec::as_slice),
tv.psk_id.as_ref().map(Vec::as_slice),
);
let mut aead_ctx = setup_receiver::<A, Kdf, Kem>(&mode, &sk_recip, &encapped_key, &tv.info)
.expect("setup_receiver failed");
for enc_packet in tv.encryptions {
let EncryptionTestVector {
aad,
ciphertext,
plaintext,
..
} = enc_packet;
let decrypted = aead_ctx.open(&ciphertext, &aad).expect("open failed");
assert_eq!(decrypted, plaintext, "plaintexts don't match");
}
for export in tv.exports {
let mut exported_val = vec![0u8; export.export_len];
aead_ctx
.export(&export.export_ctx, &mut exported_val)
.unwrap();
assert_eq!(exported_val, export.export_val, "export values don't match");
}
}
macro_rules! dispatch_testcase {
($tv:ident, ($( $aead_ty:ty ),*), ($( $kdf_ty:ty ),*), ($( $kem_ty:ty ),*)) => {
dispatch_testcase!(@tup1 $tv, ($( $aead_ty ),*), ($( $kdf_ty ),*), ($( $kem_ty ),*))
};
(@tup1 $tv:ident, ($( $aead_ty:ty ),*), $kdf_tup:tt, $kem_tup:tt) => {
$(
dispatch_testcase!(@tup2 $tv, $aead_ty, $kdf_tup, $kem_tup);
)*
};
(@tup2 $tv:ident, $aead_ty:ty, ($( $kdf_ty:ty ),*), $kem_tup:tt) => {
$(
dispatch_testcase!(@tup3 $tv, $aead_ty, $kdf_ty, $kem_tup);
)*
};
(@tup3 $tv:ident, $aead_ty:ty, $kdf_ty:ty, ($( $kem_ty:ty ),*)) => {
$(
dispatch_testcase!(@base $tv, $aead_ty, $kdf_ty, $kem_ty);
)*
};
(@base $tv:ident, $aead_ty:ty, $kdf_ty:ty, $kem_ty:ty) => {
if let (<$aead_ty>::AEAD_ID, <$kdf_ty>::KDF_ID, <$kem_ty>::KEM_ID) =
($tv.aead_id, $tv.kdf_id, $tv.kem_id)
{
println!(
"Running test case on {}, {}, {}",
stringify!($aead_ty),
stringify!($kdf_ty),
stringify!($kem_ty)
);
let tv = $tv.clone();
test_case::<$aead_ty, $kdf_ty, $kem_ty>(tv);
continue;
}
};
}
#[test]
fn kat_test() {
let file = File::open("test-vectors-5f503c5.json").unwrap();
let tvs: Vec<MainTestVector> = serde_json::from_reader(file).unwrap();
for tv in tvs.into_iter() {
if tv.kem_id != X25519HkdfSha256::KEM_ID
&& tv.kem_id != DhP256HkdfSha256::KEM_ID
&& tv.kem_id != DhP384HkdfSha384::KEM_ID
&& tv.kem_id != DhP521HkdfSha512::KEM_ID
{
continue;
}
dispatch_testcase!(
tv,
(AesGcm128, AesGcm256, ChaCha20Poly1305, ExportOnlyAead),
(HkdfSha256, HkdfSha384, HkdfSha512),
(
X25519HkdfSha256,
DhP256HkdfSha256,
DhP384HkdfSha384,
DhP521HkdfSha512
)
);
panic!(
"Unrecognized (AEAD ID, KDF ID, KEM ID) combo: ({}, {}, {})",
tv.aead_id, tv.kdf_id, tv.kem_id
);
}
}