1use alloc::boxed::Box;
2use alloc::vec::Vec;
3use core::fmt::{self, Debug, Formatter};
4
5use aws_lc_rs::aead::{
6 self, Aad, BoundKey, NONCE_LEN, Nonce, NonceSequence, OpeningKey, SealingKey, UnboundKey,
7};
8use aws_lc_rs::agreement;
9use aws_lc_rs::cipher::{AES_128_KEY_LEN, AES_256_KEY_LEN};
10use aws_lc_rs::digest::{SHA256_OUTPUT_LEN, SHA384_OUTPUT_LEN, SHA512_OUTPUT_LEN};
11use aws_lc_rs::encoding::{AsBigEndian, Curve25519SeedBin, EcPrivateKeyBin};
12use zeroize::Zeroize;
13
14use crate::crypto::aws_lc_rs::hmac::{HMAC_SHA256, HMAC_SHA384, HMAC_SHA512};
15use crate::crypto::aws_lc_rs::unspecified_err;
16use crate::crypto::hpke::{
17 EncapsulatedSecret, Hpke, HpkeAead, HpkeKdf, HpkeKem, HpkeOpener, HpkePrivateKey,
18 HpkePublicKey, HpkeSealer, HpkeSuite, HpkeSymmetricCipherSuite,
19};
20use crate::crypto::tls13::{HkdfExpander, HkdfPrkExtract, HkdfUsingHmac, expand};
21use crate::error::{Error, OtherError};
22
23pub static ALL_SUPPORTED_SUITES: &[&dyn Hpke] = &[
25 DH_KEM_P256_HKDF_SHA256_AES_128,
26 DH_KEM_P256_HKDF_SHA256_AES_256,
27 #[cfg(not(feature = "fips"))]
28 DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305,
29 DH_KEM_P384_HKDF_SHA384_AES_128,
30 DH_KEM_P384_HKDF_SHA384_AES_256,
31 #[cfg(not(feature = "fips"))]
32 DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305,
33 DH_KEM_P521_HKDF_SHA512_AES_128,
34 DH_KEM_P521_HKDF_SHA512_AES_256,
35 #[cfg(not(feature = "fips"))]
36 DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305,
37 #[cfg(not(feature = "fips"))]
38 DH_KEM_X25519_HKDF_SHA256_AES_128,
39 #[cfg(not(feature = "fips"))]
40 DH_KEM_X25519_HKDF_SHA256_AES_256,
41 #[cfg(not(feature = "fips"))]
42 DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305,
43];
44
45pub static DH_KEM_P256_HKDF_SHA256_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA256_OUTPUT_LEN> =
48 &HpkeAwsLcRs {
49 suite: HpkeSuite {
50 kem: HpkeKem::DHKEM_P256_HKDF_SHA256,
51 sym: HpkeSymmetricCipherSuite {
52 kdf_id: HpkeKdf::HKDF_SHA256,
53 aead_id: HpkeAead::AES_128_GCM,
54 },
55 },
56 dh_kem: DH_KEM_P256_HKDF_SHA256,
57 hkdf: RING_HKDF_HMAC_SHA256,
58 aead: &aead::AES_128_GCM,
59 };
60
61pub static DH_KEM_P256_HKDF_SHA256_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA256_OUTPUT_LEN> =
64 &HpkeAwsLcRs {
65 suite: HpkeSuite {
66 kem: HpkeKem::DHKEM_P256_HKDF_SHA256,
67 sym: HpkeSymmetricCipherSuite {
68 kdf_id: HpkeKdf::HKDF_SHA256,
69 aead_id: HpkeAead::AES_256_GCM,
70 },
71 },
72 dh_kem: DH_KEM_P256_HKDF_SHA256,
73 hkdf: RING_HKDF_HMAC_SHA256,
74 aead: &aead::AES_256_GCM,
75 };
76
77pub static DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305: &HpkeAwsLcRs<
80 CHACHA_KEY_LEN,
81 SHA256_OUTPUT_LEN,
82> = &HpkeAwsLcRs {
83 suite: HpkeSuite {
84 kem: HpkeKem::DHKEM_P256_HKDF_SHA256,
85 sym: HpkeSymmetricCipherSuite {
86 kdf_id: HpkeKdf::HKDF_SHA256,
87 aead_id: HpkeAead::CHACHA20_POLY_1305,
88 },
89 },
90 dh_kem: DH_KEM_P256_HKDF_SHA256,
91 hkdf: RING_HKDF_HMAC_SHA256,
92 aead: &aead::CHACHA20_POLY1305,
93};
94
95pub static DH_KEM_P384_HKDF_SHA384_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA384_OUTPUT_LEN> =
98 &HpkeAwsLcRs {
99 suite: HpkeSuite {
100 kem: HpkeKem::DHKEM_P384_HKDF_SHA384,
101 sym: HpkeSymmetricCipherSuite {
102 kdf_id: HpkeKdf::HKDF_SHA384,
103 aead_id: HpkeAead::AES_128_GCM,
104 },
105 },
106 dh_kem: DH_KEM_P384_HKDF_SHA384,
107 hkdf: RING_HKDF_HMAC_SHA384,
108 aead: &aead::AES_128_GCM,
109 };
110
111pub static DH_KEM_P384_HKDF_SHA384_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA384_OUTPUT_LEN> =
114 &HpkeAwsLcRs {
115 suite: HpkeSuite {
116 kem: HpkeKem::DHKEM_P384_HKDF_SHA384,
117 sym: HpkeSymmetricCipherSuite {
118 kdf_id: HpkeKdf::HKDF_SHA384,
119 aead_id: HpkeAead::AES_256_GCM,
120 },
121 },
122 dh_kem: DH_KEM_P384_HKDF_SHA384,
123 hkdf: RING_HKDF_HMAC_SHA384,
124 aead: &aead::AES_256_GCM,
125 };
126
127pub static DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305: &HpkeAwsLcRs<
130 CHACHA_KEY_LEN,
131 SHA384_OUTPUT_LEN,
132> = &HpkeAwsLcRs {
133 suite: HpkeSuite {
134 kem: HpkeKem::DHKEM_P384_HKDF_SHA384,
135 sym: HpkeSymmetricCipherSuite {
136 kdf_id: HpkeKdf::HKDF_SHA384,
137 aead_id: HpkeAead::CHACHA20_POLY_1305,
138 },
139 },
140 dh_kem: DH_KEM_P384_HKDF_SHA384,
141 hkdf: RING_HKDF_HMAC_SHA384,
142 aead: &aead::CHACHA20_POLY1305,
143};
144
145pub static DH_KEM_P521_HKDF_SHA512_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA512_OUTPUT_LEN> =
148 &HpkeAwsLcRs {
149 suite: HpkeSuite {
150 kem: HpkeKem::DHKEM_P521_HKDF_SHA512,
151 sym: HpkeSymmetricCipherSuite {
152 kdf_id: HpkeKdf::HKDF_SHA512,
153 aead_id: HpkeAead::AES_128_GCM,
154 },
155 },
156 dh_kem: DH_KEM_P521_HKDF_SHA512,
157 hkdf: RING_HKDF_HMAC_SHA512,
158 aead: &aead::AES_128_GCM,
159 };
160
161pub static DH_KEM_P521_HKDF_SHA512_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA512_OUTPUT_LEN> =
164 &HpkeAwsLcRs {
165 suite: HpkeSuite {
166 kem: HpkeKem::DHKEM_P521_HKDF_SHA512,
167 sym: HpkeSymmetricCipherSuite {
168 kdf_id: HpkeKdf::HKDF_SHA512,
169 aead_id: HpkeAead::AES_256_GCM,
170 },
171 },
172 dh_kem: DH_KEM_P521_HKDF_SHA512,
173 hkdf: RING_HKDF_HMAC_SHA512,
174 aead: &aead::AES_256_GCM,
175 };
176
177pub static DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305: &HpkeAwsLcRs<
180 CHACHA_KEY_LEN,
181 SHA512_OUTPUT_LEN,
182> = &HpkeAwsLcRs {
183 suite: HpkeSuite {
184 kem: HpkeKem::DHKEM_P521_HKDF_SHA512,
185 sym: HpkeSymmetricCipherSuite {
186 kdf_id: HpkeKdf::HKDF_SHA512,
187 aead_id: HpkeAead::CHACHA20_POLY_1305,
188 },
189 },
190 dh_kem: DH_KEM_P521_HKDF_SHA512,
191 hkdf: RING_HKDF_HMAC_SHA512,
192 aead: &aead::CHACHA20_POLY1305,
193};
194
195pub static DH_KEM_X25519_HKDF_SHA256_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA256_OUTPUT_LEN> =
198 &HpkeAwsLcRs {
199 suite: HpkeSuite {
200 kem: HpkeKem::DHKEM_X25519_HKDF_SHA256,
201 sym: HpkeSymmetricCipherSuite {
202 kdf_id: HpkeKdf::HKDF_SHA256,
203 aead_id: HpkeAead::AES_128_GCM,
204 },
205 },
206 dh_kem: DH_KEM_X25519_HKDF_SHA256,
207 hkdf: RING_HKDF_HMAC_SHA256,
208 aead: &aead::AES_128_GCM,
209 };
210
211pub static DH_KEM_X25519_HKDF_SHA256_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA256_OUTPUT_LEN> =
214 &HpkeAwsLcRs {
215 suite: HpkeSuite {
216 kem: HpkeKem::DHKEM_X25519_HKDF_SHA256,
217 sym: HpkeSymmetricCipherSuite {
218 kdf_id: HpkeKdf::HKDF_SHA256,
219 aead_id: HpkeAead::AES_256_GCM,
220 },
221 },
222 dh_kem: DH_KEM_X25519_HKDF_SHA256,
223 hkdf: RING_HKDF_HMAC_SHA256,
224 aead: &aead::AES_256_GCM,
225 };
226
227pub static DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305: &HpkeAwsLcRs<
230 CHACHA_KEY_LEN,
231 SHA256_OUTPUT_LEN,
232> = &HpkeAwsLcRs {
233 suite: HpkeSuite {
234 kem: HpkeKem::DHKEM_X25519_HKDF_SHA256,
235 sym: HpkeSymmetricCipherSuite {
236 kdf_id: HpkeKdf::HKDF_SHA256,
237 aead_id: HpkeAead::CHACHA20_POLY_1305,
238 },
239 },
240 dh_kem: DH_KEM_X25519_HKDF_SHA256,
241 hkdf: RING_HKDF_HMAC_SHA256,
242 aead: &aead::CHACHA20_POLY1305,
243};
244
245pub struct HpkeAwsLcRs<const KEY_SIZE: usize, const KDF_SIZE: usize> {
247 suite: HpkeSuite,
248 dh_kem: &'static DhKem<KDF_SIZE>,
249 hkdf: &'static dyn HkdfPrkExtract,
250 aead: &'static aead::Algorithm,
251}
252
253impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
254 fn key_schedule(
258 &self,
259 shared_secret: KemSharedSecret<KDF_SIZE>,
260 info: &[u8],
261 ) -> Result<KeySchedule<KEY_SIZE>, Error> {
262 let suite_id = LabeledSuiteId::Hpke(self.suite);
266 let psk_id_hash = labeled_extract_for_prk(self.hkdf, suite_id, None, Label::PskIdHash, &[]);
267 let info_hash = labeled_extract_for_prk(self.hkdf, suite_id, None, Label::InfoHash, info);
268 let key_schedule_context = [
269 &[0][..], &psk_id_hash,
271 &info_hash,
272 ]
273 .concat();
274
275 let key = AeadKey(self.key_schedule_labeled_expand::<KEY_SIZE>(
276 &shared_secret,
277 &key_schedule_context,
278 Label::Key,
279 ));
280
281 let base_nonce = self.key_schedule_labeled_expand::<NONCE_LEN>(
282 &shared_secret,
283 &key_schedule_context,
284 Label::BaseNonce,
285 );
286
287 Ok(KeySchedule {
288 aead: self.aead,
289 key,
290 base_nonce,
291 seq_num: 0,
292 })
293 }
294
295 fn key_schedule_labeled_expand<const L: usize>(
296 &self,
297 shared_secret: &KemSharedSecret<KDF_SIZE>,
298 key_schedule_context: &[u8],
299 label: Label,
300 ) -> [u8; L] {
301 let suite_id = LabeledSuiteId::Hpke(self.suite);
302 labeled_expand::<L>(
303 suite_id,
304 labeled_extract_for_expand(
305 self.hkdf,
306 suite_id,
307 Some(&shared_secret.0),
308 Label::Secret,
309 &[],
310 ),
311 label,
312 key_schedule_context,
313 )
314 }
315}
316
317impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Hpke for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
318 fn seal(
319 &self,
320 info: &[u8],
321 aad: &[u8],
322 plaintext: &[u8],
323 pub_key: &HpkePublicKey,
324 ) -> Result<(EncapsulatedSecret, Vec<u8>), Error> {
325 let (encap, mut sealer) = self.setup_sealer(info, pub_key)?;
326 Ok((encap, sealer.seal(aad, plaintext)?))
327 }
328
329 fn setup_sealer(
330 &self,
331 info: &[u8],
332 pub_key: &HpkePublicKey,
333 ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error> {
334 let (encap, sealer) = Sealer::new(self, info, pub_key)?;
335 Ok((encap, Box::new(sealer)))
336 }
337
338 fn open(
339 &self,
340 enc: &EncapsulatedSecret,
341 info: &[u8],
342 aad: &[u8],
343 ciphertext: &[u8],
344 secret_key: &HpkePrivateKey,
345 ) -> Result<Vec<u8>, Error> {
346 self.setup_opener(enc, info, secret_key)?
347 .open(aad, ciphertext)
348 }
349
350 fn setup_opener(
351 &self,
352 enc: &EncapsulatedSecret,
353 info: &[u8],
354 secret_key: &HpkePrivateKey,
355 ) -> Result<Box<dyn HpkeOpener + 'static>, Error> {
356 Ok(Box::new(Opener::new(self, enc, info, secret_key)?))
357 }
358
359 fn fips(&self) -> bool {
360 matches!(
361 (self.suite.kem, self.suite.sym.aead_id),
365 (
366 HpkeKem::DHKEM_P256_HKDF_SHA256
368 | HpkeKem::DHKEM_P384_HKDF_SHA384
369 | HpkeKem::DHKEM_P521_HKDF_SHA512,
370 HpkeAead::AES_128_GCM | HpkeAead::AES_256_GCM,
372 )
373 )
374 }
375
376 fn generate_key_pair(&self) -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
377 (self.dh_kem.key_generator)()
378 }
379
380 fn suite(&self) -> HpkeSuite {
381 self.suite
382 }
383}
384
385impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
386 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
387 self.suite.fmt(f)
388 }
389}
390
391struct Sealer<const KEY_SIZE: usize, const KDF_SIZE: usize> {
393 key_schedule: KeySchedule<KEY_SIZE>,
394}
395
396impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Sealer<KEY_SIZE, KDF_SIZE> {
397 fn new(
401 suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>,
402 info: &[u8],
403 pub_key: &HpkePublicKey,
404 ) -> Result<(EncapsulatedSecret, Self), Error> {
405 let (shared_secret, enc) = suite.dh_kem.encap(pub_key)?;
411 let key_schedule = suite.key_schedule(shared_secret, info)?;
412 Ok((enc, Self { key_schedule }))
413 }
414
415 #[cfg(test)]
418 fn test_only_new(
419 suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>,
420 info: &[u8],
421 pub_key: &HpkePublicKey,
422 sk_e: &[u8],
423 ) -> Result<(EncapsulatedSecret, Self), Error> {
424 let (shared_secret, enc) = suite
425 .dh_kem
426 .test_only_encap(pub_key, sk_e)?;
427 let key_schedule = suite.key_schedule(shared_secret, info)?;
428 Ok((enc, Self { key_schedule }))
429 }
430}
431
432impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeSealer for Sealer<KEY_SIZE, KDF_SIZE> {
433 fn seal(&mut self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
434 let key = UnboundKey::new(self.key_schedule.aead, &self.key_schedule.key.0)
440 .map_err(unspecified_err)?;
441 let mut sealing_key = SealingKey::new(key, &mut self.key_schedule);
442
443 let mut in_out_buffer = Vec::from(plaintext);
444 sealing_key
445 .seal_in_place_append_tag(Aad::from(aad), &mut in_out_buffer)
446 .map_err(unspecified_err)?;
447
448 Ok(in_out_buffer)
449 }
450}
451
452impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for Sealer<KEY_SIZE, KDF_SIZE> {
453 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
454 f.debug_struct("Sealer").finish()
455 }
456}
457
458struct Opener<const KEY_SIZE: usize, const KDF_SIZE: usize> {
460 key_schedule: KeySchedule<KEY_SIZE>,
461}
462
463impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Opener<KEY_SIZE, KDF_SIZE> {
464 fn new(
468 suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>,
469 enc: &EncapsulatedSecret,
470 info: &[u8],
471 secret_key: &HpkePrivateKey,
472 ) -> Result<Self, Error> {
473 Ok(Self {
478 key_schedule: suite.key_schedule(suite.dh_kem.decap(enc, secret_key)?, info)?,
479 })
480 }
481}
482
483impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeOpener for Opener<KEY_SIZE, KDF_SIZE> {
484 fn open(&mut self, aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
485 let key = UnboundKey::new(self.key_schedule.aead, &self.key_schedule.key.0)
493 .map_err(unspecified_err)?;
494 let mut opening_key = OpeningKey::new(key, &mut self.key_schedule);
495
496 let mut in_out_buffer = Vec::from(ciphertext);
497 let plaintext = opening_key
498 .open_in_place(Aad::from(aad), &mut in_out_buffer)
499 .map_err(unspecified_err)?;
500
501 Ok(plaintext.to_vec())
502 }
503}
504
505impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for Opener<KEY_SIZE, KDF_SIZE> {
506 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
507 f.debug_struct("Opener").finish()
508 }
509}
510
511struct DhKem<const KDF_SIZE: usize> {
517 id: HpkeKem,
518 agreement_algorithm: &'static agreement::Algorithm,
519 key_generator:
520 &'static (dyn Fn() -> Result<(HpkePublicKey, HpkePrivateKey), Error> + Send + Sync),
521 hkdf: &'static dyn HkdfPrkExtract,
522}
523
524impl<const KDF_SIZE: usize> DhKem<KDF_SIZE> {
525 fn encap(
529 &self,
530 recipient: &HpkePublicKey,
531 ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
532 let sk_e =
536 agreement::PrivateKey::generate(self.agreement_algorithm).map_err(unspecified_err)?;
537 self.encap_impl(recipient, sk_e)
538 }
539
540 #[cfg(test)]
543 fn test_only_encap(
544 &self,
545 recipient: &HpkePublicKey,
546 test_only_ske: &[u8],
547 ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
548 let sk_e = agreement::PrivateKey::from_private_key(self.agreement_algorithm, test_only_ske)
550 .map_err(key_rejected_err)?;
551 self.encap_impl(recipient, sk_e)
552 }
553
554 fn encap_impl(
555 &self,
556 recipient: &HpkePublicKey,
557 sk_e: agreement::PrivateKey,
558 ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
559 let enc = sk_e
571 .compute_public_key()
572 .map_err(unspecified_err)?;
573 let pk_r = agreement::UnparsedPublicKey::new(self.agreement_algorithm, &recipient.0);
574 let kem_context = [enc.as_ref(), pk_r.bytes()].concat();
575
576 let shared_secret = agreement::agree(&sk_e, pk_r, aws_lc_rs::error::Unspecified, |dh| {
577 Ok(self.extract_and_expand(dh, &kem_context))
578 })
579 .map_err(unspecified_err)?;
580
581 Ok((
582 KemSharedSecret(shared_secret),
583 EncapsulatedSecret(enc.as_ref().into()),
584 ))
585 }
586
587 fn decap(
591 &self,
592 enc: &EncapsulatedSecret,
593 recipient: &HpkePrivateKey,
594 ) -> Result<KemSharedSecret<KDF_SIZE>, Error> {
595 let pk_e = agreement::UnparsedPublicKey::new(self.agreement_algorithm, &enc.0);
606 let sk_r = agreement::PrivateKey::from_private_key(
607 self.agreement_algorithm,
608 recipient.secret_bytes(),
609 )
610 .map_err(key_rejected_err)?;
611 let pk_rm = sk_r
612 .compute_public_key()
613 .map_err(unspecified_err)?;
614 let kem_context = [&enc.0, pk_rm.as_ref()].concat();
615
616 let shared_secret = agreement::agree(&sk_r, pk_e, aws_lc_rs::error::Unspecified, |dh| {
617 Ok(self.extract_and_expand(dh, &kem_context))
618 })
619 .map_err(unspecified_err)?;
620
621 Ok(KemSharedSecret(shared_secret))
622 }
623
624 fn extract_and_expand(&self, dh: &[u8], kem_context: &[u8]) -> [u8; KDF_SIZE] {
628 let suite_id = LabeledSuiteId::Kem(self.id);
635 labeled_expand(
636 suite_id,
637 labeled_extract_for_expand(self.hkdf, suite_id, None, Label::EaePrk, dh),
638 Label::SharedSecret,
639 kem_context,
640 )
641 }
642}
643
644static DH_KEM_P256_HKDF_SHA256: &DhKem<SHA256_OUTPUT_LEN> = &DhKem {
645 id: HpkeKem::DHKEM_P256_HKDF_SHA256,
646 agreement_algorithm: &agreement::ECDH_P256,
647 key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P256),
648 hkdf: RING_HKDF_HMAC_SHA256,
649};
650
651static DH_KEM_P384_HKDF_SHA384: &DhKem<SHA384_OUTPUT_LEN> = &DhKem {
652 id: HpkeKem::DHKEM_P384_HKDF_SHA384,
653 agreement_algorithm: &agreement::ECDH_P384,
654 key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P384),
655 hkdf: RING_HKDF_HMAC_SHA384,
656};
657
658static DH_KEM_P521_HKDF_SHA512: &DhKem<SHA512_OUTPUT_LEN> = &DhKem {
659 id: HpkeKem::DHKEM_P521_HKDF_SHA512,
660 agreement_algorithm: &agreement::ECDH_P521,
661 key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P521),
662 hkdf: RING_HKDF_HMAC_SHA512,
663};
664
665static DH_KEM_X25519_HKDF_SHA256: &DhKem<SHA256_OUTPUT_LEN> = &DhKem {
666 id: HpkeKem::DHKEM_X25519_HKDF_SHA256,
667 agreement_algorithm: &agreement::X25519,
668 key_generator: &generate_x25519_key_pair,
669 hkdf: RING_HKDF_HMAC_SHA256,
670};
671
672fn generate_p_curve_key_pair(
679 alg: &'static agreement::Algorithm,
680) -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
681 debug_assert_ne!(alg, &agreement::X25519);
685 let (public_key, private_key) = generate_key_pair(alg)?;
686 let raw_private_key: EcPrivateKeyBin<'_> = private_key
687 .as_be_bytes()
688 .map_err(unspecified_err)?;
689 Ok((
690 public_key,
691 HpkePrivateKey::from(raw_private_key.as_ref().to_vec()),
692 ))
693}
694
695fn generate_x25519_key_pair() -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
702 let (public_key, private_key) = generate_key_pair(&agreement::X25519)?;
703 let raw_private_key: Curve25519SeedBin<'_> = private_key
704 .as_be_bytes()
705 .map_err(unspecified_err)?;
706 Ok((
707 public_key,
708 HpkePrivateKey::from(raw_private_key.as_ref().to_vec()),
709 ))
710}
711
712fn generate_key_pair(
713 alg: &'static agreement::Algorithm,
714) -> Result<(HpkePublicKey, agreement::PrivateKey), Error> {
715 let private_key = agreement::PrivateKey::generate(alg).map_err(unspecified_err)?;
716 let public_key = HpkePublicKey(
717 private_key
718 .compute_public_key()
719 .map_err(unspecified_err)?
720 .as_ref()
721 .to_vec(),
722 );
723 Ok((public_key, private_key))
724}
725
726struct KeySchedule<const KEY_SIZE: usize> {
729 aead: &'static aead::Algorithm,
730 key: AeadKey<KEY_SIZE>,
731 base_nonce: [u8; NONCE_LEN],
732 seq_num: u32,
733}
734
735impl<const KEY_SIZE: usize> KeySchedule<KEY_SIZE> {
736 fn compute_nonce(&self) -> [u8; NONCE_LEN] {
740 let mut nonce = [0; NONCE_LEN];
748 let seq_bytes = self.seq_num.to_be_bytes();
749 nonce[NONCE_LEN - seq_bytes.len()..].copy_from_slice(&seq_bytes);
750
751 for (n, &b) in nonce.iter_mut().zip(&self.base_nonce) {
752 *n ^= b;
753 }
754
755 nonce
756 }
757
758 fn increment_seq_num(&mut self) -> Result<(), aws_lc_rs::error::Unspecified> {
762 let max_seq_num = (1u128 << (NONCE_LEN * 8)) - 1;
770
771 if u128::from(self.seq_num) >= max_seq_num {
774 return Err(aws_lc_rs::error::Unspecified);
775 }
776
777 self.seq_num += 1;
778 Ok(())
779 }
780}
781
782impl<const KEY_SIZE: usize> NonceSequence for &mut KeySchedule<KEY_SIZE> {
783 fn advance(&mut self) -> Result<Nonce, aws_lc_rs::error::Unspecified> {
784 let nonce = self.compute_nonce();
785 self.increment_seq_num()?;
786 Nonce::try_assume_unique_for_key(&nonce)
787 }
788}
789
790fn labeled_extract_for_expand(
794 hkdf: &'static dyn HkdfPrkExtract,
795 suite_id: LabeledSuiteId,
796 salt: Option<&[u8]>,
797 label: Label,
798 ikm: &[u8],
799) -> Box<dyn HkdfExpander> {
800 let labeled_ikm = [&b"HPKE-v1"[..], &suite_id.encoded(), label.as_ref(), ikm].concat();
805 hkdf.extract_from_secret(salt, &labeled_ikm)
806}
807
808fn labeled_extract_for_prk(
812 hkdf: &'static dyn HkdfPrkExtract,
813 suite_id: LabeledSuiteId,
814 salt: Option<&[u8]>,
815 label: Label,
816 ikm: &[u8],
817) -> Vec<u8> {
818 let labeled_ikm = [&b"HPKE-v1"[..], &suite_id.encoded(), label.as_ref(), ikm].concat();
823 hkdf.extract_prk_from_secret(salt, &labeled_ikm)
824}
825
826fn labeled_expand<const L: usize>(
830 suite_id: LabeledSuiteId,
831 expander: Box<dyn HkdfExpander>,
832 label: Label,
833 kem_context: &[u8],
834) -> [u8; L] {
835 let output_len = u16::to_be_bytes(L as u16);
841 let info = &[
842 &output_len[..],
843 b"HPKE-v1",
844 &suite_id.encoded(),
845 label.as_ref(),
846 kem_context,
847 ];
848
849 expand(&*expander, info)
850}
851
852#[derive(Debug)]
854enum Label {
855 PskIdHash,
856 InfoHash,
857 Secret,
858 Key,
859 BaseNonce,
860 EaePrk,
861 SharedSecret,
862}
863
864impl AsRef<[u8]> for Label {
865 fn as_ref(&self) -> &[u8] {
866 match self {
867 Self::PskIdHash => b"psk_id_hash",
868 Self::InfoHash => b"info_hash",
869 Self::Secret => b"secret",
870 Self::Key => b"key",
871 Self::BaseNonce => b"base_nonce",
872 Self::EaePrk => b"eae_prk",
873 Self::SharedSecret => b"shared_secret",
874 }
875 }
876}
877
878#[derive(Debug, Copy, Clone)]
881enum LabeledSuiteId {
882 Hpke(HpkeSuite),
883 Kem(HpkeKem),
884}
885
886impl LabeledSuiteId {
887 fn encoded(&self) -> Vec<u8> {
894 match self {
895 Self::Hpke(suite) => [
896 &b"HPKE"[..],
897 &u16::from(suite.kem).to_be_bytes(),
898 &u16::from(suite.sym.kdf_id).to_be_bytes(),
899 &u16::from(suite.sym.aead_id).to_be_bytes(),
900 ]
901 .concat(),
902 Self::Kem(kem) => [&b"KEM"[..], &u16::from(*kem).to_be_bytes()].concat(),
903 }
904 }
905}
906
907struct AeadKey<const KEY_LEN: usize>([u8; KEY_LEN]);
909
910impl<const KEY_LEN: usize> Drop for AeadKey<KEY_LEN> {
911 fn drop(&mut self) {
912 self.0.zeroize()
913 }
914}
915
916struct KemSharedSecret<const KDF_LEN: usize>([u8; KDF_LEN]);
918
919impl<const KDF_LEN: usize> Drop for KemSharedSecret<KDF_LEN> {
920 fn drop(&mut self) {
921 self.0.zeroize();
922 }
923}
924
925fn key_rejected_err(e: aws_lc_rs::error::KeyRejected) -> Error {
926 Error::Other(OtherError::new(e))
927}
928
929const CHACHA_KEY_LEN: usize = 32;
932
933static RING_HKDF_HMAC_SHA256: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA256);
934static RING_HKDF_HMAC_SHA384: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA384);
935static RING_HKDF_HMAC_SHA512: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA512);
936
937#[cfg(test)]
938mod tests {
939 use alloc::{format, vec};
940
941 use super::*;
942
943 #[test]
944 fn smoke_test() {
945 for suite in ALL_SUPPORTED_SUITES {
946 _ = format!("{suite:?}"); let (pk, sk) = suite.generate_key_pair().unwrap();
950
951 let info = &[
953 0x4f, 0x64, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x47, 0x72, 0x65, 0x63, 0x69,
954 0x61, 0x6e, 0x20, 0x55, 0x72, 0x6e,
955 ][..];
956
957 let (enc, mut sealer) = suite.setup_sealer(info, &pk).unwrap();
959
960 _ = format!("{sealer:?}"); let bad_setup_res = suite.setup_sealer(info, &HpkePublicKey(vec![]));
964 assert!(matches!(bad_setup_res.unwrap_err(), Error::Other(_)));
965
966 let aad = &[0xC0, 0xFF, 0xEE];
968 let pt = &[0xF0, 0x0D];
969 let ct = sealer.seal(aad, pt).unwrap();
970
971 let mut opener = suite
973 .setup_opener(&enc, info, &sk)
974 .unwrap();
975 _ = format!("{opener:?}"); let bad_key_res = suite.setup_opener(&enc, info, &HpkePrivateKey::from(vec![]));
979 assert!(matches!(bad_key_res.unwrap_err(), Error::Other(_)));
980
981 let pt_prime = opener.open(aad, &ct).unwrap();
983 assert_eq!(pt_prime, pt);
984
985 let open_res = opener.open(&[0x0], &ct);
987 assert!(matches!(open_res.unwrap_err(), Error::Other(_)));
988
989 let mut sk_rm_prime = sk.secret_bytes().to_vec();
991 sk_rm_prime[10] ^= 0xFF; let mut opener_two = suite
993 .setup_opener(&enc, info, &HpkePrivateKey::from(sk_rm_prime))
994 .unwrap();
995 let open_res = opener_two.open(aad, &ct);
996 assert!(matches!(open_res.unwrap_err(), Error::Other(_)));
997 }
998 }
999
1000 #[cfg(not(feature = "fips"))] #[test]
1002 fn test_fips() {
1003 let testcases: &[(&dyn Hpke, bool)] = &[
1004 (DH_KEM_P256_HKDF_SHA256_AES_128, true),
1006 (DH_KEM_P256_HKDF_SHA256_AES_256, true),
1007 (DH_KEM_P384_HKDF_SHA384_AES_128, true),
1008 (DH_KEM_P384_HKDF_SHA384_AES_256, true),
1009 (DH_KEM_P521_HKDF_SHA512_AES_128, true),
1010 (DH_KEM_P521_HKDF_SHA512_AES_256, true),
1011 (DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305, false),
1013 (DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305, false),
1014 (DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305, false),
1015 (DH_KEM_X25519_HKDF_SHA256_AES_128, false),
1017 (DH_KEM_X25519_HKDF_SHA256_AES_256, false),
1018 (DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305, false),
1019 ];
1020 for (suite, expected) in testcases {
1021 assert_eq!(suite.fips(), *expected);
1022 }
1023 }
1024}
1025
1026#[cfg(test)]
1027mod rfc_tests {
1028 use alloc::string::String;
1029 use std::fs::File;
1030 use std::println;
1031
1032 use serde::Deserialize;
1033
1034 use super::*;
1035
1036 #[test]
1040 fn check_test_vectors() {
1041 for (idx, vec) in test_vectors().into_iter().enumerate() {
1042 let Some(hpke) = vec.applicable() else {
1043 println!("skipping inapplicable vector {idx}");
1044 continue;
1045 };
1046
1047 println!("testing vector {idx}");
1048 let pk_r = HpkePublicKey(hex::decode(vec.pk_rm).unwrap());
1049 let sk_r = HpkePrivateKey::from(hex::decode(vec.sk_rm).unwrap());
1050 let sk_em = hex::decode(vec.sk_em).unwrap();
1051 let info = hex::decode(vec.info).unwrap();
1052 let expected_enc = hex::decode(vec.enc).unwrap();
1053
1054 let (enc, mut sealer) = hpke
1055 .setup_test_sealer(&info, &pk_r, &sk_em)
1056 .unwrap();
1057 assert_eq!(enc.0, expected_enc);
1058
1059 let mut opener = hpke
1060 .setup_opener(&enc, &info, &sk_r)
1061 .unwrap();
1062
1063 for test_encryption in vec.encryptions {
1064 let aad = hex::decode(test_encryption.aad).unwrap();
1065 let pt = hex::decode(test_encryption.pt).unwrap();
1066 let expected_ct = hex::decode(test_encryption.ct).unwrap();
1067
1068 let ciphertext = sealer.seal(&aad, &pt).unwrap();
1069 assert_eq!(ciphertext, expected_ct);
1070
1071 let plaintext = opener.open(&aad, &ciphertext).unwrap();
1072 assert_eq!(plaintext, pt);
1073 }
1074 }
1075 }
1076
1077 trait TestHpke: Hpke {
1078 fn setup_test_sealer(
1079 &self,
1080 info: &[u8],
1081 pub_key: &HpkePublicKey,
1082 sk_em: &[u8],
1083 ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error>;
1084 }
1085
1086 impl<const KEY_SIZE: usize, const KDF_SIZE: usize> TestHpke for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
1087 fn setup_test_sealer(
1088 &self,
1089 info: &[u8],
1090 pub_key: &HpkePublicKey,
1091 sk_em: &[u8],
1092 ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error> {
1093 let (encap, sealer) = Sealer::test_only_new(self, info, pub_key, sk_em)?;
1094 Ok((encap, Box::new(sealer)))
1095 }
1096 }
1097
1098 static TEST_SUITES: &[&dyn TestHpke] = &[
1099 DH_KEM_P256_HKDF_SHA256_AES_128,
1100 DH_KEM_P256_HKDF_SHA256_AES_256,
1101 #[cfg(not(feature = "fips"))]
1102 DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305,
1103 DH_KEM_P384_HKDF_SHA384_AES_128,
1104 DH_KEM_P384_HKDF_SHA384_AES_256,
1105 #[cfg(not(feature = "fips"))]
1106 DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305,
1107 DH_KEM_P521_HKDF_SHA512_AES_128,
1108 DH_KEM_P521_HKDF_SHA512_AES_256,
1109 #[cfg(not(feature = "fips"))]
1110 DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305,
1111 #[cfg(not(feature = "fips"))]
1112 DH_KEM_X25519_HKDF_SHA256_AES_128,
1113 #[cfg(not(feature = "fips"))]
1114 DH_KEM_X25519_HKDF_SHA256_AES_256,
1115 #[cfg(not(feature = "fips"))]
1116 DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305,
1117 ];
1118
1119 #[derive(Deserialize, Debug)]
1120 struct TestVector {
1121 mode: u8,
1122 kem_id: u16,
1123 kdf_id: u16,
1124 aead_id: u16,
1125 info: String,
1126 #[serde(rename(deserialize = "pkRm"))]
1127 pk_rm: String,
1128 #[serde(rename(deserialize = "skRm"))]
1129 sk_rm: String,
1130 #[serde(rename(deserialize = "skEm"))]
1131 sk_em: String,
1132 enc: String,
1133 encryptions: Vec<TestEncryption>,
1134 }
1135
1136 #[derive(Deserialize, Debug)]
1137 struct TestEncryption {
1138 aad: String,
1139 pt: String,
1140 ct: String,
1141 }
1142
1143 impl TestVector {
1144 fn suite(&self) -> HpkeSuite {
1145 HpkeSuite {
1146 kem: HpkeKem::from(self.kem_id),
1147 sym: HpkeSymmetricCipherSuite {
1148 kdf_id: HpkeKdf::from(self.kdf_id),
1149 aead_id: HpkeAead::from(self.aead_id),
1150 },
1151 }
1152 }
1153
1154 fn applicable(&self) -> Option<&'static dyn TestHpke> {
1155 if self.mode != 0 {
1157 return None;
1158 }
1159
1160 Self::lookup_suite(self.suite(), TEST_SUITES)
1161 }
1162
1163 fn lookup_suite(
1164 suite: HpkeSuite,
1165 supported: &[&'static dyn TestHpke],
1166 ) -> Option<&'static dyn TestHpke> {
1167 supported
1168 .iter()
1169 .find(|s| s.suite() == suite)
1170 .copied()
1171 }
1172 }
1173
1174 fn test_vectors() -> Vec<TestVector> {
1175 serde_json::from_reader(
1176 &mut File::open("../rustls-provider-test/tests/rfc-9180-test-vectors.json")
1177 .expect("failed to open test vectors data file"),
1178 )
1179 .expect("failed to deserialize test vectors")
1180 }
1181}