use crate::{
aead::{Aead, AeadCtx, AeadCtxR, AeadCtxS},
kdf::{labeled_extract, Kdf as KdfTrait, LabeledExpand, MAX_DIGEST_SIZE},
kem::{self, EncappedKey, Kem as KemTrait, SharedSecret},
kex::KeyExchange,
op_mode::{OpMode, OpModeR, OpModeS},
util::full_suite_id,
HpkeError,
};
use digest::Digest;
use generic_array::GenericArray;
use rand::{CryptoRng, RngCore};
pub(crate) type ExporterSecret<K> =
GenericArray<u8, <<K as KdfTrait>::HashImpl as Digest>::OutputSize>;
fn derive_enc_ctx<A, Kdf, Kem, O>(
mode: &O,
shared_secret: SharedSecret<Kem>,
info: &[u8],
) -> AeadCtx<A, Kdf, Kem>
where
A: Aead,
Kdf: KdfTrait,
Kem: KemTrait,
O: OpMode<Kem::Kex>,
{
let suite_id = full_suite_id::<A, Kdf, Kem>();
let (sched_context_buf, sched_context_size) = {
let (psk_id_hash, _) =
labeled_extract::<Kdf>(&[], &suite_id, b"psk_id_hash", mode.get_psk_id());
let (info_hash, _) = labeled_extract::<Kdf>(&[], &suite_id, b"info_hash", info);
concat_with_known_maxlen!(
MAX_DIGEST_SIZE,
&[mode.mode_id()],
&psk_id_hash.as_slice(),
&info_hash.as_slice()
)
};
let sched_context = &sched_context_buf[..sched_context_size];
let (extracted_psk, _) =
labeled_extract::<Kdf>(&[], &suite_id, b"psk_hash", mode.get_psk_bytes());
let (_, secret_ctx) =
labeled_extract::<Kdf>(&extracted_psk, &suite_id, b"secret", &shared_secret);
let mut key = crate::aead::AeadKey::<A>::default();
let mut nonce = crate::aead::AeadNonce::<A>::default();
let mut exporter_secret = <ExporterSecret<Kdf> as Default>::default();
secret_ctx
.labeled_expand(&suite_id, b"key", &sched_context, key.as_mut_slice())
.expect("aead key len is way too big");
secret_ctx
.labeled_expand(&suite_id, b"nonce", &sched_context, nonce.as_mut_slice())
.expect("nonce len is way too big");
secret_ctx
.labeled_expand(
&suite_id,
b"exp",
&sched_context,
exporter_secret.as_mut_slice(),
)
.expect("exporter secret len is way too big");
AeadCtx::new(&key, nonce, exporter_secret)
}
pub fn setup_sender<A, Kdf, Kem, R>(
mode: &OpModeS<Kem::Kex>,
pk_recip: &<Kem::Kex as KeyExchange>::PublicKey,
info: &[u8],
csprng: &mut R,
) -> Result<(EncappedKey<Kem::Kex>, AeadCtxS<A, Kdf, Kem>), HpkeError>
where
A: Aead,
Kdf: KdfTrait,
Kem: KemTrait,
R: CryptoRng + RngCore,
{
let sender_id_keypair = mode.get_sender_id_keypair();
let (shared_secret, encapped_key) = kem::encap::<Kem, _>(pk_recip, sender_id_keypair, csprng)?;
let enc_ctx = derive_enc_ctx::<_, _, Kem, _>(mode, shared_secret, info);
Ok((encapped_key, enc_ctx.into()))
}
pub fn setup_receiver<A, Kdf, Kem>(
mode: &OpModeR<Kem::Kex>,
sk_recip: &<Kem::Kex as KeyExchange>::PrivateKey,
encapped_key: &EncappedKey<Kem::Kex>,
info: &[u8],
) -> Result<AeadCtxR<A, Kdf, Kem>, HpkeError>
where
A: Aead,
Kdf: KdfTrait,
Kem: KemTrait,
{
let pk_sender_id: Option<&<Kem::Kex as KeyExchange>::PublicKey> = mode.get_pk_sender_id();
let shared_secret = kem::decap::<Kem>(sk_recip, pk_sender_id, encapped_key)?;
let enc_ctx = derive_enc_ctx::<_, _, Kem, _>(mode, shared_secret, info);
Ok(enc_ctx.into())
}
#[cfg(test)]
mod test {
use super::{setup_receiver, setup_sender};
use crate::test_util::{aead_ctx_eq, gen_rand_buf, new_op_mode_pair, OpModeKind};
use crate::{aead::ChaCha20Poly1305, kdf::HkdfSha256, kem::Kem as KemTrait};
use rand::{rngs::StdRng, SeedableRng};
macro_rules! test_setup_correctness {
($test_name:ident, $aead_ty:ty, $kdf_ty:ty, $kem_ty:ty) => {
#[test]
fn $test_name() {
type A = $aead_ty;
type Kdf = $kdf_ty;
type Kem = $kem_ty;
type Kex = <Kem as KemTrait>::Kex;
let mut csprng = StdRng::from_entropy();
let info = b"why would you think in a million years that that would actually work";
let (sk_recip, pk_recip) = Kem::gen_keypair(&mut csprng);
for op_mode_kind in &[
OpModeKind::Base,
OpModeKind::Auth,
OpModeKind::Psk,
OpModeKind::AuthPsk,
] {
let (psk, psk_id) = (gen_rand_buf(), gen_rand_buf());
let (sender_mode, receiver_mode) =
new_op_mode_pair::<Kex, Kdf>(*op_mode_kind, &psk, &psk_id);
let (encapped_key, mut aead_ctx1) = setup_sender::<A, Kdf, Kem, _>(
&sender_mode,
&pk_recip,
&info[..],
&mut csprng,
)
.unwrap();
let mut aead_ctx2 = setup_receiver::<A, Kdf, Kem>(
&receiver_mode,
&sk_recip,
&encapped_key,
&info[..],
)
.unwrap();
assert!(aead_ctx_eq(&mut aead_ctx1, &mut aead_ctx2));
}
}
};
}
macro_rules! test_setup_soundness {
($test_name:ident, $aead:ty, $kdf:ty, $kem:ty) => {
#[test]
fn $test_name() {
type A = $aead;
type Kdf = $kdf;
type Kem = $kem;
type Kex = <Kem as KemTrait>::Kex;
let mut csprng = StdRng::from_entropy();
let info = b"why would you think in a million years that that would actually work";
let (sk_recip, pk_recip) = Kem::gen_keypair(&mut csprng);
let (psk, psk_id) = (gen_rand_buf(), gen_rand_buf());
let (sender_mode, receiver_mode) =
new_op_mode_pair::<Kex, Kdf>(OpModeKind::Base, &psk, &psk_id);
let (encapped_key, sender_ctx) =
setup_sender::<A, Kdf, Kem, _>(&sender_mode, &pk_recip, &info[..], &mut csprng)
.unwrap();
let bad_info = b"something else";
let mut receiver_ctx = setup_receiver::<_, _, Kem>(
&receiver_mode,
&sk_recip,
&encapped_key,
&bad_info[..],
)
.unwrap();
assert!(!aead_ctx_eq(&mut sender_ctx.clone(), &mut receiver_ctx));
let (bad_sk, _) = Kem::gen_keypair(&mut csprng);
let mut aead_ctx2 =
setup_receiver::<_, _, Kem>(&receiver_mode, &bad_sk, &encapped_key, &info[..])
.unwrap();
assert!(!aead_ctx_eq(&mut sender_ctx.clone(), &mut aead_ctx2));
let (bad_encapped_key, _) =
setup_sender::<A, Kdf, Kem, _>(&sender_mode, &pk_recip, &info[..], &mut csprng)
.unwrap();
let mut aead_ctx2 = setup_receiver::<_, _, Kem>(
&receiver_mode,
&sk_recip,
&bad_encapped_key,
&info[..],
)
.unwrap();
assert!(!aead_ctx_eq(&mut sender_ctx.clone(), &mut aead_ctx2));
let mut aead_ctx2 = setup_receiver::<_, _, Kem>(
&receiver_mode,
&sk_recip,
&encapped_key,
&info[..],
)
.unwrap();
assert!(aead_ctx_eq(&mut sender_ctx.clone(), &mut aead_ctx2));
}
};
}
#[cfg(feature = "x25519-dalek")]
test_setup_correctness!(
test_setup_correctness_x25519,
ChaCha20Poly1305,
HkdfSha256,
crate::kem::X25519HkdfSha256
);
#[cfg(feature = "p256")]
test_setup_correctness!(
test_setup_correctness_p256,
ChaCha20Poly1305,
HkdfSha256,
crate::kem::DhP256HkdfSha256
);
#[cfg(feature = "x25519-dalek")]
test_setup_soundness!(
test_setup_soundness_x25519,
ChaCha20Poly1305,
HkdfSha256,
crate::kem::X25519HkdfSha256
);
#[cfg(feature = "p256")]
test_setup_soundness!(
test_setup_soundness_p256,
ChaCha20Poly1305,
HkdfSha256,
crate::kem::DhP256HkdfSha256
);
}