use chacha20poly1305::ChaCha20Poly1305;
use cipher::{KeySizeUser, typenum::Unsigned};
use hkdf::Hkdf;
use hpke::{
HpkeError,
aead::{AeadCtxR, AeadCtxS, AeadKey, AeadNonce},
danger::streaming_enc::{ExporterSecret, create_receiver_context, create_sender_context},
};
use sha2::Sha256;
use zeroize::Zeroize;
use super::{Aead, Kdf, Kem};
use crate::Curve25519PublicKey;
pub(super) type AeadKeySize = <ChaCha20Poly1305 as KeySizeUser>::KeySize;
pub(super) trait CreateResponseContext {
type ResponseContext;
fn export(&self, info: &[u8], output: &mut [u8]) -> Result<(), HpkeError>;
fn create_context(
&self,
response_key: &AeadKey<Aead>,
response_nonce: AeadNonce<Aead>,
) -> Self::ResponseContext;
fn create_response_context(
&self,
application_info_prefix: &str,
encapsulated_key: Curve25519PublicKey,
response_nonce: &[u8],
) -> Self::ResponseContext {
let mut secret = [0u8; AeadKeySize::USIZE];
let info = format!("{application_info_prefix}_RESPONSE");
#[allow(clippy::expect_used)]
self.export(info.as_bytes(), &mut secret)
.expect("We should be able to export 32 bytes from the HPKE export interface");
let salt: Vec<u8> = [encapsulated_key.as_bytes().as_slice(), response_nonce].concat();
let hkdf = Hkdf::<Sha256>::new(Some(&salt), &secret);
let mut aead_key = AeadKey::default();
let mut aead_nonce = AeadNonce::default();
#[allow(clippy::expect_used)]
hkdf.expand(b"key", aead_key.0.as_mut_slice())
.expect("We should be able to expand the base response secret into an AEAD key");
#[allow(clippy::expect_used)]
hkdf.expand(b"nonce", aead_nonce.0.as_mut_slice())
.expect("We should be able to expand the base response secret into a response nonce");
debug_assert_ne!(aead_nonce.0.as_slice(), [0u8; 12]);
debug_assert_ne!(aead_key.0.as_slice(), [0u8; 32]);
secret.zeroize();
self.create_context(&aead_key, aead_nonce)
}
}
impl CreateResponseContext for AeadCtxS<Aead, Kdf, Kem> {
type ResponseContext = AeadCtxR<Aead, Kdf, Kem>;
fn export(&self, info: &[u8], output: &mut [u8]) -> Result<(), HpkeError> {
self.export(info, output)
}
fn create_context(
&self,
response_key: &AeadKey<Aead>,
response_nonce: AeadNonce<Aead>,
) -> Self::ResponseContext {
let exporter_secret = ExporterSecret::default();
create_receiver_context(response_key, response_nonce, exporter_secret)
}
}
impl CreateResponseContext for AeadCtxR<Aead, Kdf, Kem> {
type ResponseContext = AeadCtxS<Aead, Kdf, Kem>;
fn export(&self, info: &[u8], output: &mut [u8]) -> Result<(), HpkeError> {
self.export(info, output)
}
fn create_context(
&self,
response_key: &AeadKey<Aead>,
response_nonce: AeadNonce<Aead>,
) -> Self::ResponseContext {
let exporter_secret = ExporterSecret::default();
create_sender_context(response_key, response_nonce, exporter_secret)
}
}