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, HpkeOpener, HpkePrivateKey, HpkePublicKey, HpkeSealer, HpkeSuite,
18};
19use crate::crypto::tls13::{HkdfExpander, HkdfPrkExtract, HkdfUsingHmac, expand};
20use crate::error::{Error, OtherError};
21use crate::msgs::enums::{HpkeAead, HpkeKdf, HpkeKem};
22use crate::msgs::handshake::HpkeSymmetricCipherSuite;
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 mut sealing_key = SealingKey::new(key, &mut self.key_schedule);
443
444 let mut in_out_buffer = Vec::from(plaintext);
445 sealing_key
446 .seal_in_place_append_tag(Aad::from(aad), &mut in_out_buffer)
447 .map_err(unspecified_err)?;
448
449 Ok(in_out_buffer)
450 }
451}
452
453impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for Sealer<KEY_SIZE, KDF_SIZE> {
454 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
455 f.debug_struct("Sealer").finish()
456 }
457}
458
459struct Opener<const KEY_SIZE: usize, const KDF_SIZE: usize> {
461 key_schedule: KeySchedule<KEY_SIZE>,
462}
463
464impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Opener<KEY_SIZE, KDF_SIZE> {
465 fn new(
469 suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>,
470 enc: &EncapsulatedSecret,
471 info: &[u8],
472 secret_key: &HpkePrivateKey,
473 ) -> Result<Self, Error> {
474 Ok(Self {
479 key_schedule: suite.key_schedule(suite.dh_kem.decap(enc, secret_key)?, info)?,
480 })
481 }
482}
483
484impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeOpener for Opener<KEY_SIZE, KDF_SIZE> {
485 fn open(&mut self, aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
486 let key = UnboundKey::new(self.key_schedule.aead, &self.key_schedule.key.0)
494 .map_err(unspecified_err)?;
495 let mut opening_key = OpeningKey::new(key, &mut self.key_schedule);
496
497 let mut in_out_buffer = Vec::from(ciphertext);
498 let plaintext = opening_key
499 .open_in_place(Aad::from(aad), &mut in_out_buffer)
500 .map_err(unspecified_err)?;
501
502 Ok(plaintext.to_vec())
503 }
504}
505
506impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for Opener<KEY_SIZE, KDF_SIZE> {
507 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
508 f.debug_struct("Opener").finish()
509 }
510}
511
512struct DhKem<const KDF_SIZE: usize> {
518 id: HpkeKem,
519 agreement_algorithm: &'static agreement::Algorithm,
520 key_generator:
521 &'static (dyn Fn() -> Result<(HpkePublicKey, HpkePrivateKey), Error> + Send + Sync),
522 hkdf: &'static dyn HkdfPrkExtract,
523}
524
525impl<const KDF_SIZE: usize> DhKem<KDF_SIZE> {
526 fn encap(
530 &self,
531 recipient: &HpkePublicKey,
532 ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
533 let sk_e =
537 agreement::PrivateKey::generate(self.agreement_algorithm).map_err(unspecified_err)?;
538 self.encap_impl(recipient, sk_e)
539 }
540
541 #[cfg(test)]
544 fn test_only_encap(
545 &self,
546 recipient: &HpkePublicKey,
547 test_only_ske: &[u8],
548 ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
549 let sk_e = agreement::PrivateKey::from_private_key(self.agreement_algorithm, test_only_ske)
551 .map_err(key_rejected_err)?;
552 self.encap_impl(recipient, sk_e)
553 }
554
555 fn encap_impl(
556 &self,
557 recipient: &HpkePublicKey,
558 sk_e: agreement::PrivateKey,
559 ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
560 let enc = sk_e
572 .compute_public_key()
573 .map_err(unspecified_err)?;
574 let pk_r = agreement::UnparsedPublicKey::new(self.agreement_algorithm, &recipient.0);
575 let kem_context = [enc.as_ref(), pk_r.bytes()].concat();
576
577 let shared_secret = agreement::agree(&sk_e, pk_r, aws_lc_rs::error::Unspecified, |dh| {
578 Ok(self.extract_and_expand(dh, &kem_context))
579 })
580 .map_err(unspecified_err)?;
581
582 Ok((
583 KemSharedSecret(shared_secret),
584 EncapsulatedSecret(enc.as_ref().into()),
585 ))
586 }
587
588 fn decap(
592 &self,
593 enc: &EncapsulatedSecret,
594 recipient: &HpkePrivateKey,
595 ) -> Result<KemSharedSecret<KDF_SIZE>, Error> {
596 let pk_e = agreement::UnparsedPublicKey::new(self.agreement_algorithm, &enc.0);
607 let sk_r = agreement::PrivateKey::from_private_key(
608 self.agreement_algorithm,
609 recipient.secret_bytes(),
610 )
611 .map_err(key_rejected_err)?;
612 let pk_rm = sk_r
613 .compute_public_key()
614 .map_err(unspecified_err)?;
615 let kem_context = [&enc.0, pk_rm.as_ref()].concat();
616
617 let shared_secret = agreement::agree(&sk_r, pk_e, aws_lc_rs::error::Unspecified, |dh| {
618 Ok(self.extract_and_expand(dh, &kem_context))
619 })
620 .map_err(unspecified_err)?;
621
622 Ok(KemSharedSecret(shared_secret))
623 }
624
625 fn extract_and_expand(&self, dh: &[u8], kem_context: &[u8]) -> [u8; KDF_SIZE] {
629 let suite_id = LabeledSuiteId::Kem(self.id);
636 labeled_expand(
637 suite_id,
638 labeled_extract_for_expand(self.hkdf, suite_id, None, Label::EaePrk, dh),
639 Label::SharedSecret,
640 kem_context,
641 )
642 }
643}
644
645static DH_KEM_P256_HKDF_SHA256: &DhKem<SHA256_OUTPUT_LEN> = &DhKem {
646 id: HpkeKem::DHKEM_P256_HKDF_SHA256,
647 agreement_algorithm: &agreement::ECDH_P256,
648 key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P256),
649 hkdf: RING_HKDF_HMAC_SHA256,
650};
651
652static DH_KEM_P384_HKDF_SHA384: &DhKem<SHA384_OUTPUT_LEN> = &DhKem {
653 id: HpkeKem::DHKEM_P384_HKDF_SHA384,
654 agreement_algorithm: &agreement::ECDH_P384,
655 key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P384),
656 hkdf: RING_HKDF_HMAC_SHA384,
657};
658
659static DH_KEM_P521_HKDF_SHA512: &DhKem<SHA512_OUTPUT_LEN> = &DhKem {
660 id: HpkeKem::DHKEM_P521_HKDF_SHA512,
661 agreement_algorithm: &agreement::ECDH_P521,
662 key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P521),
663 hkdf: RING_HKDF_HMAC_SHA512,
664};
665
666static DH_KEM_X25519_HKDF_SHA256: &DhKem<SHA256_OUTPUT_LEN> = &DhKem {
667 id: HpkeKem::DHKEM_X25519_HKDF_SHA256,
668 agreement_algorithm: &agreement::X25519,
669 key_generator: &generate_x25519_key_pair,
670 hkdf: RING_HKDF_HMAC_SHA256,
671};
672
673fn generate_p_curve_key_pair(
680 alg: &'static agreement::Algorithm,
681) -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
682 debug_assert_ne!(alg, &agreement::X25519);
686 let (public_key, private_key) = generate_key_pair(alg)?;
687 let raw_private_key: EcPrivateKeyBin<'_> = private_key
688 .as_be_bytes()
689 .map_err(unspecified_err)?;
690 Ok((
691 public_key,
692 HpkePrivateKey::from(raw_private_key.as_ref().to_vec()),
693 ))
694}
695
696fn generate_x25519_key_pair() -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
703 let (public_key, private_key) = generate_key_pair(&agreement::X25519)?;
704 let raw_private_key: Curve25519SeedBin<'_> = private_key
705 .as_be_bytes()
706 .map_err(unspecified_err)?;
707 Ok((
708 public_key,
709 HpkePrivateKey::from(raw_private_key.as_ref().to_vec()),
710 ))
711}
712
713fn generate_key_pair(
714 alg: &'static agreement::Algorithm,
715) -> Result<(HpkePublicKey, agreement::PrivateKey), Error> {
716 let private_key = agreement::PrivateKey::generate(alg).map_err(unspecified_err)?;
717 let public_key = HpkePublicKey(
718 private_key
719 .compute_public_key()
720 .map_err(unspecified_err)?
721 .as_ref()
722 .to_vec(),
723 );
724 Ok((public_key, private_key))
725}
726
727struct KeySchedule<const KEY_SIZE: usize> {
730 aead: &'static aead::Algorithm,
731 key: AeadKey<KEY_SIZE>,
732 base_nonce: [u8; NONCE_LEN],
733 seq_num: u32,
734}
735
736impl<const KEY_SIZE: usize> KeySchedule<KEY_SIZE> {
737 fn compute_nonce(&self) -> [u8; NONCE_LEN] {
741 let mut nonce = [0; NONCE_LEN];
749 let seq_bytes = self.seq_num.to_be_bytes();
750 nonce[NONCE_LEN - seq_bytes.len()..].copy_from_slice(&seq_bytes);
751
752 for (n, &b) in nonce.iter_mut().zip(&self.base_nonce) {
753 *n ^= b;
754 }
755
756 nonce
757 }
758
759 fn increment_seq_num(&mut self) -> Result<(), aws_lc_rs::error::Unspecified> {
763 let max_seq_num = (1u128 << (NONCE_LEN * 8)) - 1;
771
772 if u128::from(self.seq_num) >= max_seq_num {
775 return Err(aws_lc_rs::error::Unspecified);
776 }
777
778 self.seq_num += 1;
779 Ok(())
780 }
781}
782
783impl<const KEY_SIZE: usize> NonceSequence for &mut KeySchedule<KEY_SIZE> {
784 fn advance(&mut self) -> Result<Nonce, aws_lc_rs::error::Unspecified> {
785 let nonce = self.compute_nonce();
786 self.increment_seq_num()?;
787 Nonce::try_assume_unique_for_key(&nonce)
788 }
789}
790
791fn labeled_extract_for_expand(
795 hkdf: &'static dyn HkdfPrkExtract,
796 suite_id: LabeledSuiteId,
797 salt: Option<&[u8]>,
798 label: Label,
799 ikm: &[u8],
800) -> Box<dyn HkdfExpander> {
801 let labeled_ikm = [&b"HPKE-v1"[..], &suite_id.encoded(), label.as_ref(), ikm].concat();
806 hkdf.extract_from_secret(salt, &labeled_ikm)
807}
808
809fn labeled_extract_for_prk(
813 hkdf: &'static dyn HkdfPrkExtract,
814 suite_id: LabeledSuiteId,
815 salt: Option<&[u8]>,
816 label: Label,
817 ikm: &[u8],
818) -> Vec<u8> {
819 let labeled_ikm = [&b"HPKE-v1"[..], &suite_id.encoded(), label.as_ref(), ikm].concat();
824 hkdf.extract_prk_from_secret(salt, &labeled_ikm)
825}
826
827fn labeled_expand<const L: usize>(
831 suite_id: LabeledSuiteId,
832 expander: Box<dyn HkdfExpander>,
833 label: Label,
834 kem_context: &[u8],
835) -> [u8; L] {
836 let output_len = u16::to_be_bytes(L as u16);
842 let info = &[
843 &output_len[..],
844 b"HPKE-v1",
845 &suite_id.encoded(),
846 label.as_ref(),
847 kem_context,
848 ];
849
850 expand(&*expander, info)
851}
852
853#[derive(Debug)]
855enum Label {
856 PskIdHash,
857 InfoHash,
858 Secret,
859 Key,
860 BaseNonce,
861 EaePrk,
862 SharedSecret,
863}
864
865impl AsRef<[u8]> for Label {
866 fn as_ref(&self) -> &[u8] {
867 match self {
868 Self::PskIdHash => b"psk_id_hash",
869 Self::InfoHash => b"info_hash",
870 Self::Secret => b"secret",
871 Self::Key => b"key",
872 Self::BaseNonce => b"base_nonce",
873 Self::EaePrk => b"eae_prk",
874 Self::SharedSecret => b"shared_secret",
875 }
876 }
877}
878
879#[derive(Debug, Copy, Clone)]
882enum LabeledSuiteId {
883 Hpke(HpkeSuite),
884 Kem(HpkeKem),
885}
886
887impl LabeledSuiteId {
888 fn encoded(&self) -> Vec<u8> {
895 match self {
896 Self::Hpke(suite) => [
897 &b"HPKE"[..],
898 &u16::from(suite.kem).to_be_bytes(),
899 &u16::from(suite.sym.kdf_id).to_be_bytes(),
900 &u16::from(suite.sym.aead_id).to_be_bytes(),
901 ]
902 .concat(),
903 Self::Kem(kem) => [&b"KEM"[..], &u16::from(*kem).to_be_bytes()].concat(),
904 }
905 }
906}
907
908struct AeadKey<const KEY_LEN: usize>([u8; KEY_LEN]);
910
911impl<const KEY_LEN: usize> Drop for AeadKey<KEY_LEN> {
912 fn drop(&mut self) {
913 self.0.zeroize()
914 }
915}
916
917struct KemSharedSecret<const KDF_LEN: usize>([u8; KDF_LEN]);
919
920impl<const KDF_LEN: usize> Drop for KemSharedSecret<KDF_LEN> {
921 fn drop(&mut self) {
922 self.0.zeroize();
923 }
924}
925
926fn key_rejected_err(e: aws_lc_rs::error::KeyRejected) -> Error {
927 Error::Other(OtherError::new(e))
928}
929
930const CHACHA_KEY_LEN: usize = 32;
933
934static RING_HKDF_HMAC_SHA256: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA256);
935static RING_HKDF_HMAC_SHA384: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA384);
936static RING_HKDF_HMAC_SHA512: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA512);
937
938#[cfg(test)]
939mod tests {
940 use alloc::{format, vec};
941
942 use super::*;
943
944 #[test]
945 fn smoke_test() {
946 for suite in ALL_SUPPORTED_SUITES {
947 _ = format!("{suite:?}"); let (pk, sk) = suite.generate_key_pair().unwrap();
951
952 let info = &[
954 0x4f, 0x64, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x47, 0x72, 0x65, 0x63, 0x69,
955 0x61, 0x6e, 0x20, 0x55, 0x72, 0x6e,
956 ][..];
957
958 let (enc, mut sealer) = suite.setup_sealer(info, &pk).unwrap();
960
961 _ = format!("{sealer:?}"); let bad_setup_res = suite.setup_sealer(info, &HpkePublicKey(vec![]));
965 assert!(matches!(bad_setup_res.unwrap_err(), Error::Other(_)));
966
967 let aad = &[0xC0, 0xFF, 0xEE];
969 let pt = &[0xF0, 0x0D];
970 let ct = sealer.seal(aad, pt).unwrap();
971
972 let mut opener = suite
974 .setup_opener(&enc, info, &sk)
975 .unwrap();
976 _ = format!("{opener:?}"); let bad_key_res = suite.setup_opener(&enc, info, &HpkePrivateKey::from(vec![]));
980 assert!(matches!(bad_key_res.unwrap_err(), Error::Other(_)));
981
982 let pt_prime = opener.open(aad, &ct).unwrap();
984 assert_eq!(pt_prime, pt);
985
986 let open_res = opener.open(&[0x0], &ct);
988 assert!(matches!(open_res.unwrap_err(), Error::Other(_)));
989
990 let mut sk_rm_prime = sk.secret_bytes().to_vec();
992 sk_rm_prime[10] ^= 0xFF; let mut opener_two = suite
994 .setup_opener(&enc, info, &HpkePrivateKey::from(sk_rm_prime))
995 .unwrap();
996 let open_res = opener_two.open(aad, &ct);
997 assert!(matches!(open_res.unwrap_err(), Error::Other(_)));
998 }
999 }
1000
1001 #[cfg(not(feature = "fips"))] #[test]
1003 fn test_fips() {
1004 let testcases: &[(&dyn Hpke, bool)] = &[
1005 (DH_KEM_P256_HKDF_SHA256_AES_128, true),
1007 (DH_KEM_P256_HKDF_SHA256_AES_256, true),
1008 (DH_KEM_P384_HKDF_SHA384_AES_128, true),
1009 (DH_KEM_P384_HKDF_SHA384_AES_256, true),
1010 (DH_KEM_P521_HKDF_SHA512_AES_128, true),
1011 (DH_KEM_P521_HKDF_SHA512_AES_256, true),
1012 (DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305, false),
1014 (DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305, false),
1015 (DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305, false),
1016 (DH_KEM_X25519_HKDF_SHA256_AES_128, false),
1018 (DH_KEM_X25519_HKDF_SHA256_AES_256, false),
1019 (DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305, false),
1020 ];
1021 for (suite, expected) in testcases {
1022 assert_eq!(suite.fips(), *expected);
1023 }
1024 }
1025}
1026
1027#[cfg(test)]
1028mod rfc_tests {
1029 use alloc::string::String;
1030 use std::fs::File;
1031 use std::println;
1032
1033 use serde::Deserialize;
1034
1035 use super::*;
1036
1037 #[test]
1041 fn check_test_vectors() {
1042 for (idx, vec) in test_vectors().into_iter().enumerate() {
1043 let Some(hpke) = vec.applicable() else {
1044 println!("skipping inapplicable vector {idx}");
1045 continue;
1046 };
1047
1048 println!("testing vector {idx}");
1049 let pk_r = HpkePublicKey(hex::decode(vec.pk_rm).unwrap());
1050 let sk_r = HpkePrivateKey::from(hex::decode(vec.sk_rm).unwrap());
1051 let sk_em = hex::decode(vec.sk_em).unwrap();
1052 let info = hex::decode(vec.info).unwrap();
1053 let expected_enc = hex::decode(vec.enc).unwrap();
1054
1055 let (enc, mut sealer) = hpke
1056 .setup_test_sealer(&info, &pk_r, &sk_em)
1057 .unwrap();
1058 assert_eq!(enc.0, expected_enc);
1059
1060 let mut opener = hpke
1061 .setup_opener(&enc, &info, &sk_r)
1062 .unwrap();
1063
1064 for test_encryption in vec.encryptions {
1065 let aad = hex::decode(test_encryption.aad).unwrap();
1066 let pt = hex::decode(test_encryption.pt).unwrap();
1067 let expected_ct = hex::decode(test_encryption.ct).unwrap();
1068
1069 let ciphertext = sealer.seal(&aad, &pt).unwrap();
1070 assert_eq!(ciphertext, expected_ct);
1071
1072 let plaintext = opener.open(&aad, &ciphertext).unwrap();
1073 assert_eq!(plaintext, pt);
1074 }
1075 }
1076 }
1077
1078 trait TestHpke: Hpke {
1079 fn setup_test_sealer(
1080 &self,
1081 info: &[u8],
1082 pub_key: &HpkePublicKey,
1083 sk_em: &[u8],
1084 ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error>;
1085 }
1086
1087 impl<const KEY_SIZE: usize, const KDF_SIZE: usize> TestHpke for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
1088 fn setup_test_sealer(
1089 &self,
1090 info: &[u8],
1091 pub_key: &HpkePublicKey,
1092 sk_em: &[u8],
1093 ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error> {
1094 let (encap, sealer) = Sealer::test_only_new(self, info, pub_key, sk_em)?;
1095 Ok((encap, Box::new(sealer)))
1096 }
1097 }
1098
1099 static TEST_SUITES: &[&dyn TestHpke] = &[
1100 DH_KEM_P256_HKDF_SHA256_AES_128,
1101 DH_KEM_P256_HKDF_SHA256_AES_256,
1102 #[cfg(not(feature = "fips"))]
1103 DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305,
1104 DH_KEM_P384_HKDF_SHA384_AES_128,
1105 DH_KEM_P384_HKDF_SHA384_AES_256,
1106 #[cfg(not(feature = "fips"))]
1107 DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305,
1108 DH_KEM_P521_HKDF_SHA512_AES_128,
1109 DH_KEM_P521_HKDF_SHA512_AES_256,
1110 #[cfg(not(feature = "fips"))]
1111 DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305,
1112 #[cfg(not(feature = "fips"))]
1113 DH_KEM_X25519_HKDF_SHA256_AES_128,
1114 #[cfg(not(feature = "fips"))]
1115 DH_KEM_X25519_HKDF_SHA256_AES_256,
1116 #[cfg(not(feature = "fips"))]
1117 DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305,
1118 ];
1119
1120 #[derive(Deserialize, Debug)]
1121 struct TestVector {
1122 mode: u8,
1123 kem_id: u16,
1124 kdf_id: u16,
1125 aead_id: u16,
1126 info: String,
1127 #[serde(rename(deserialize = "pkRm"))]
1128 pk_rm: String,
1129 #[serde(rename(deserialize = "skRm"))]
1130 sk_rm: String,
1131 #[serde(rename(deserialize = "skEm"))]
1132 sk_em: String,
1133 enc: String,
1134 encryptions: Vec<TestEncryption>,
1135 }
1136
1137 #[derive(Deserialize, Debug)]
1138 struct TestEncryption {
1139 aad: String,
1140 pt: String,
1141 ct: String,
1142 }
1143
1144 impl TestVector {
1145 fn suite(&self) -> HpkeSuite {
1146 HpkeSuite {
1147 kem: HpkeKem::from(self.kem_id),
1148 sym: HpkeSymmetricCipherSuite {
1149 kdf_id: HpkeKdf::from(self.kdf_id),
1150 aead_id: HpkeAead::from(self.aead_id),
1151 },
1152 }
1153 }
1154
1155 fn applicable(&self) -> Option<&'static dyn TestHpke> {
1156 if self.mode != 0 {
1158 return None;
1159 }
1160
1161 Self::lookup_suite(self.suite(), TEST_SUITES)
1162 }
1163
1164 fn lookup_suite(
1165 suite: HpkeSuite,
1166 supported: &[&'static dyn TestHpke],
1167 ) -> Option<&'static dyn TestHpke> {
1168 supported
1169 .iter()
1170 .find(|s| s.suite() == suite)
1171 .copied()
1172 }
1173 }
1174
1175 fn test_vectors() -> Vec<TestVector> {
1176 serde_json::from_reader(
1177 &mut File::open("../rustls-provider-test/tests/rfc-9180-test-vectors.json")
1178 .expect("failed to open test vectors data file"),
1179 )
1180 .expect("failed to deserialize test vectors")
1181 }
1182}