1#![cfg_attr(not(feature = "std"), no_std)]
24#![forbid(unsafe_code, unstable_features)]
25#![deny(
26 missing_docs,
27 rustdoc::broken_intra_doc_links,
28 rustdoc::private_intra_doc_links,
29 trivial_casts,
30 trivial_numeric_casts,
31 unused_must_use,
32 unused_import_braces,
33 unused_qualifications,
34 clippy::pedantic
35)]
36#![allow(
37 clippy::module_name_repetitions,
38 clippy::missing_errors_doc,
39 clippy::type_complexity,
40 unused_extern_crates
41)]
42
43extern crate alloc;
44
45mod aead;
46mod error;
47mod kdf;
48mod sealed;
49
50pub mod kem;
51
52pub use aead::{Aead, Aes128Gcm, Aes256Gcm, ChaCha20Poly1305, ExportOnly, SealingAead};
53pub use error::HpkeError;
54pub use kdf::{HkdfSha256, HkdfSha384, HkdfSha512, Kdf};
55pub use kem::{
56 AuthKem, Kem,
57 dh::{
58 DhKemK256HkdfSha256, DhKemP256HkdfSha256, DhKemP384HkdfSha384, DhKemP521HkdfSha512,
59 DhKemX448HkdfSha512, DhKemX25519HkdfSha256,
60 },
61};
62
63#[cfg(feature = "pq")]
64pub use kem::pq::{MlKem768, MlKem1024, XWingDraft06};
65
66mod context;
67
68pub use context::Context;
69
70use alloc::vec::Vec;
71use core::marker::PhantomData;
72
73use zeroize::Zeroizing;
74
75use crate::kdf::{labeled_expand, labeled_extract};
76
77#[derive(Debug, Clone, Copy, Default)]
99pub struct Hpke<K: Kem, F: Kdf, A: Aead>(PhantomData<(K, F, A)>);
100
101pub(crate) mod modes {
102 pub const BASE: u8 = 0x00;
103 pub const PSK: u8 = 0x01;
104 pub const AUTH: u8 = 0x02;
105 pub const AUTH_PSK: u8 = 0x03;
106}
107
108#[inline]
109pub(crate) fn ciphersuite<K: Kem, F: Kdf, A: Aead>() -> [u8; 10] {
110 let mut s = [0u8; 10];
111 s[..4].copy_from_slice(b"HPKE");
112 s[4..6].copy_from_slice(&K::ID.to_be_bytes());
113 s[6..8].copy_from_slice(&F::ID.to_be_bytes());
114 s[8..10].copy_from_slice(&A::ID.to_be_bytes());
115 s
116}
117
118#[inline]
119fn verify_psk_inputs(mode: u8, psk: &[u8], psk_id: &[u8]) -> Result<(), HpkeError> {
120 let got_psk = !psk.is_empty();
121 let got_psk_id = !psk_id.is_empty();
122 if got_psk != got_psk_id {
123 return Err(HpkeError::InconsistentPsk);
124 }
125 if got_psk && (mode == modes::BASE || mode == modes::AUTH) {
126 return Err(HpkeError::UnnecessaryPsk);
127 }
128 if !got_psk && (mode == modes::PSK || mode == modes::AUTH_PSK) {
129 return Err(HpkeError::MissingPsk);
130 }
131 if got_psk && psk.len() < 32 {
132 return Err(HpkeError::InsecurePsk);
133 }
134 Ok(())
135}
136
137#[cfg(not(feature = "kat-internals"))]
138pub(crate) fn key_schedule<K: Kem, F: Kdf, A: Aead>(
139 mode: u8,
140 shared_secret: &[u8],
141 info: &[u8],
142 psk: &[u8],
143 psk_id: &[u8],
144) -> Result<Context<K, F, A>, HpkeError> {
145 key_schedule_inner::<K, F, A>(mode, shared_secret, info, psk, psk_id)
146}
147
148#[cfg(feature = "kat-internals")]
149#[doc(hidden)]
150pub fn key_schedule<K: Kem, F: Kdf, A: Aead>(
151 mode: u8,
152 shared_secret: &[u8],
153 info: &[u8],
154 psk: &[u8],
155 psk_id: &[u8],
156) -> Result<Context<K, F, A>, HpkeError> {
157 key_schedule_inner::<K, F, A>(mode, shared_secret, info, psk, psk_id)
158}
159
160fn key_schedule_inner<K: Kem, F: Kdf, A: Aead>(
161 mode: u8,
162 shared_secret: &[u8],
163 info: &[u8],
164 psk: &[u8],
165 psk_id: &[u8],
166) -> Result<Context<K, F, A>, HpkeError> {
167 verify_psk_inputs(mode, psk, psk_id)?;
168 let suite = ciphersuite::<K, F, A>();
169 let psk_id_hash = labeled_extract::<F>(&[], &suite, b"psk_id_hash", psk_id);
170 let info_hash = labeled_extract::<F>(&[], &suite, b"info_hash", info);
171
172 let mut ks_ctx = Vec::with_capacity(1 + psk_id_hash.len() + info_hash.len());
173 ks_ctx.push(mode);
174 ks_ctx.extend_from_slice(&psk_id_hash);
175 ks_ctx.extend_from_slice(&info_hash);
176
177 let secret = Zeroizing::new(labeled_extract::<F>(shared_secret, &suite, b"secret", psk));
178 let key = labeled_expand::<F>(&secret, &suite, b"key", &ks_ctx, A::KEY_LEN)?;
179 let base_nonce = labeled_expand::<F>(&secret, &suite, b"base_nonce", &ks_ctx, A::NONCE_LEN)?;
180 let exporter_secret = labeled_expand::<F>(&secret, &suite, b"exp", &ks_ctx, F::HASH_LEN)?;
181
182 Ok(Context::new(key, base_nonce, exporter_secret))
183}
184
185#[cfg(test)]
186mod ks_tests {
187 use super::*;
188
189 #[test]
190 fn psk_validation_inconsistent() {
191 let r = verify_psk_inputs(modes::PSK, b"", b"some_id");
192 assert_eq!(r, Err(HpkeError::InconsistentPsk));
193 let r = verify_psk_inputs(modes::PSK, &[0u8; 32], b"");
194 assert_eq!(r, Err(HpkeError::InconsistentPsk));
195 }
196
197 #[test]
198 fn psk_validation_missing() {
199 let r = verify_psk_inputs(modes::PSK, b"", b"");
200 assert_eq!(r, Err(HpkeError::MissingPsk));
201 }
202
203 #[test]
204 fn psk_validation_unnecessary() {
205 let r = verify_psk_inputs(modes::BASE, &[0u8; 32], b"id");
206 assert_eq!(r, Err(HpkeError::UnnecessaryPsk));
207 }
208
209 #[test]
210 fn psk_validation_too_short() {
211 let r = verify_psk_inputs(modes::PSK, b"too short", b"id");
212 assert_eq!(r, Err(HpkeError::InsecurePsk));
213 }
214
215 #[test]
216 fn psk_validation_ok() {
217 assert!(verify_psk_inputs(modes::BASE, b"", b"").is_ok());
218 assert!(verify_psk_inputs(modes::PSK, &[0u8; 32], b"id").is_ok());
219 }
220}
221
222use rand_core::{CryptoRng, RngCore};
223
224impl<K: Kem, F: Kdf, A: Aead> Hpke<K, F, A> {
225 pub fn setup_sender_base<R: CryptoRng + RngCore>(
227 rng: &mut R,
228 pk_r: &K::PublicKey,
229 info: &[u8],
230 ) -> Result<(K::EncappedKey, Context<K, F, A>), HpkeError> {
231 let (ss, enc) = K::encap(rng, pk_r)?;
232 let ctx = key_schedule::<K, F, A>(modes::BASE, ss.as_ref(), info, &[], &[])?;
233 Ok((enc, ctx))
234 }
235
236 pub fn setup_receiver_base(
238 enc: &K::EncappedKey,
239 sk_r: &K::PrivateKey,
240 info: &[u8],
241 ) -> Result<Context<K, F, A>, HpkeError> {
242 let ss = K::decap(enc, sk_r)?;
243 key_schedule::<K, F, A>(modes::BASE, ss.as_ref(), info, &[], &[])
244 }
245
246 pub fn setup_sender_psk<R: CryptoRng + RngCore>(
252 rng: &mut R,
253 pk_r: &K::PublicKey,
254 info: &[u8],
255 psk: &[u8],
256 psk_id: &[u8],
257 ) -> Result<(K::EncappedKey, Context<K, F, A>), HpkeError> {
258 let (ss, enc) = K::encap(rng, pk_r)?;
259 let ctx = key_schedule::<K, F, A>(modes::PSK, ss.as_ref(), info, psk, psk_id)?;
260 Ok((enc, ctx))
261 }
262
263 pub fn setup_receiver_psk(
269 enc: &K::EncappedKey,
270 sk_r: &K::PrivateKey,
271 info: &[u8],
272 psk: &[u8],
273 psk_id: &[u8],
274 ) -> Result<Context<K, F, A>, HpkeError> {
275 let ss = K::decap(enc, sk_r)?;
276 key_schedule::<K, F, A>(modes::PSK, ss.as_ref(), info, psk, psk_id)
277 }
278}
279
280impl<K: Kem, F: Kdf, A: SealingAead> Hpke<K, F, A> {
281 pub fn seal_base<R: CryptoRng + RngCore>(
283 rng: &mut R,
284 pk_r: &K::PublicKey,
285 info: &[u8],
286 aad: &[u8],
287 pt: &[u8],
288 ) -> Result<(K::EncappedKey, Vec<u8>), HpkeError> {
289 let (enc, mut ctx) = Self::setup_sender_base(rng, pk_r, info)?;
290 let ct = ctx.seal(aad, pt)?;
291 Ok((enc, ct))
292 }
293
294 pub fn open_base(
296 enc: &K::EncappedKey,
297 sk_r: &K::PrivateKey,
298 info: &[u8],
299 aad: &[u8],
300 ct: &[u8],
301 ) -> Result<Vec<u8>, HpkeError> {
302 let mut ctx = Self::setup_receiver_base(enc, sk_r, info)?;
303 ctx.open(aad, ct)
304 }
305
306 #[allow(clippy::too_many_arguments)]
308 pub fn seal_psk<R: CryptoRng + RngCore>(
309 rng: &mut R,
310 pk_r: &K::PublicKey,
311 info: &[u8],
312 aad: &[u8],
313 pt: &[u8],
314 psk: &[u8],
315 psk_id: &[u8],
316 ) -> Result<(K::EncappedKey, Vec<u8>), HpkeError> {
317 let (enc, mut ctx) = Self::setup_sender_psk(rng, pk_r, info, psk, psk_id)?;
318 let ct = ctx.seal(aad, pt)?;
319 Ok((enc, ct))
320 }
321
322 #[allow(clippy::too_many_arguments)]
324 pub fn open_psk(
325 enc: &K::EncappedKey,
326 sk_r: &K::PrivateKey,
327 info: &[u8],
328 aad: &[u8],
329 ct: &[u8],
330 psk: &[u8],
331 psk_id: &[u8],
332 ) -> Result<Vec<u8>, HpkeError> {
333 let mut ctx = Self::setup_receiver_psk(enc, sk_r, info, psk, psk_id)?;
334 ctx.open(aad, ct)
335 }
336}
337
338impl<K: AuthKem, F: Kdf, A: SealingAead> Hpke<K, F, A> {
339 pub fn seal_auth<R: CryptoRng + RngCore>(
341 rng: &mut R,
342 pk_r: &K::PublicKey,
343 info: &[u8],
344 aad: &[u8],
345 pt: &[u8],
346 sk_s: &K::PrivateKey,
347 ) -> Result<(K::EncappedKey, Vec<u8>), HpkeError> {
348 let (enc, mut ctx) = Self::setup_sender_auth(rng, pk_r, info, sk_s)?;
349 let ct = ctx.seal(aad, pt)?;
350 Ok((enc, ct))
351 }
352
353 pub fn open_auth(
355 enc: &K::EncappedKey,
356 sk_r: &K::PrivateKey,
357 info: &[u8],
358 aad: &[u8],
359 ct: &[u8],
360 pk_s: &K::PublicKey,
361 ) -> Result<Vec<u8>, HpkeError> {
362 let mut ctx = Self::setup_receiver_auth(enc, sk_r, info, pk_s)?;
363 ctx.open(aad, ct)
364 }
365
366 #[allow(clippy::too_many_arguments)]
368 pub fn seal_auth_psk<R: CryptoRng + RngCore>(
369 rng: &mut R,
370 pk_r: &K::PublicKey,
371 info: &[u8],
372 aad: &[u8],
373 pt: &[u8],
374 psk: &[u8],
375 psk_id: &[u8],
376 sk_s: &K::PrivateKey,
377 ) -> Result<(K::EncappedKey, Vec<u8>), HpkeError> {
378 let (enc, mut ctx) = Self::setup_sender_auth_psk(rng, pk_r, info, psk, psk_id, sk_s)?;
379 let ct = ctx.seal(aad, pt)?;
380 Ok((enc, ct))
381 }
382
383 #[allow(clippy::too_many_arguments)]
385 pub fn open_auth_psk(
386 enc: &K::EncappedKey,
387 sk_r: &K::PrivateKey,
388 info: &[u8],
389 aad: &[u8],
390 ct: &[u8],
391 psk: &[u8],
392 psk_id: &[u8],
393 pk_s: &K::PublicKey,
394 ) -> Result<Vec<u8>, HpkeError> {
395 let mut ctx = Self::setup_receiver_auth_psk(enc, sk_r, info, psk, psk_id, pk_s)?;
396 ctx.open(aad, ct)
397 }
398}
399
400impl<K: Kem, F: Kdf, A: Aead> Hpke<K, F, A> {
401 pub fn send_export_base<R: CryptoRng + RngCore>(
403 rng: &mut R,
404 pk_r: &K::PublicKey,
405 info: &[u8],
406 exporter_context: &[u8],
407 length: usize,
408 ) -> Result<(K::EncappedKey, Vec<u8>), HpkeError> {
409 let (enc, ctx) = Self::setup_sender_base(rng, pk_r, info)?;
410 Ok((enc, ctx.export(exporter_context, length)?))
411 }
412
413 pub fn receiver_export_base(
415 enc: &K::EncappedKey,
416 sk_r: &K::PrivateKey,
417 info: &[u8],
418 exporter_context: &[u8],
419 length: usize,
420 ) -> Result<Vec<u8>, HpkeError> {
421 let ctx = Self::setup_receiver_base(enc, sk_r, info)?;
422 ctx.export(exporter_context, length)
423 }
424
425 #[allow(clippy::too_many_arguments)]
427 pub fn send_export_psk<R: CryptoRng + RngCore>(
428 rng: &mut R,
429 pk_r: &K::PublicKey,
430 info: &[u8],
431 psk: &[u8],
432 psk_id: &[u8],
433 exporter_context: &[u8],
434 length: usize,
435 ) -> Result<(K::EncappedKey, Vec<u8>), HpkeError> {
436 let (enc, ctx) = Self::setup_sender_psk(rng, pk_r, info, psk, psk_id)?;
437 Ok((enc, ctx.export(exporter_context, length)?))
438 }
439
440 #[allow(clippy::too_many_arguments)]
442 pub fn receiver_export_psk(
443 enc: &K::EncappedKey,
444 sk_r: &K::PrivateKey,
445 info: &[u8],
446 psk: &[u8],
447 psk_id: &[u8],
448 exporter_context: &[u8],
449 length: usize,
450 ) -> Result<Vec<u8>, HpkeError> {
451 let ctx = Self::setup_receiver_psk(enc, sk_r, info, psk, psk_id)?;
452 ctx.export(exporter_context, length)
453 }
454}
455
456impl<K: AuthKem, F: Kdf, A: Aead> Hpke<K, F, A> {
457 pub fn send_export_auth<R: CryptoRng + RngCore>(
459 rng: &mut R,
460 pk_r: &K::PublicKey,
461 info: &[u8],
462 sk_s: &K::PrivateKey,
463 exporter_context: &[u8],
464 length: usize,
465 ) -> Result<(K::EncappedKey, Vec<u8>), HpkeError> {
466 let (enc, ctx) = Self::setup_sender_auth(rng, pk_r, info, sk_s)?;
467 Ok((enc, ctx.export(exporter_context, length)?))
468 }
469
470 pub fn receiver_export_auth(
472 enc: &K::EncappedKey,
473 sk_r: &K::PrivateKey,
474 info: &[u8],
475 pk_s: &K::PublicKey,
476 exporter_context: &[u8],
477 length: usize,
478 ) -> Result<Vec<u8>, HpkeError> {
479 let ctx = Self::setup_receiver_auth(enc, sk_r, info, pk_s)?;
480 ctx.export(exporter_context, length)
481 }
482
483 #[allow(clippy::too_many_arguments)]
485 pub fn send_export_auth_psk<R: CryptoRng + RngCore>(
486 rng: &mut R,
487 pk_r: &K::PublicKey,
488 info: &[u8],
489 psk: &[u8],
490 psk_id: &[u8],
491 sk_s: &K::PrivateKey,
492 exporter_context: &[u8],
493 length: usize,
494 ) -> Result<(K::EncappedKey, Vec<u8>), HpkeError> {
495 let (enc, ctx) = Self::setup_sender_auth_psk(rng, pk_r, info, psk, psk_id, sk_s)?;
496 Ok((enc, ctx.export(exporter_context, length)?))
497 }
498
499 #[allow(clippy::too_many_arguments)]
501 pub fn receiver_export_auth_psk(
502 enc: &K::EncappedKey,
503 sk_r: &K::PrivateKey,
504 info: &[u8],
505 psk: &[u8],
506 psk_id: &[u8],
507 pk_s: &K::PublicKey,
508 exporter_context: &[u8],
509 length: usize,
510 ) -> Result<Vec<u8>, HpkeError> {
511 let ctx = Self::setup_receiver_auth_psk(enc, sk_r, info, psk, psk_id, pk_s)?;
512 ctx.export(exporter_context, length)
513 }
514}
515
516impl<K: AuthKem, F: Kdf, A: Aead> Hpke<K, F, A> {
517 pub fn setup_sender_auth<R: CryptoRng + RngCore>(
519 rng: &mut R,
520 pk_r: &K::PublicKey,
521 info: &[u8],
522 sk_s: &K::PrivateKey,
523 ) -> Result<(K::EncappedKey, Context<K, F, A>), HpkeError> {
524 let (ss, enc) = K::auth_encap(rng, pk_r, sk_s)?;
525 let ctx = key_schedule::<K, F, A>(modes::AUTH, ss.as_ref(), info, &[], &[])?;
526 Ok((enc, ctx))
527 }
528
529 pub fn setup_receiver_auth(
531 enc: &K::EncappedKey,
532 sk_r: &K::PrivateKey,
533 info: &[u8],
534 pk_s: &K::PublicKey,
535 ) -> Result<Context<K, F, A>, HpkeError> {
536 let ss = K::auth_decap(enc, sk_r, pk_s)?;
537 key_schedule::<K, F, A>(modes::AUTH, ss.as_ref(), info, &[], &[])
538 }
539
540 pub fn setup_sender_auth_psk<R: CryptoRng + RngCore>(
546 rng: &mut R,
547 pk_r: &K::PublicKey,
548 info: &[u8],
549 psk: &[u8],
550 psk_id: &[u8],
551 sk_s: &K::PrivateKey,
552 ) -> Result<(K::EncappedKey, Context<K, F, A>), HpkeError> {
553 let (ss, enc) = K::auth_encap(rng, pk_r, sk_s)?;
554 let ctx = key_schedule::<K, F, A>(modes::AUTH_PSK, ss.as_ref(), info, psk, psk_id)?;
555 Ok((enc, ctx))
556 }
557
558 pub fn setup_receiver_auth_psk(
564 enc: &K::EncappedKey,
565 sk_r: &K::PrivateKey,
566 info: &[u8],
567 psk: &[u8],
568 psk_id: &[u8],
569 pk_s: &K::PublicKey,
570 ) -> Result<Context<K, F, A>, HpkeError> {
571 let ss = K::auth_decap(enc, sk_r, pk_s)?;
572 key_schedule::<K, F, A>(modes::AUTH_PSK, ss.as_ref(), info, psk, psk_id)
573 }
574}
575
576#[cfg(feature = "kat-internals")]
577#[doc(hidden)]
578pub mod __test_only {
579 pub use crate::key_schedule;
580}
581
582#[cfg(test)]
583mod hpke_tests {
584 use super::*;
585 use rand_core::{OsRng, TryRngCore as _};
586
587 type Suite = Hpke<DhKemX25519HkdfSha256, HkdfSha256, ChaCha20Poly1305>;
588
589 #[test]
590 fn setup_base_roundtrip_x25519_chacha() {
591 let mut os_rng = OsRng;
592 let mut rng = os_rng.unwrap_mut();
593 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
594 let (enc, mut sender) = Suite::setup_sender_base(&mut rng, &pk_r, b"info").unwrap();
595 let mut receiver = Suite::setup_receiver_base(&enc, &sk_r, b"info").unwrap();
596
597 let ct = sender.seal(b"aad", b"plaintext").unwrap();
598 let pt = receiver.open(b"aad", &ct).unwrap();
599 assert_eq!(pt, b"plaintext");
600 }
601
602 #[test]
603 fn setup_psk_roundtrip() {
604 let mut os_rng = OsRng;
605 let mut rng = os_rng.unwrap_mut();
606 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
607 let psk = [0xAAu8; 32];
608 let psk_id = b"id";
609 let (enc, mut s) = Suite::setup_sender_psk(&mut rng, &pk_r, b"info", &psk, psk_id).unwrap();
610 let mut r = Suite::setup_receiver_psk(&enc, &sk_r, b"info", &psk, psk_id).unwrap();
611 let ct = s.seal(b"aad", b"hi").unwrap();
612 assert_eq!(r.open(b"aad", &ct).unwrap(), b"hi");
613 }
614
615 #[test]
616 fn setup_auth_roundtrip() {
617 let mut os_rng = OsRng;
618 let mut rng = os_rng.unwrap_mut();
619 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
620 let (sk_s, pk_s) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
621 let (enc, mut s) = Suite::setup_sender_auth(&mut rng, &pk_r, b"info", &sk_s).unwrap();
622 let mut r = Suite::setup_receiver_auth(&enc, &sk_r, b"info", &pk_s).unwrap();
623 let ct = s.seal(b"aad", b"auth").unwrap();
624 assert_eq!(r.open(b"aad", &ct).unwrap(), b"auth");
625 }
626
627 #[test]
628 fn setup_auth_psk_roundtrip() {
629 let mut os_rng = OsRng;
630 let mut rng = os_rng.unwrap_mut();
631 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
632 let (sk_s, pk_s) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
633 let psk = [0xBBu8; 32];
634 let (enc, mut s) =
635 Suite::setup_sender_auth_psk(&mut rng, &pk_r, b"info", &psk, b"id", &sk_s).unwrap();
636 let mut r =
637 Suite::setup_receiver_auth_psk(&enc, &sk_r, b"info", &psk, b"id", &pk_s).unwrap();
638 let ct = s.seal(b"aad", b"auth-psk").unwrap();
639 assert_eq!(r.open(b"aad", &ct).unwrap(), b"auth-psk");
640 }
641
642 #[test]
643 fn single_shot_base_roundtrip() {
644 let mut os_rng = OsRng;
645 let mut rng = os_rng.unwrap_mut();
646 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
647 let (enc, ct) = Suite::seal_base(&mut rng, &pk_r, b"info", b"aad", b"hi").unwrap();
648 assert_eq!(
649 Suite::open_base(&enc, &sk_r, b"info", b"aad", &ct).unwrap(),
650 b"hi"
651 );
652 }
653
654 #[test]
655 fn single_shot_psk_roundtrip() {
656 let mut os_rng = OsRng;
657 let mut rng = os_rng.unwrap_mut();
658 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
659 let psk = [0u8; 32];
660 let (enc, ct) = Suite::seal_psk(&mut rng, &pk_r, b"i", b"a", b"p", &psk, b"id").unwrap();
661 assert_eq!(
662 Suite::open_psk(&enc, &sk_r, b"i", b"a", &ct, &psk, b"id").unwrap(),
663 b"p",
664 );
665 }
666
667 #[test]
668 fn single_shot_auth_roundtrip() {
669 let mut os_rng = OsRng;
670 let mut rng = os_rng.unwrap_mut();
671 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
672 let (sk_s, pk_s) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
673 let (enc, ct) = Suite::seal_auth(&mut rng, &pk_r, b"i", b"a", b"p", &sk_s).unwrap();
674 assert_eq!(
675 Suite::open_auth(&enc, &sk_r, b"i", b"a", &ct, &pk_s).unwrap(),
676 b"p",
677 );
678 }
679
680 #[test]
681 fn single_shot_auth_psk_roundtrip() {
682 let mut os_rng = OsRng;
683 let mut rng = os_rng.unwrap_mut();
684 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
685 let (sk_s, pk_s) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
686 let psk = [0u8; 32];
687 let (enc, ct) =
688 Suite::seal_auth_psk(&mut rng, &pk_r, b"i", b"a", b"p", &psk, b"id", &sk_s).unwrap();
689 assert_eq!(
690 Suite::open_auth_psk(&enc, &sk_r, b"i", b"a", &ct, &psk, b"id", &pk_s).unwrap(),
691 b"p",
692 );
693 }
694
695 #[test]
696 fn export_sender_receiver_match_base() {
697 let mut os_rng = OsRng;
698 let mut rng = os_rng.unwrap_mut();
699 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
700 let (enc, sender_secret) =
701 Suite::send_export_base(&mut rng, &pk_r, b"info", b"ctx", 32).unwrap();
702 let receiver_secret =
703 Suite::receiver_export_base(&enc, &sk_r, b"info", b"ctx", 32).unwrap();
704 assert_eq!(sender_secret, receiver_secret);
705 assert_eq!(sender_secret.len(), 32);
706 }
707
708 #[test]
712 fn export_only_suite_compiles() {
713 type ExportSuite = Hpke<DhKemX25519HkdfSha256, HkdfSha256, ExportOnly>;
714 let mut os_rng = OsRng;
715 let mut rng = os_rng.unwrap_mut();
716 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
717 let (enc, sec) =
718 ExportSuite::send_export_base(&mut rng, &pk_r, b"info", b"ctx", 32).unwrap();
719 let recv = ExportSuite::receiver_export_base(&enc, &sk_r, b"info", b"ctx", 32).unwrap();
720 assert_eq!(sec, recv);
721 }
722
723 #[test]
724 fn export_sender_receiver_match_psk() {
725 let mut os_rng = OsRng;
726 let mut rng = os_rng.unwrap_mut();
727 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
728 let psk = [0x33u8; 32];
729 let (enc, sec) =
730 Suite::send_export_psk(&mut rng, &pk_r, b"info", &psk, b"id", b"ctx", 64).unwrap();
731 let recv =
732 Suite::receiver_export_psk(&enc, &sk_r, b"info", &psk, b"id", b"ctx", 64).unwrap();
733 assert_eq!(sec, recv);
734 assert_eq!(sec.len(), 64);
735 }
736
737 #[test]
738 fn export_sender_receiver_match_auth() {
739 let mut os_rng = OsRng;
740 let mut rng = os_rng.unwrap_mut();
741 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
742 let (sk_s, pk_s) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
743 let (enc, sec) =
744 Suite::send_export_auth(&mut rng, &pk_r, b"info", &sk_s, b"ctx", 48).unwrap();
745 let recv = Suite::receiver_export_auth(&enc, &sk_r, b"info", &pk_s, b"ctx", 48).unwrap();
746 assert_eq!(sec, recv);
747 assert_eq!(sec.len(), 48);
748 }
749
750 #[test]
751 fn export_sender_receiver_match_auth_psk() {
752 let mut os_rng = OsRng;
753 let mut rng = os_rng.unwrap_mut();
754 let (sk_r, pk_r) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
755 let (sk_s, pk_s) = DhKemX25519HkdfSha256::generate(&mut rng).unwrap();
756 let psk = [0x77u8; 32];
757 let (enc, sec) =
758 Suite::send_export_auth_psk(&mut rng, &pk_r, b"info", &psk, b"id", &sk_s, b"ctx", 32)
759 .unwrap();
760 let recv =
761 Suite::receiver_export_auth_psk(&enc, &sk_r, b"info", &psk, b"id", &pk_s, b"ctx", 32)
762 .unwrap();
763 assert_eq!(sec, recv);
764 assert_eq!(sec.len(), 32);
765 }
766}