use crate::{
aead::{Aead, AeadCtx, AeadCtxR, AeadCtxS},
kdf::{labeled_extract, DigestArray, Kdf as KdfTrait, LabeledExpand, MAX_DIGEST_SIZE},
kem::{Kem as KemTrait, SharedSecret},
op_mode::{OpMode, OpModeR, OpModeS},
util::full_suite_id,
HpkeError,
};
use rand_core::{CryptoRng, RngCore};
use zeroize::Zeroize;
pub(crate) struct ExporterSecret<K: KdfTrait>(pub(crate) DigestArray<K>);
impl<K: KdfTrait> Default for ExporterSecret<K> {
fn default() -> ExporterSecret<K> {
ExporterSecret(DigestArray::<K>::default())
}
}
impl<K: KdfTrait> Clone for ExporterSecret<K> {
fn clone(&self) -> ExporterSecret<K> {
ExporterSecret(self.0.clone())
}
}
impl<K: KdfTrait> Drop for ExporterSecret<K> {
fn drop(&mut self) {
self.0.zeroize();
}
}
pub(crate) 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>,
{
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 (_, secret_ctx) =
labeled_extract::<Kdf>(&shared_secret.0, &suite_id, b"secret", mode.get_psk_bytes());
let mut key = crate::aead::AeadKey::<A>::default();
let mut base_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.0.as_mut_slice())
.expect("aead key len is way too big");
secret_ctx
.labeled_expand(
&suite_id,
b"base_nonce",
sched_context,
base_nonce.0.as_mut_slice(),
)
.expect("nonce len is way too big");
secret_ctx
.labeled_expand(
&suite_id,
b"exp",
sched_context,
exporter_secret.0.as_mut_slice(),
)
.expect("exporter secret len is way too big");
AeadCtx::new(&key, base_nonce, exporter_secret)
}
pub fn setup_sender<A, Kdf, Kem, R>(
mode: &OpModeS<Kem>,
pk_recip: &Kem::PublicKey,
info: &[u8],
csprng: &mut R,
) -> Result<(Kem::EncappedKey, 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(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>,
sk_recip: &Kem::PrivateKey,
encapped_key: &Kem::EncappedKey,
info: &[u8],
) -> Result<AeadCtxR<A, Kdf, Kem>, HpkeError>
where
A: Aead,
Kdf: KdfTrait,
Kem: KemTrait,
{
let pk_sender_id: Option<&Kem::PublicKey> = mode.get_pk_sender_id();
let shared_secret = Kem::decap(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;
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::<Kdf, Kem>(*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;
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::<Kdf, Kem>(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 = "secp")]
mod secp_tests {
use super::*;
test_setup_correctness!(
test_setup_correctness_secp,
ChaCha20Poly1305,
HkdfSha256,
crate::kem::SecpK256HkdfSha256
);
test_setup_soundness!(
test_setup_soundness_secp,
ChaCha20Poly1305,
HkdfSha256,
crate::kem::SecpK256HkdfSha256
);
}
}