1use crate::{
2 aead::{Aead, AeadCtx, AeadCtxR, AeadCtxS},
3 kdf::{labeled_extract, DigestArray, Kdf as KdfTrait, LabeledExpand, MAX_DIGEST_SIZE},
4 kem::{Kem as KemTrait, SharedSecret},
5 op_mode::{OpMode, OpModeR, OpModeS},
6 util::full_suite_id,
7 HpkeError,
8};
9
10use rand_core::{CryptoRng, RngCore};
11use zeroize::Zeroize;
12
13pub(crate) struct ExporterSecret<K: KdfTrait>(pub(crate) DigestArray<K>);
15
16impl<K: KdfTrait> Default for ExporterSecret<K> {
18 fn default() -> ExporterSecret<K> {
19 ExporterSecret(DigestArray::<K>::default())
20 }
21}
22
23impl<K: KdfTrait> Clone for ExporterSecret<K> {
24 fn clone(&self) -> ExporterSecret<K> {
25 ExporterSecret(self.0.clone())
26 }
27}
28
29impl<K: KdfTrait> Drop for ExporterSecret<K> {
31 fn drop(&mut self) {
32 self.0.zeroize();
33 }
34}
35
36fn derive_enc_ctx<A, Kdf, Kem, O>(
57 mode: &O,
58 shared_secret: SharedSecret<Kem>,
59 info: &[u8],
60) -> AeadCtx<A, Kdf, Kem>
61where
62 A: Aead,
63 Kdf: KdfTrait,
64 Kem: KemTrait,
65 O: OpMode<Kem>,
66{
67 let suite_id = full_suite_id::<A, Kdf, Kem>();
69
70 let (sched_context_buf, sched_context_size) = {
78 let (psk_id_hash, _) =
79 labeled_extract::<Kdf>(&[], &suite_id, b"psk_id_hash", mode.get_psk_id());
80 let (info_hash, _) = labeled_extract::<Kdf>(&[], &suite_id, b"info_hash", info);
81
82 concat_with_known_maxlen!(
85 MAX_DIGEST_SIZE,
86 &[mode.mode_id()],
87 psk_id_hash.as_slice(),
88 info_hash.as_slice()
89 )
90 };
91 let sched_context = &sched_context_buf[..sched_context_size];
92
93 let (_, secret_ctx) =
101 labeled_extract::<Kdf>(&shared_secret.0, &suite_id, b"secret", mode.get_psk_bytes());
102
103 let mut key = crate::aead::AeadKey::<A>::default();
105 let mut base_nonce = crate::aead::AeadNonce::<A>::default();
106 let mut exporter_secret = <ExporterSecret<Kdf> as Default>::default();
107
108 secret_ctx
112 .labeled_expand(&suite_id, b"key", sched_context, key.0.as_mut_slice())
113 .expect("aead key len is way too big");
114 secret_ctx
115 .labeled_expand(
116 &suite_id,
117 b"base_nonce",
118 sched_context,
119 base_nonce.0.as_mut_slice(),
120 )
121 .expect("nonce len is way too big");
122 secret_ctx
123 .labeled_expand(
124 &suite_id,
125 b"exp",
126 sched_context,
127 exporter_secret.0.as_mut_slice(),
128 )
129 .expect("exporter secret len is way too big");
130
131 AeadCtx::new(&key, base_nonce, exporter_secret)
132}
133
134pub fn setup_sender<A, Kdf, Kem, R>(
148 mode: &OpModeS<Kem>,
149 pk_recip: &Kem::PublicKey,
150 info: &[u8],
151 csprng: &mut R,
152) -> Result<(Kem::EncappedKey, AeadCtxS<A, Kdf, Kem>), HpkeError>
153where
154 A: Aead,
155 Kdf: KdfTrait,
156 Kem: KemTrait,
157 R: CryptoRng + RngCore,
158{
159 let sender_id_keypair = mode.get_sender_id_keypair();
161 let (shared_secret, encapped_key) = Kem::encap(pk_recip, sender_id_keypair, csprng)?;
163 let enc_ctx = derive_enc_ctx::<_, _, Kem, _>(mode, shared_secret, info);
165
166 Ok((encapped_key, enc_ctx.into()))
167}
168
169pub fn setup_receiver<A, Kdf, Kem>(
183 mode: &OpModeR<Kem>,
184 sk_recip: &Kem::PrivateKey,
185 encapped_key: &Kem::EncappedKey,
186 info: &[u8],
187) -> Result<AeadCtxR<A, Kdf, Kem>, HpkeError>
188where
189 A: Aead,
190 Kdf: KdfTrait,
191 Kem: KemTrait,
192{
193 let pk_sender_id: Option<&Kem::PublicKey> = mode.get_pk_sender_id();
195 let shared_secret = Kem::decap(sk_recip, pk_sender_id, encapped_key)?;
197
198 let enc_ctx = derive_enc_ctx::<_, _, Kem, _>(mode, shared_secret, info);
200 Ok(enc_ctx.into())
201}
202
203#[cfg(test)]
204mod test {
205 use super::{setup_receiver, setup_sender};
206 use crate::test_util::{aead_ctx_eq, gen_rand_buf, new_op_mode_pair, OpModeKind};
207 use crate::{aead::ChaCha20Poly1305, kdf::HkdfSha256, kem::Kem as KemTrait};
208
209 use rand::{rngs::StdRng, SeedableRng};
210
211 macro_rules! test_setup_correctness {
214 ($test_name:ident, $aead_ty:ty, $kdf_ty:ty, $kem_ty:ty) => {
215 #[test]
216 fn $test_name() {
217 type A = $aead_ty;
218 type Kdf = $kdf_ty;
219 type Kem = $kem_ty;
220
221 let mut csprng = StdRng::from_os_rng();
222
223 let info = b"why would you think in a million years that that would actually work";
224
225 let (sk_recip, pk_recip) = Kem::gen_keypair(&mut csprng);
227
228 for op_mode_kind in &[
230 OpModeKind::Base,
231 OpModeKind::Auth,
232 OpModeKind::Psk,
233 OpModeKind::AuthPsk,
234 ] {
235 let (psk, psk_id) = (gen_rand_buf(), gen_rand_buf());
237 let (sender_mode, receiver_mode) =
238 new_op_mode_pair::<Kdf, Kem>(*op_mode_kind, &psk, &psk_id);
239
240 let (encapped_key, mut aead_ctx1) = setup_sender::<A, Kdf, Kem, _>(
242 &sender_mode,
243 &pk_recip,
244 &info[..],
245 &mut csprng,
246 )
247 .unwrap();
248
249 let mut aead_ctx2 = setup_receiver::<A, Kdf, Kem>(
251 &receiver_mode,
252 &sk_recip,
253 &encapped_key,
254 &info[..],
255 )
256 .unwrap();
257
258 assert!(aead_ctx_eq(&mut aead_ctx1, &mut aead_ctx2));
260 }
261 }
262 };
263 }
264
265 macro_rules! test_setup_soundness {
267 ($test_name:ident, $aead:ty, $kdf:ty, $kem:ty) => {
268 #[test]
269 fn $test_name() {
270 type A = $aead;
271 type Kdf = $kdf;
272 type Kem = $kem;
273
274 let mut csprng = StdRng::from_os_rng();
275
276 let info = b"why would you think in a million years that that would actually work";
277
278 let (sk_recip, pk_recip) = Kem::gen_keypair(&mut csprng);
280
281 let (psk, psk_id) = (gen_rand_buf(), gen_rand_buf());
283 let (sender_mode, receiver_mode) =
284 new_op_mode_pair::<Kdf, Kem>(OpModeKind::Base, &psk, &psk_id);
285
286 let (encapped_key, sender_ctx) =
288 setup_sender::<A, Kdf, Kem, _>(&sender_mode, &pk_recip, &info[..], &mut csprng)
289 .unwrap();
290
291 let bad_info = b"something else";
294 let mut receiver_ctx = setup_receiver::<_, _, Kem>(
295 &receiver_mode,
296 &sk_recip,
297 &encapped_key,
298 &bad_info[..],
299 )
300 .unwrap();
301 assert!(!aead_ctx_eq(&mut sender_ctx.clone(), &mut receiver_ctx));
302
303 let (bad_sk, _) = Kem::gen_keypair(&mut csprng);
306 let mut aead_ctx2 =
307 setup_receiver::<_, _, Kem>(&receiver_mode, &bad_sk, &encapped_key, &info[..])
308 .unwrap();
309 assert!(!aead_ctx_eq(&mut sender_ctx.clone(), &mut aead_ctx2));
310
311 let (bad_encapped_key, _) =
315 setup_sender::<A, Kdf, Kem, _>(&sender_mode, &pk_recip, &info[..], &mut csprng)
316 .unwrap();
317 let mut aead_ctx2 = setup_receiver::<_, _, Kem>(
318 &receiver_mode,
319 &sk_recip,
320 &bad_encapped_key,
321 &info[..],
322 )
323 .unwrap();
324 assert!(!aead_ctx_eq(&mut sender_ctx.clone(), &mut aead_ctx2));
325
326 let mut aead_ctx2 = setup_receiver::<_, _, Kem>(
329 &receiver_mode,
330 &sk_recip,
331 &encapped_key,
332 &info[..],
333 )
334 .unwrap();
335 assert!(aead_ctx_eq(&mut sender_ctx.clone(), &mut aead_ctx2));
336 }
337 };
338 }
339
340 #[cfg(feature = "x25519")]
341 mod x25519_tests {
342 use super::*;
343
344 test_setup_correctness!(
345 test_setup_correctness_x25519,
346 ChaCha20Poly1305,
347 HkdfSha256,
348 crate::kem::x25519_hkdfsha256::X25519HkdfSha256
349 );
350 test_setup_soundness!(
351 test_setup_soundness_x25519,
352 ChaCha20Poly1305,
353 HkdfSha256,
354 crate::kem::x25519_hkdfsha256::X25519HkdfSha256
355 );
356 }
357
358 #[cfg(feature = "p256")]
359 mod p256_tests {
360 use super::*;
361
362 test_setup_correctness!(
363 test_setup_correctness_p256,
364 ChaCha20Poly1305,
365 HkdfSha256,
366 crate::kem::dhp256_hkdfsha256::DhP256HkdfSha256
367 );
368 test_setup_soundness!(
369 test_setup_soundness_p256,
370 ChaCha20Poly1305,
371 HkdfSha256,
372 crate::kem::dhp256_hkdfsha256::DhP256HkdfSha256
373 );
374 }
375
376 #[cfg(feature = "p384")]
377 mod p384_tests {
378 use super::*;
379 use crate::kdf::HkdfSha384;
380
381 test_setup_correctness!(
382 test_setup_correctness_p384,
383 ChaCha20Poly1305,
384 HkdfSha384,
385 crate::kem::dhp384_hkdfsha384::DhP384HkdfSha384
386 );
387 test_setup_soundness!(
388 test_setup_soundness_p384,
389 ChaCha20Poly1305,
390 HkdfSha384,
391 crate::kem::dhp384_hkdfsha384::DhP384HkdfSha384
392 );
393 }
394
395 #[cfg(feature = "p521")]
396 mod p521_tests {
397 use super::*;
398 use crate::kdf::HkdfSha512;
399
400 test_setup_correctness!(
401 test_setup_correctness_p521,
402 ChaCha20Poly1305,
403 HkdfSha512,
404 crate::kem::dhp521_hkdfsha512::DhP521HkdfSha512
405 );
406 test_setup_soundness!(
407 test_setup_soundness_p521,
408 ChaCha20Poly1305,
409 HkdfSha512,
410 crate::kem::dhp521_hkdfsha512::DhP521HkdfSha512
411 );
412 }
413}