#![allow(clippy::missing_errors_doc)]
pub mod dhkem;
use hpke_crypto::{
Crypto, EncapsulatedSecret, EncapsulatedSecretRef, HpkeKemId, HpkePrivateKey,
HpkePrivateKeyRef, HpkePublicKey, HpkePublicKeyRef, IkmRef, SharedSecret,
};
use crate::error::Error;
pub fn generate_key_pair<C: Crypto>(
crypto_backend: &mut C,
alg: HpkeKemId,
) -> Result<(HpkePrivateKey, HpkePublicKey), Error> {
dhkem::generate_key_pair(crypto_backend, alg)
}
pub fn derive_key_pair<C: Crypto>(
crypto_backend: &mut C,
alg: HpkeKemId,
ikm: IkmRef<'_>,
) -> Result<(HpkePrivateKey, HpkePublicKey), Error> {
dhkem::derive_key_pair(crypto_backend, alg, ikm)
}
pub fn encap<C: Crypto>(
crypto_backend: &mut C,
alg: HpkeKemId,
pk_r: HpkePublicKeyRef<'_>,
) -> Result<(SharedSecret, EncapsulatedSecret), Error> {
dhkem::encap(crypto_backend, alg, pk_r)
}
pub fn decap<C: Crypto>(
crypto_backend: &mut C,
alg: HpkeKemId,
enc: EncapsulatedSecretRef<'_>,
sk_r: HpkePrivateKeyRef<'_>,
) -> Result<SharedSecret, Error> {
dhkem::decap(crypto_backend, alg, enc, sk_r)
}
pub fn auth_encap<C: Crypto>(
crypto_backend: &mut C,
alg: HpkeKemId,
pk_r: HpkePublicKeyRef<'_>,
sk_s: HpkePrivateKeyRef<'_>,
) -> Result<(SharedSecret, EncapsulatedSecret), Error> {
dhkem::auth_encap(crypto_backend, alg, pk_r, sk_s)
}
pub fn auth_decap<C: Crypto>(
crypto_backend: &mut C,
alg: HpkeKemId,
enc: EncapsulatedSecretRef<'_>,
sk_r: HpkePrivateKeyRef<'_>,
pk_s: HpkePublicKeyRef<'_>,
) -> Result<SharedSecret, Error> {
dhkem::auth_decap(crypto_backend, alg, enc, sk_r, pk_s)
}
#[cfg(test)]
mod tests {
use alloc::format;
use alloc::vec::Vec;
use std::panic::catch_unwind;
use std::println;
use super::*;
use crate::{CryptoError, HPKE_TEST_VECTORS};
#[test_case::test_matrix(
[
hpke_crypto::backend::HpkeCryptoAwsLc::new,
hpke_crypto::backend::HpkeCryptoGraviola::new,
]
)]
fn kat_derive_key_pair<C: Crypto + Send + Sync + core::panic::UnwindSafe, F>(crypto_backend: F)
where
F: Fn() -> Result<C, CryptoError>,
{
let mut rets = Vec::new();
for (idx, test_case) in HPKE_TEST_VECTORS.iter().enumerate() {
{
let crypto_backend = crypto_backend().unwrap();
let ret = catch_unwind(move || {
test_derive_key_pair_each(
crypto_backend,
idx,
"R",
test_case.kem_id,
&test_case.ikm_r,
&test_case.sk_rm,
&test_case.pk_rm,
);
});
rets.push((format!("R-{}({})", test_case.kem_id, idx), ret));
}
{
let crypto_backend = crypto_backend().unwrap();
let ret = catch_unwind(move || {
test_derive_key_pair_each(
crypto_backend,
idx,
"E",
test_case.kem_id,
&test_case.ikm_e,
&test_case.sk_em,
&test_case.pk_em,
);
});
rets.push((format!("E-{}({})", test_case.kem_id, idx), ret));
}
if test_case.ikm_s.is_some() {
let crypto_backend = crypto_backend().unwrap();
let ret = catch_unwind(move || {
test_derive_key_pair_each(
crypto_backend,
idx,
"E",
test_case.kem_id,
&test_case.ikm_s.as_ref().unwrap(),
&test_case.sk_sm.as_ref().unwrap(),
&test_case.pk_sm.as_ref().unwrap(),
);
});
rets.push((format!("S-{}({})", test_case.kem_id, idx), ret));
}
}
let errors: Vec<_> = rets
.iter()
.filter(|(_, ret)| ret.is_err())
.collect();
if !errors.is_empty() {
for (name, err) in &errors {
println!("[FAILED] {name}: {err:?}");
}
panic!("{} test cases failed", errors.len());
}
}
fn test_derive_key_pair_each<C: Crypto>(
mut crypto_backend: C,
idx: usize,
role: &'static str,
alg: HpkeKemId,
ikm: &[u8],
expected_sk: &[u8],
expected_pk: &[u8],
) {
if !crypto_backend.is_kem_supported(&alg) {
println!(
"[{name}][{idx}][{role}] Skipping, unsupported KEM {alg:?}",
name = core::any::type_name::<C>(),
);
return;
}
let (sk, pk) = derive_key_pair(&mut crypto_backend, alg, ikm.into()).unwrap();
assert_eq!(&*sk, expected_sk);
assert_eq!(&*pk, expected_pk);
}
}