1use alloc::boxed::Box;
2use alloc::vec::Vec;
3use core::fmt::{self, Debug, Formatter};
4
5use aws_lc_rs::aead::{self, Aad, LessSafeKey, NONCE_LEN, Nonce, UnboundKey};
6use aws_lc_rs::agreement;
7use aws_lc_rs::cipher::{AES_128_KEY_LEN, AES_256_KEY_LEN};
8use aws_lc_rs::digest::{SHA256_OUTPUT_LEN, SHA384_OUTPUT_LEN, SHA512_OUTPUT_LEN};
9use aws_lc_rs::encoding::{AsBigEndian, Curve25519SeedBin, EcPrivateKeyBin};
10use zeroize::Zeroize;
11
12use crate::crypto::aws_lc_rs::hmac::{HMAC_SHA256, HMAC_SHA384, HMAC_SHA512};
13use crate::crypto::aws_lc_rs::unspecified_err;
14use crate::crypto::hpke::{
15 EncapsulatedSecret, Hpke, HpkeOpener, HpkePrivateKey, HpkePublicKey, HpkeSealer, HpkeSuite,
16};
17use crate::crypto::tls13::{HkdfExpander, HkdfPrkExtract, HkdfUsingHmac, expand};
18use crate::msgs::enums::{HpkeAead, HpkeKdf, HpkeKem};
19use crate::msgs::handshake::HpkeSymmetricCipherSuite;
20#[cfg(feature = "std")]
21use crate::sync::Arc;
22use crate::{Error, OtherError};
23
24pub static ALL_SUPPORTED_SUITES: &[&dyn Hpke] = &[
26 DH_KEM_P256_HKDF_SHA256_AES_128,
27 DH_KEM_P256_HKDF_SHA256_AES_256,
28 #[cfg(not(feature = "fips"))]
29 DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305,
30 DH_KEM_P384_HKDF_SHA384_AES_128,
31 DH_KEM_P384_HKDF_SHA384_AES_256,
32 #[cfg(not(feature = "fips"))]
33 DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305,
34 DH_KEM_P521_HKDF_SHA512_AES_128,
35 DH_KEM_P521_HKDF_SHA512_AES_256,
36 #[cfg(not(feature = "fips"))]
37 DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305,
38 #[cfg(not(feature = "fips"))]
39 DH_KEM_X25519_HKDF_SHA256_AES_128,
40 #[cfg(not(feature = "fips"))]
41 DH_KEM_X25519_HKDF_SHA256_AES_256,
42 #[cfg(not(feature = "fips"))]
43 DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305,
44];
45
46pub static DH_KEM_P256_HKDF_SHA256_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA256_OUTPUT_LEN> =
49 &HpkeAwsLcRs {
50 suite: HpkeSuite {
51 kem: HpkeKem::DHKEM_P256_HKDF_SHA256,
52 sym: HpkeSymmetricCipherSuite {
53 kdf_id: HpkeKdf::HKDF_SHA256,
54 aead_id: HpkeAead::AES_128_GCM,
55 },
56 },
57 dh_kem: DH_KEM_P256_HKDF_SHA256,
58 hkdf: RING_HKDF_HMAC_SHA256,
59 aead: &aead::AES_128_GCM,
60 };
61
62pub static DH_KEM_P256_HKDF_SHA256_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA256_OUTPUT_LEN> =
65 &HpkeAwsLcRs {
66 suite: HpkeSuite {
67 kem: HpkeKem::DHKEM_P256_HKDF_SHA256,
68 sym: HpkeSymmetricCipherSuite {
69 kdf_id: HpkeKdf::HKDF_SHA256,
70 aead_id: HpkeAead::AES_256_GCM,
71 },
72 },
73 dh_kem: DH_KEM_P256_HKDF_SHA256,
74 hkdf: RING_HKDF_HMAC_SHA256,
75 aead: &aead::AES_256_GCM,
76 };
77
78pub static DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305: &HpkeAwsLcRs<
81 CHACHA_KEY_LEN,
82 SHA256_OUTPUT_LEN,
83> = &HpkeAwsLcRs {
84 suite: HpkeSuite {
85 kem: HpkeKem::DHKEM_P256_HKDF_SHA256,
86 sym: HpkeSymmetricCipherSuite {
87 kdf_id: HpkeKdf::HKDF_SHA256,
88 aead_id: HpkeAead::CHACHA20_POLY_1305,
89 },
90 },
91 dh_kem: DH_KEM_P256_HKDF_SHA256,
92 hkdf: RING_HKDF_HMAC_SHA256,
93 aead: &aead::CHACHA20_POLY1305,
94};
95
96pub static DH_KEM_P384_HKDF_SHA384_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA384_OUTPUT_LEN> =
99 &HpkeAwsLcRs {
100 suite: HpkeSuite {
101 kem: HpkeKem::DHKEM_P384_HKDF_SHA384,
102 sym: HpkeSymmetricCipherSuite {
103 kdf_id: HpkeKdf::HKDF_SHA384,
104 aead_id: HpkeAead::AES_128_GCM,
105 },
106 },
107 dh_kem: DH_KEM_P384_HKDF_SHA384,
108 hkdf: RING_HKDF_HMAC_SHA384,
109 aead: &aead::AES_128_GCM,
110 };
111
112pub static DH_KEM_P384_HKDF_SHA384_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA384_OUTPUT_LEN> =
115 &HpkeAwsLcRs {
116 suite: HpkeSuite {
117 kem: HpkeKem::DHKEM_P384_HKDF_SHA384,
118 sym: HpkeSymmetricCipherSuite {
119 kdf_id: HpkeKdf::HKDF_SHA384,
120 aead_id: HpkeAead::AES_256_GCM,
121 },
122 },
123 dh_kem: DH_KEM_P384_HKDF_SHA384,
124 hkdf: RING_HKDF_HMAC_SHA384,
125 aead: &aead::AES_256_GCM,
126 };
127
128pub static DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305: &HpkeAwsLcRs<
131 CHACHA_KEY_LEN,
132 SHA384_OUTPUT_LEN,
133> = &HpkeAwsLcRs {
134 suite: HpkeSuite {
135 kem: HpkeKem::DHKEM_P384_HKDF_SHA384,
136 sym: HpkeSymmetricCipherSuite {
137 kdf_id: HpkeKdf::HKDF_SHA384,
138 aead_id: HpkeAead::CHACHA20_POLY_1305,
139 },
140 },
141 dh_kem: DH_KEM_P384_HKDF_SHA384,
142 hkdf: RING_HKDF_HMAC_SHA384,
143 aead: &aead::CHACHA20_POLY1305,
144};
145
146pub static DH_KEM_P521_HKDF_SHA512_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA512_OUTPUT_LEN> =
149 &HpkeAwsLcRs {
150 suite: HpkeSuite {
151 kem: HpkeKem::DHKEM_P521_HKDF_SHA512,
152 sym: HpkeSymmetricCipherSuite {
153 kdf_id: HpkeKdf::HKDF_SHA512,
154 aead_id: HpkeAead::AES_128_GCM,
155 },
156 },
157 dh_kem: DH_KEM_P521_HKDF_SHA512,
158 hkdf: RING_HKDF_HMAC_SHA512,
159 aead: &aead::AES_128_GCM,
160 };
161
162pub static DH_KEM_P521_HKDF_SHA512_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA512_OUTPUT_LEN> =
165 &HpkeAwsLcRs {
166 suite: HpkeSuite {
167 kem: HpkeKem::DHKEM_P521_HKDF_SHA512,
168 sym: HpkeSymmetricCipherSuite {
169 kdf_id: HpkeKdf::HKDF_SHA512,
170 aead_id: HpkeAead::AES_256_GCM,
171 },
172 },
173 dh_kem: DH_KEM_P521_HKDF_SHA512,
174 hkdf: RING_HKDF_HMAC_SHA512,
175 aead: &aead::AES_256_GCM,
176 };
177
178pub static DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305: &HpkeAwsLcRs<
181 CHACHA_KEY_LEN,
182 SHA512_OUTPUT_LEN,
183> = &HpkeAwsLcRs {
184 suite: HpkeSuite {
185 kem: HpkeKem::DHKEM_P521_HKDF_SHA512,
186 sym: HpkeSymmetricCipherSuite {
187 kdf_id: HpkeKdf::HKDF_SHA512,
188 aead_id: HpkeAead::CHACHA20_POLY_1305,
189 },
190 },
191 dh_kem: DH_KEM_P521_HKDF_SHA512,
192 hkdf: RING_HKDF_HMAC_SHA512,
193 aead: &aead::CHACHA20_POLY1305,
194};
195
196pub static DH_KEM_X25519_HKDF_SHA256_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA256_OUTPUT_LEN> =
199 &HpkeAwsLcRs {
200 suite: HpkeSuite {
201 kem: HpkeKem::DHKEM_X25519_HKDF_SHA256,
202 sym: HpkeSymmetricCipherSuite {
203 kdf_id: HpkeKdf::HKDF_SHA256,
204 aead_id: HpkeAead::AES_128_GCM,
205 },
206 },
207 dh_kem: DH_KEM_X25519_HKDF_SHA256,
208 hkdf: RING_HKDF_HMAC_SHA256,
209 aead: &aead::AES_128_GCM,
210 };
211
212pub static DH_KEM_X25519_HKDF_SHA256_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA256_OUTPUT_LEN> =
215 &HpkeAwsLcRs {
216 suite: HpkeSuite {
217 kem: HpkeKem::DHKEM_X25519_HKDF_SHA256,
218 sym: HpkeSymmetricCipherSuite {
219 kdf_id: HpkeKdf::HKDF_SHA256,
220 aead_id: HpkeAead::AES_256_GCM,
221 },
222 },
223 dh_kem: DH_KEM_X25519_HKDF_SHA256,
224 hkdf: RING_HKDF_HMAC_SHA256,
225 aead: &aead::AES_256_GCM,
226 };
227
228pub static DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305: &HpkeAwsLcRs<
231 CHACHA_KEY_LEN,
232 SHA256_OUTPUT_LEN,
233> = &HpkeAwsLcRs {
234 suite: HpkeSuite {
235 kem: HpkeKem::DHKEM_X25519_HKDF_SHA256,
236 sym: HpkeSymmetricCipherSuite {
237 kdf_id: HpkeKdf::HKDF_SHA256,
238 aead_id: HpkeAead::CHACHA20_POLY_1305,
239 },
240 },
241 dh_kem: DH_KEM_X25519_HKDF_SHA256,
242 hkdf: RING_HKDF_HMAC_SHA256,
243 aead: &aead::CHACHA20_POLY1305,
244};
245
246pub struct HpkeAwsLcRs<const KEY_SIZE: usize, const KDF_SIZE: usize> {
248 suite: HpkeSuite,
249 dh_kem: &'static DhKem<KDF_SIZE>,
250 hkdf: &'static dyn HkdfPrkExtract,
251 aead: &'static aead::Algorithm,
252}
253
254impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
255 fn key_schedule(
259 &self,
260 shared_secret: KemSharedSecret<KDF_SIZE>,
261 info: &[u8],
262 ) -> Result<KeySchedule<KEY_SIZE>, Error> {
263 let suite_id = LabeledSuiteId::Hpke(self.suite);
267 let psk_id_hash = labeled_extract_for_prk(self.hkdf, suite_id, None, Label::PskIdHash, &[]);
268 let info_hash = labeled_extract_for_prk(self.hkdf, suite_id, None, Label::InfoHash, info);
269 let key_schedule_context = [
270 &[0][..], &psk_id_hash,
272 &info_hash,
273 ]
274 .concat();
275
276 let key = AeadKey(self.key_schedule_labeled_expand::<KEY_SIZE>(
277 &shared_secret,
278 &key_schedule_context,
279 Label::Key,
280 ));
281
282 let base_nonce = self.key_schedule_labeled_expand::<NONCE_LEN>(
283 &shared_secret,
284 &key_schedule_context,
285 Label::BaseNonce,
286 );
287
288 Ok(KeySchedule {
289 aead: self.aead,
290 key,
291 base_nonce,
292 seq_num: 0,
293 })
294 }
295
296 fn key_schedule_labeled_expand<const L: usize>(
297 &self,
298 shared_secret: &KemSharedSecret<KDF_SIZE>,
299 key_schedule_context: &[u8],
300 label: Label,
301 ) -> [u8; L] {
302 let suite_id = LabeledSuiteId::Hpke(self.suite);
303 labeled_expand::<L>(
304 suite_id,
305 labeled_extract_for_expand(
306 self.hkdf,
307 suite_id,
308 Some(&shared_secret.0),
309 Label::Secret,
310 &[],
311 ),
312 label,
313 key_schedule_context,
314 )
315 }
316}
317
318impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Hpke for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
319 fn seal(
320 &self,
321 info: &[u8],
322 aad: &[u8],
323 plaintext: &[u8],
324 pub_key: &HpkePublicKey,
325 ) -> Result<(EncapsulatedSecret, Vec<u8>), Error> {
326 let (encap, mut sealer) = self.setup_sealer(info, pub_key)?;
327 Ok((encap, sealer.seal(aad, plaintext)?))
328 }
329
330 fn setup_sealer(
331 &self,
332 info: &[u8],
333 pub_key: &HpkePublicKey,
334 ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error> {
335 let (encap, sealer) = Sealer::new(self, info, pub_key)?;
336 Ok((encap, Box::new(sealer)))
337 }
338
339 fn open(
340 &self,
341 enc: &EncapsulatedSecret,
342 info: &[u8],
343 aad: &[u8],
344 ciphertext: &[u8],
345 secret_key: &HpkePrivateKey,
346 ) -> Result<Vec<u8>, Error> {
347 self.setup_opener(enc, info, secret_key)?
348 .open(aad, ciphertext)
349 }
350
351 fn setup_opener(
352 &self,
353 enc: &EncapsulatedSecret,
354 info: &[u8],
355 secret_key: &HpkePrivateKey,
356 ) -> Result<Box<dyn HpkeOpener + 'static>, Error> {
357 Ok(Box::new(Opener::new(self, enc, info, secret_key)?))
358 }
359
360 fn fips(&self) -> bool {
361 matches!(
362 (self.suite.kem, self.suite.sym.aead_id),
366 (
367 HpkeKem::DHKEM_P256_HKDF_SHA256
369 | HpkeKem::DHKEM_P384_HKDF_SHA384
370 | HpkeKem::DHKEM_P521_HKDF_SHA512,
371 HpkeAead::AES_128_GCM | HpkeAead::AES_256_GCM,
373 )
374 )
375 }
376
377 fn generate_key_pair(&self) -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
378 (self.dh_kem.key_generator)()
379 }
380
381 fn suite(&self) -> HpkeSuite {
382 self.suite
383 }
384}
385
386impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
387 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
388 self.suite.fmt(f)
389 }
390}
391
392struct Sealer<const KEY_SIZE: usize, const KDF_SIZE: usize> {
394 key_schedule: KeySchedule<KEY_SIZE>,
395}
396
397impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Sealer<KEY_SIZE, KDF_SIZE> {
398 fn new(
402 suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>,
403 info: &[u8],
404 pub_key: &HpkePublicKey,
405 ) -> Result<(EncapsulatedSecret, Self), Error> {
406 let (shared_secret, enc) = suite.dh_kem.encap(pub_key)?;
412 let key_schedule = suite.key_schedule(shared_secret, info)?;
413 Ok((enc, Self { key_schedule }))
414 }
415
416 #[cfg(test)]
419 fn test_only_new(
420 suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>,
421 info: &[u8],
422 pub_key: &HpkePublicKey,
423 sk_e: &[u8],
424 ) -> Result<(EncapsulatedSecret, Self), Error> {
425 let (shared_secret, enc) = suite
426 .dh_kem
427 .test_only_encap(pub_key, sk_e)?;
428 let key_schedule = suite.key_schedule(shared_secret, info)?;
429 Ok((enc, Self { key_schedule }))
430 }
431}
432
433impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeSealer for Sealer<KEY_SIZE, KDF_SIZE> {
434 fn seal(&mut self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
435 let key = UnboundKey::new(self.key_schedule.aead, &self.key_schedule.key.0)
441 .map_err(unspecified_err)?;
442 let sealing_key = LessSafeKey::new(key);
443
444 let nonce = self.key_schedule.compute_nonce();
445 self.key_schedule
446 .increment_seq_num()
447 .map_err(unspecified_err)?;
448
449 let mut in_out_buffer = Vec::from(plaintext);
450 sealing_key
451 .seal_in_place_append_tag(nonce, Aad::from(aad), &mut in_out_buffer)
452 .map_err(unspecified_err)?;
453
454 Ok(in_out_buffer)
455 }
456}
457
458impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for Sealer<KEY_SIZE, KDF_SIZE> {
459 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
460 f.debug_struct("Sealer").finish()
461 }
462}
463
464struct Opener<const KEY_SIZE: usize, const KDF_SIZE: usize> {
466 key_schedule: KeySchedule<KEY_SIZE>,
467}
468
469impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Opener<KEY_SIZE, KDF_SIZE> {
470 fn new(
474 suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>,
475 enc: &EncapsulatedSecret,
476 info: &[u8],
477 secret_key: &HpkePrivateKey,
478 ) -> Result<Self, Error> {
479 Ok(Self {
484 key_schedule: suite.key_schedule(suite.dh_kem.decap(enc, secret_key)?, info)?,
485 })
486 }
487}
488
489impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeOpener for Opener<KEY_SIZE, KDF_SIZE> {
490 fn open(&mut self, aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
491 let key = UnboundKey::new(self.key_schedule.aead, &self.key_schedule.key.0)
499 .map_err(unspecified_err)?;
500 let opening_key = LessSafeKey::new(key);
501
502 let mut in_out_buffer = Vec::from(ciphertext);
503 let plaintext = opening_key
504 .open_in_place(
505 self.key_schedule.compute_nonce(),
506 Aad::from(aad),
507 &mut in_out_buffer,
508 )
509 .map_err(unspecified_err)?;
510 self.key_schedule
511 .increment_seq_num()
512 .map_err(unspecified_err)?;
513
514 Ok(plaintext.to_vec())
515 }
516}
517
518impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for Opener<KEY_SIZE, KDF_SIZE> {
519 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
520 f.debug_struct("Opener").finish()
521 }
522}
523
524struct DhKem<const KDF_SIZE: usize> {
530 id: HpkeKem,
531 agreement_algorithm: &'static agreement::Algorithm,
532 key_generator:
533 &'static (dyn Fn() -> Result<(HpkePublicKey, HpkePrivateKey), Error> + Send + Sync),
534 hkdf: &'static dyn HkdfPrkExtract,
535}
536
537impl<const KDF_SIZE: usize> DhKem<KDF_SIZE> {
538 fn encap(
542 &self,
543 recipient: &HpkePublicKey,
544 ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
545 let sk_e =
549 agreement::PrivateKey::generate(self.agreement_algorithm).map_err(unspecified_err)?;
550 self.encap_impl(recipient, sk_e)
551 }
552
553 #[cfg(test)]
556 fn test_only_encap(
557 &self,
558 recipient: &HpkePublicKey,
559 test_only_ske: &[u8],
560 ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
561 let sk_e = agreement::PrivateKey::from_private_key(self.agreement_algorithm, test_only_ske)
563 .map_err(key_rejected_err)?;
564 self.encap_impl(recipient, sk_e)
565 }
566
567 fn encap_impl(
568 &self,
569 recipient: &HpkePublicKey,
570 sk_e: agreement::PrivateKey,
571 ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
572 let enc = sk_e
584 .compute_public_key()
585 .map_err(unspecified_err)?;
586 let pk_r = agreement::UnparsedPublicKey::new(self.agreement_algorithm, &recipient.0);
587 let kem_context = [enc.as_ref(), pk_r.bytes()].concat();
588
589 let shared_secret = agreement::agree(&sk_e, pk_r, aws_lc_rs::error::Unspecified, |dh| {
590 Ok(self.extract_and_expand(dh, &kem_context))
591 })
592 .map_err(unspecified_err)?;
593
594 Ok((
595 KemSharedSecret(shared_secret),
596 EncapsulatedSecret(enc.as_ref().into()),
597 ))
598 }
599
600 fn decap(
604 &self,
605 enc: &EncapsulatedSecret,
606 recipient: &HpkePrivateKey,
607 ) -> Result<KemSharedSecret<KDF_SIZE>, Error> {
608 let pk_e = agreement::UnparsedPublicKey::new(self.agreement_algorithm, &enc.0);
619 let sk_r = agreement::PrivateKey::from_private_key(
620 self.agreement_algorithm,
621 recipient.secret_bytes(),
622 )
623 .map_err(key_rejected_err)?;
624 let pk_rm = sk_r
625 .compute_public_key()
626 .map_err(unspecified_err)?;
627 let kem_context = [&enc.0, pk_rm.as_ref()].concat();
628
629 let shared_secret = agreement::agree(&sk_r, pk_e, aws_lc_rs::error::Unspecified, |dh| {
630 Ok(self.extract_and_expand(dh, &kem_context))
631 })
632 .map_err(unspecified_err)?;
633
634 Ok(KemSharedSecret(shared_secret))
635 }
636
637 fn extract_and_expand(&self, dh: &[u8], kem_context: &[u8]) -> [u8; KDF_SIZE] {
641 let suite_id = LabeledSuiteId::Kem(self.id);
648 labeled_expand(
649 suite_id,
650 labeled_extract_for_expand(self.hkdf, suite_id, None, Label::EaePrk, dh),
651 Label::SharedSecret,
652 kem_context,
653 )
654 }
655}
656
657static DH_KEM_P256_HKDF_SHA256: &DhKem<SHA256_OUTPUT_LEN> = &DhKem {
658 id: HpkeKem::DHKEM_P256_HKDF_SHA256,
659 agreement_algorithm: &agreement::ECDH_P256,
660 key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P256),
661 hkdf: RING_HKDF_HMAC_SHA256,
662};
663
664static DH_KEM_P384_HKDF_SHA384: &DhKem<SHA384_OUTPUT_LEN> = &DhKem {
665 id: HpkeKem::DHKEM_P384_HKDF_SHA384,
666 agreement_algorithm: &agreement::ECDH_P384,
667 key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P384),
668 hkdf: RING_HKDF_HMAC_SHA384,
669};
670
671static DH_KEM_P521_HKDF_SHA512: &DhKem<SHA512_OUTPUT_LEN> = &DhKem {
672 id: HpkeKem::DHKEM_P521_HKDF_SHA512,
673 agreement_algorithm: &agreement::ECDH_P521,
674 key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P521),
675 hkdf: RING_HKDF_HMAC_SHA512,
676};
677
678static DH_KEM_X25519_HKDF_SHA256: &DhKem<SHA256_OUTPUT_LEN> = &DhKem {
679 id: HpkeKem::DHKEM_X25519_HKDF_SHA256,
680 agreement_algorithm: &agreement::X25519,
681 key_generator: &generate_x25519_key_pair,
682 hkdf: RING_HKDF_HMAC_SHA256,
683};
684
685fn generate_p_curve_key_pair(
692 alg: &'static agreement::Algorithm,
693) -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
694 debug_assert_ne!(alg, &agreement::X25519);
698 let (public_key, private_key) = generate_key_pair(alg)?;
699 let raw_private_key: EcPrivateKeyBin<'_> = private_key
700 .as_be_bytes()
701 .map_err(unspecified_err)?;
702 Ok((
703 public_key,
704 HpkePrivateKey::from(raw_private_key.as_ref().to_vec()),
705 ))
706}
707
708fn generate_x25519_key_pair() -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
715 let (public_key, private_key) = generate_key_pair(&agreement::X25519)?;
716 let raw_private_key: Curve25519SeedBin<'_> = private_key
717 .as_be_bytes()
718 .map_err(unspecified_err)?;
719 Ok((
720 public_key,
721 HpkePrivateKey::from(raw_private_key.as_ref().to_vec()),
722 ))
723}
724
725fn generate_key_pair(
726 alg: &'static agreement::Algorithm,
727) -> Result<(HpkePublicKey, agreement::PrivateKey), Error> {
728 let private_key = agreement::PrivateKey::generate(alg).map_err(unspecified_err)?;
729 let public_key = HpkePublicKey(
730 private_key
731 .compute_public_key()
732 .map_err(unspecified_err)?
733 .as_ref()
734 .to_vec(),
735 );
736 Ok((public_key, private_key))
737}
738
739struct KeySchedule<const KEY_SIZE: usize> {
742 aead: &'static aead::Algorithm,
743 key: AeadKey<KEY_SIZE>,
744 base_nonce: [u8; NONCE_LEN],
745 seq_num: u32,
746}
747
748impl<const KEY_SIZE: usize> KeySchedule<KEY_SIZE> {
749 fn compute_nonce(&self) -> Nonce {
753 let mut nonce = [0; NONCE_LEN];
761 let seq_bytes = self.seq_num.to_be_bytes();
762 nonce[NONCE_LEN - seq_bytes.len()..].copy_from_slice(&seq_bytes);
763
764 for (n, &b) in nonce.iter_mut().zip(&self.base_nonce) {
765 *n ^= b;
766 }
767
768 Nonce::assume_unique_for_key(nonce)
769 }
770
771 fn increment_seq_num(&mut self) -> Result<(), aws_lc_rs::error::Unspecified> {
775 let max_seq_num = (1u128 << (NONCE_LEN * 8)) - 1;
783
784 if u128::from(self.seq_num) >= max_seq_num {
787 return Err(aws_lc_rs::error::Unspecified);
788 }
789
790 self.seq_num += 1;
791 Ok(())
792 }
793}
794
795fn labeled_extract_for_expand(
799 hkdf: &'static dyn HkdfPrkExtract,
800 suite_id: LabeledSuiteId,
801 salt: Option<&[u8]>,
802 label: Label,
803 ikm: &[u8],
804) -> Box<dyn HkdfExpander> {
805 let labeled_ikm = [&b"HPKE-v1"[..], &suite_id.encoded(), label.as_ref(), ikm].concat();
810 hkdf.extract_from_secret(salt, &labeled_ikm)
811}
812
813fn labeled_extract_for_prk(
817 hkdf: &'static dyn HkdfPrkExtract,
818 suite_id: LabeledSuiteId,
819 salt: Option<&[u8]>,
820 label: Label,
821 ikm: &[u8],
822) -> Vec<u8> {
823 let labeled_ikm = [&b"HPKE-v1"[..], &suite_id.encoded(), label.as_ref(), ikm].concat();
828 hkdf.extract_prk_from_secret(salt, &labeled_ikm)
829}
830
831fn labeled_expand<const L: usize>(
835 suite_id: LabeledSuiteId,
836 expander: Box<dyn HkdfExpander>,
837 label: Label,
838 kem_context: &[u8],
839) -> [u8; L] {
840 let output_len = u16::to_be_bytes(L as u16);
846 let info = &[
847 &output_len[..],
848 b"HPKE-v1",
849 &suite_id.encoded(),
850 label.as_ref(),
851 kem_context,
852 ];
853
854 expand(&*expander, info)
855}
856
857#[derive(Debug)]
859enum Label {
860 PskIdHash,
861 InfoHash,
862 Secret,
863 Key,
864 BaseNonce,
865 EaePrk,
866 SharedSecret,
867}
868
869impl AsRef<[u8]> for Label {
870 fn as_ref(&self) -> &[u8] {
871 match self {
872 Self::PskIdHash => b"psk_id_hash",
873 Self::InfoHash => b"info_hash",
874 Self::Secret => b"secret",
875 Self::Key => b"key",
876 Self::BaseNonce => b"base_nonce",
877 Self::EaePrk => b"eae_prk",
878 Self::SharedSecret => b"shared_secret",
879 }
880 }
881}
882
883#[derive(Debug, Copy, Clone)]
886enum LabeledSuiteId {
887 Hpke(HpkeSuite),
888 Kem(HpkeKem),
889}
890
891impl LabeledSuiteId {
892 fn encoded(&self) -> Vec<u8> {
899 match self {
900 Self::Hpke(suite) => [
901 &b"HPKE"[..],
902 &u16::from(suite.kem).to_be_bytes(),
903 &u16::from(suite.sym.kdf_id).to_be_bytes(),
904 &u16::from(suite.sym.aead_id).to_be_bytes(),
905 ]
906 .concat(),
907 Self::Kem(kem) => [&b"KEM"[..], &u16::from(*kem).to_be_bytes()].concat(),
908 }
909 }
910}
911
912struct AeadKey<const KEY_LEN: usize>([u8; KEY_LEN]);
914
915impl<const KEY_LEN: usize> Drop for AeadKey<KEY_LEN> {
916 fn drop(&mut self) {
917 self.0.zeroize()
918 }
919}
920
921struct KemSharedSecret<const KDF_LEN: usize>([u8; KDF_LEN]);
923
924impl<const KDF_LEN: usize> Drop for KemSharedSecret<KDF_LEN> {
925 fn drop(&mut self) {
926 self.0.zeroize();
927 }
928}
929
930fn key_rejected_err(_e: aws_lc_rs::error::KeyRejected) -> Error {
931 #[cfg(feature = "std")]
932 {
933 Error::Other(OtherError(Arc::new(_e)))
934 }
935 #[cfg(not(feature = "std"))]
936 {
937 Error::Other(OtherError())
938 }
939}
940
941const CHACHA_KEY_LEN: usize = 32;
944
945static RING_HKDF_HMAC_SHA256: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA256);
946static RING_HKDF_HMAC_SHA384: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA384);
947static RING_HKDF_HMAC_SHA512: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA512);
948
949#[cfg(test)]
950mod tests {
951 use alloc::{format, vec};
952
953 use super::*;
954
955 #[test]
956 fn smoke_test() {
957 for suite in ALL_SUPPORTED_SUITES {
958 _ = format!("{suite:?}"); let (pk, sk) = suite.generate_key_pair().unwrap();
962
963 let info = &[
965 0x4f, 0x64, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x47, 0x72, 0x65, 0x63, 0x69,
966 0x61, 0x6e, 0x20, 0x55, 0x72, 0x6e,
967 ][..];
968
969 let (enc, mut sealer) = suite.setup_sealer(info, &pk).unwrap();
971
972 _ = format!("{sealer:?}"); let bad_setup_res = suite.setup_sealer(info, &HpkePublicKey(vec![]));
976 assert!(matches!(bad_setup_res.unwrap_err(), Error::Other(_)));
977
978 let aad = &[0xC0, 0xFF, 0xEE];
980 let pt = &[0xF0, 0x0D];
981 let ct = sealer.seal(aad, pt).unwrap();
982
983 let mut opener = suite
985 .setup_opener(&enc, info, &sk)
986 .unwrap();
987 _ = format!("{opener:?}"); let bad_key_res = suite.setup_opener(&enc, info, &HpkePrivateKey::from(vec![]));
991 assert!(matches!(bad_key_res.unwrap_err(), Error::Other(_)));
992
993 let pt_prime = opener.open(aad, &ct).unwrap();
995 assert_eq!(pt_prime, pt);
996
997 let open_res = opener.open(&[0x0], &ct);
999 assert!(matches!(open_res.unwrap_err(), Error::Other(_)));
1000
1001 let mut sk_rm_prime = sk.secret_bytes().to_vec();
1003 sk_rm_prime[10] ^= 0xFF; let mut opener_two = suite
1005 .setup_opener(&enc, info, &HpkePrivateKey::from(sk_rm_prime))
1006 .unwrap();
1007 let open_res = opener_two.open(aad, &ct);
1008 assert!(matches!(open_res.unwrap_err(), Error::Other(_)));
1009 }
1010 }
1011
1012 #[cfg(not(feature = "fips"))] #[test]
1014 fn test_fips() {
1015 let testcases: &[(&dyn Hpke, bool)] = &[
1016 (DH_KEM_P256_HKDF_SHA256_AES_128, true),
1018 (DH_KEM_P256_HKDF_SHA256_AES_256, true),
1019 (DH_KEM_P384_HKDF_SHA384_AES_128, true),
1020 (DH_KEM_P384_HKDF_SHA384_AES_256, true),
1021 (DH_KEM_P521_HKDF_SHA512_AES_128, true),
1022 (DH_KEM_P521_HKDF_SHA512_AES_256, true),
1023 (DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305, false),
1025 (DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305, false),
1026 (DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305, false),
1027 (DH_KEM_X25519_HKDF_SHA256_AES_128, false),
1029 (DH_KEM_X25519_HKDF_SHA256_AES_256, false),
1030 (DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305, false),
1031 ];
1032 for (suite, expected) in testcases {
1033 assert_eq!(suite.fips(), *expected);
1034 }
1035 }
1036
1037 #[test]
1038 fn failed_open_does_not_consume_sequence() {
1039 for suite in ALL_SUPPORTED_SUITES {
1040 let (pk, sk) = suite.generate_key_pair().unwrap();
1041 let (enc, mut sealer) = suite
1042 .setup_sealer(b"example", &pk)
1043 .unwrap();
1044 let ct0 = sealer
1045 .seal(b"aad", b"message 0")
1046 .unwrap();
1047 let ct1 = sealer
1048 .seal(b"aad", b"message 1")
1049 .unwrap();
1050
1051 let mut opener = suite
1052 .setup_opener(&enc, b"example", &sk)
1053 .unwrap();
1054 assert!(opener.open(b"wrong aad", &ct0).is_err());
1055 assert_eq!(opener.open(b"aad", &ct0).unwrap(), b"message 0");
1056 assert_eq!(opener.open(b"aad", &ct1).unwrap(), b"message 1");
1057 }
1058 }
1059}
1060
1061#[cfg(test)]
1062mod rfc_tests {
1063 use alloc::string::String;
1064 use std::fs::File;
1065 use std::println;
1066
1067 use serde::Deserialize;
1068
1069 use super::*;
1070
1071 #[test]
1075 fn check_test_vectors() {
1076 for (idx, vec) in test_vectors().into_iter().enumerate() {
1077 let Some(hpke) = vec.applicable() else {
1078 println!("skipping inapplicable vector {idx}");
1079 continue;
1080 };
1081
1082 println!("testing vector {idx}");
1083 let pk_r = HpkePublicKey(hex::decode(vec.pk_rm).unwrap());
1084 let sk_r = HpkePrivateKey::from(hex::decode(vec.sk_rm).unwrap());
1085 let sk_em = hex::decode(vec.sk_em).unwrap();
1086 let info = hex::decode(vec.info).unwrap();
1087 let expected_enc = hex::decode(vec.enc).unwrap();
1088
1089 let (enc, mut sealer) = hpke
1090 .setup_test_sealer(&info, &pk_r, &sk_em)
1091 .unwrap();
1092 assert_eq!(enc.0, expected_enc);
1093
1094 let mut opener = hpke
1095 .setup_opener(&enc, &info, &sk_r)
1096 .unwrap();
1097
1098 for test_encryption in vec.encryptions {
1099 let aad = hex::decode(test_encryption.aad).unwrap();
1100 let pt = hex::decode(test_encryption.pt).unwrap();
1101 let expected_ct = hex::decode(test_encryption.ct).unwrap();
1102
1103 let ciphertext = sealer.seal(&aad, &pt).unwrap();
1104 assert_eq!(ciphertext, expected_ct);
1105
1106 let plaintext = opener.open(&aad, &ciphertext).unwrap();
1107 assert_eq!(plaintext, pt);
1108 }
1109 }
1110 }
1111
1112 trait TestHpke: Hpke {
1113 fn setup_test_sealer(
1114 &self,
1115 info: &[u8],
1116 pub_key: &HpkePublicKey,
1117 sk_em: &[u8],
1118 ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error>;
1119 }
1120
1121 impl<const KEY_SIZE: usize, const KDF_SIZE: usize> TestHpke for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
1122 fn setup_test_sealer(
1123 &self,
1124 info: &[u8],
1125 pub_key: &HpkePublicKey,
1126 sk_em: &[u8],
1127 ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error> {
1128 let (encap, sealer) = Sealer::test_only_new(self, info, pub_key, sk_em)?;
1129 Ok((encap, Box::new(sealer)))
1130 }
1131 }
1132
1133 static TEST_SUITES: &[&dyn TestHpke] = &[
1134 DH_KEM_P256_HKDF_SHA256_AES_128,
1135 DH_KEM_P256_HKDF_SHA256_AES_256,
1136 #[cfg(not(feature = "fips"))]
1137 DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305,
1138 DH_KEM_P384_HKDF_SHA384_AES_128,
1139 DH_KEM_P384_HKDF_SHA384_AES_256,
1140 #[cfg(not(feature = "fips"))]
1141 DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305,
1142 DH_KEM_P521_HKDF_SHA512_AES_128,
1143 DH_KEM_P521_HKDF_SHA512_AES_256,
1144 #[cfg(not(feature = "fips"))]
1145 DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305,
1146 #[cfg(not(feature = "fips"))]
1147 DH_KEM_X25519_HKDF_SHA256_AES_128,
1148 #[cfg(not(feature = "fips"))]
1149 DH_KEM_X25519_HKDF_SHA256_AES_256,
1150 #[cfg(not(feature = "fips"))]
1151 DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305,
1152 ];
1153
1154 #[derive(Deserialize, Debug)]
1155 struct TestVector {
1156 mode: u8,
1157 kem_id: u16,
1158 kdf_id: u16,
1159 aead_id: u16,
1160 info: String,
1161 #[serde(rename(deserialize = "pkRm"))]
1162 pk_rm: String,
1163 #[serde(rename(deserialize = "skRm"))]
1164 sk_rm: String,
1165 #[serde(rename(deserialize = "skEm"))]
1166 sk_em: String,
1167 enc: String,
1168 encryptions: Vec<TestEncryption>,
1169 }
1170
1171 #[derive(Deserialize, Debug)]
1172 struct TestEncryption {
1173 aad: String,
1174 pt: String,
1175 ct: String,
1176 }
1177
1178 impl TestVector {
1179 fn suite(&self) -> HpkeSuite {
1180 HpkeSuite {
1181 kem: HpkeKem::from(self.kem_id),
1182 sym: HpkeSymmetricCipherSuite {
1183 kdf_id: HpkeKdf::from(self.kdf_id),
1184 aead_id: HpkeAead::from(self.aead_id),
1185 },
1186 }
1187 }
1188
1189 fn applicable(&self) -> Option<&'static dyn TestHpke> {
1190 if self.mode != 0 {
1192 return None;
1193 }
1194
1195 Self::lookup_suite(self.suite(), TEST_SUITES)
1196 }
1197
1198 fn lookup_suite(
1199 suite: HpkeSuite,
1200 supported: &[&'static dyn TestHpke],
1201 ) -> Option<&'static dyn TestHpke> {
1202 supported
1203 .iter()
1204 .find(|s| s.suite() == suite)
1205 .copied()
1206 }
1207 }
1208
1209 fn test_vectors() -> Vec<TestVector> {
1210 serde_json::from_reader(
1211 &mut File::open("../rustls-provider-test/tests/rfc-9180-test-vectors.json")
1212 .expect("failed to open test vectors data file"),
1213 )
1214 .expect("failed to deserialize test vectors")
1215 }
1216}