rustls/crypto/aws_lc_rs/
hpke.rs

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::msgs::enums::{HpkeAead, HpkeKdf, HpkeKem};
21use crate::msgs::handshake::HpkeSymmetricCipherSuite;
22#[cfg(feature = "std")]
23use crate::sync::Arc;
24use crate::{Error, OtherError};
25
26/// Default [RFC 9180] Hybrid Public Key Encryption (HPKE) suites supported by aws-lc-rs cryptography.
27pub static ALL_SUPPORTED_SUITES: &[&dyn Hpke] = &[
28    DH_KEM_P256_HKDF_SHA256_AES_128,
29    DH_KEM_P256_HKDF_SHA256_AES_256,
30    #[cfg(not(feature = "fips"))]
31    DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305,
32    DH_KEM_P384_HKDF_SHA384_AES_128,
33    DH_KEM_P384_HKDF_SHA384_AES_256,
34    #[cfg(not(feature = "fips"))]
35    DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305,
36    DH_KEM_P521_HKDF_SHA512_AES_128,
37    DH_KEM_P521_HKDF_SHA512_AES_256,
38    #[cfg(not(feature = "fips"))]
39    DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305,
40    #[cfg(not(feature = "fips"))]
41    DH_KEM_X25519_HKDF_SHA256_AES_128,
42    #[cfg(not(feature = "fips"))]
43    DH_KEM_X25519_HKDF_SHA256_AES_256,
44    #[cfg(not(feature = "fips"))]
45    DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305,
46];
47
48/// HPKE suite using ECDH P-256 for agreement, HKDF SHA-256 for key derivation, and AEAD AES-128-GCM
49/// for symmetric encryption.
50pub static DH_KEM_P256_HKDF_SHA256_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA256_OUTPUT_LEN> =
51    &HpkeAwsLcRs {
52        suite: HpkeSuite {
53            kem: HpkeKem::DHKEM_P256_HKDF_SHA256,
54            sym: HpkeSymmetricCipherSuite {
55                kdf_id: HpkeKdf::HKDF_SHA256,
56                aead_id: HpkeAead::AES_128_GCM,
57            },
58        },
59        dh_kem: DH_KEM_P256_HKDF_SHA256,
60        hkdf: RING_HKDF_HMAC_SHA256,
61        aead: &aead::AES_128_GCM,
62    };
63
64/// HPKE suite using ECDH P-256 for agreement, HKDF SHA-256 for key derivation and AEAD AES-256-GCM
65/// for symmetric encryption.
66pub static DH_KEM_P256_HKDF_SHA256_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA256_OUTPUT_LEN> =
67    &HpkeAwsLcRs {
68        suite: HpkeSuite {
69            kem: HpkeKem::DHKEM_P256_HKDF_SHA256,
70            sym: HpkeSymmetricCipherSuite {
71                kdf_id: HpkeKdf::HKDF_SHA256,
72                aead_id: HpkeAead::AES_256_GCM,
73            },
74        },
75        dh_kem: DH_KEM_P256_HKDF_SHA256,
76        hkdf: RING_HKDF_HMAC_SHA256,
77        aead: &aead::AES_256_GCM,
78    };
79
80/// HPKE suite using ECDH P-256 for agreement, HKDF SHA-256 for key derivation, and AEAD
81/// CHACHA20-POLY-1305 for symmetric encryption.
82pub static DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305: &HpkeAwsLcRs<
83    CHACHA_KEY_LEN,
84    SHA256_OUTPUT_LEN,
85> = &HpkeAwsLcRs {
86    suite: HpkeSuite {
87        kem: HpkeKem::DHKEM_P256_HKDF_SHA256,
88        sym: HpkeSymmetricCipherSuite {
89            kdf_id: HpkeKdf::HKDF_SHA256,
90            aead_id: HpkeAead::CHACHA20_POLY_1305,
91        },
92    },
93    dh_kem: DH_KEM_P256_HKDF_SHA256,
94    hkdf: RING_HKDF_HMAC_SHA256,
95    aead: &aead::CHACHA20_POLY1305,
96};
97
98/// HPKE suite using ECDH P-384 for agreement, HKDF SHA-384 for key derivation, and AEAD AES-128-GCM
99/// for symmetric encryption.
100pub static DH_KEM_P384_HKDF_SHA384_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA384_OUTPUT_LEN> =
101    &HpkeAwsLcRs {
102        suite: HpkeSuite {
103            kem: HpkeKem::DHKEM_P384_HKDF_SHA384,
104            sym: HpkeSymmetricCipherSuite {
105                kdf_id: HpkeKdf::HKDF_SHA384,
106                aead_id: HpkeAead::AES_128_GCM,
107            },
108        },
109        dh_kem: DH_KEM_P384_HKDF_SHA384,
110        hkdf: RING_HKDF_HMAC_SHA384,
111        aead: &aead::AES_128_GCM,
112    };
113
114/// HPKE suite using ECDH P-384 for agreement, HKDF SHA-384 for key derivation, and AEAD AES-256-GCM
115/// for symmetric encryption.
116pub static DH_KEM_P384_HKDF_SHA384_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA384_OUTPUT_LEN> =
117    &HpkeAwsLcRs {
118        suite: HpkeSuite {
119            kem: HpkeKem::DHKEM_P384_HKDF_SHA384,
120            sym: HpkeSymmetricCipherSuite {
121                kdf_id: HpkeKdf::HKDF_SHA384,
122                aead_id: HpkeAead::AES_256_GCM,
123            },
124        },
125        dh_kem: DH_KEM_P384_HKDF_SHA384,
126        hkdf: RING_HKDF_HMAC_SHA384,
127        aead: &aead::AES_256_GCM,
128    };
129
130/// HPKE suite using ECDH P-384 for agreement, HKDF SHA-384 for key derivation, and AEAD
131/// CHACHA20-POLY-1305 for symmetric encryption.
132pub static DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305: &HpkeAwsLcRs<
133    CHACHA_KEY_LEN,
134    SHA384_OUTPUT_LEN,
135> = &HpkeAwsLcRs {
136    suite: HpkeSuite {
137        kem: HpkeKem::DHKEM_P384_HKDF_SHA384,
138        sym: HpkeSymmetricCipherSuite {
139            kdf_id: HpkeKdf::HKDF_SHA384,
140            aead_id: HpkeAead::CHACHA20_POLY_1305,
141        },
142    },
143    dh_kem: DH_KEM_P384_HKDF_SHA384,
144    hkdf: RING_HKDF_HMAC_SHA384,
145    aead: &aead::CHACHA20_POLY1305,
146};
147
148/// HPKE suite using ECDH P-521 for agreement, HKDF SHA-512 for key derivation, and AEAD AES-128-GCM
149/// for symmetric encryption.
150pub static DH_KEM_P521_HKDF_SHA512_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA512_OUTPUT_LEN> =
151    &HpkeAwsLcRs {
152        suite: HpkeSuite {
153            kem: HpkeKem::DHKEM_P521_HKDF_SHA512,
154            sym: HpkeSymmetricCipherSuite {
155                kdf_id: HpkeKdf::HKDF_SHA512,
156                aead_id: HpkeAead::AES_128_GCM,
157            },
158        },
159        dh_kem: DH_KEM_P521_HKDF_SHA512,
160        hkdf: RING_HKDF_HMAC_SHA512,
161        aead: &aead::AES_128_GCM,
162    };
163
164/// HPKE suite using ECDH P-521 for agreement, HKDF SHA-512 for key derivation, and AEAD AES-256-GCM
165/// for symmetric encryption.
166pub static DH_KEM_P521_HKDF_SHA512_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA512_OUTPUT_LEN> =
167    &HpkeAwsLcRs {
168        suite: HpkeSuite {
169            kem: HpkeKem::DHKEM_P521_HKDF_SHA512,
170            sym: HpkeSymmetricCipherSuite {
171                kdf_id: HpkeKdf::HKDF_SHA512,
172                aead_id: HpkeAead::AES_256_GCM,
173            },
174        },
175        dh_kem: DH_KEM_P521_HKDF_SHA512,
176        hkdf: RING_HKDF_HMAC_SHA512,
177        aead: &aead::AES_256_GCM,
178    };
179
180/// HPKE suite using ECDH P-521 for agreement, HKDF SHA-512 for key derivation, and AEAD
181/// CHACHA20-POLY-1305 for symmetric encryption.
182pub static DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305: &HpkeAwsLcRs<
183    CHACHA_KEY_LEN,
184    SHA512_OUTPUT_LEN,
185> = &HpkeAwsLcRs {
186    suite: HpkeSuite {
187        kem: HpkeKem::DHKEM_P521_HKDF_SHA512,
188        sym: HpkeSymmetricCipherSuite {
189            kdf_id: HpkeKdf::HKDF_SHA512,
190            aead_id: HpkeAead::CHACHA20_POLY_1305,
191        },
192    },
193    dh_kem: DH_KEM_P521_HKDF_SHA512,
194    hkdf: RING_HKDF_HMAC_SHA512,
195    aead: &aead::CHACHA20_POLY1305,
196};
197
198/// HPKE suite using ECDH X25519 for agreement, HKDF SHA-256 for key derivation, and AEAD AES-128-GCM
199/// for symmetric encryption.
200pub static DH_KEM_X25519_HKDF_SHA256_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA256_OUTPUT_LEN> =
201    &HpkeAwsLcRs {
202        suite: HpkeSuite {
203            kem: HpkeKem::DHKEM_X25519_HKDF_SHA256,
204            sym: HpkeSymmetricCipherSuite {
205                kdf_id: HpkeKdf::HKDF_SHA256,
206                aead_id: HpkeAead::AES_128_GCM,
207            },
208        },
209        dh_kem: DH_KEM_X25519_HKDF_SHA256,
210        hkdf: RING_HKDF_HMAC_SHA256,
211        aead: &aead::AES_128_GCM,
212    };
213
214/// HPKE suite using ECDH X25519 for agreement, HKDF SHA-256 for key derivation, and AEAD AES-256-GCM
215/// for symmetric encryption.
216pub static DH_KEM_X25519_HKDF_SHA256_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA256_OUTPUT_LEN> =
217    &HpkeAwsLcRs {
218        suite: HpkeSuite {
219            kem: HpkeKem::DHKEM_X25519_HKDF_SHA256,
220            sym: HpkeSymmetricCipherSuite {
221                kdf_id: HpkeKdf::HKDF_SHA256,
222                aead_id: HpkeAead::AES_256_GCM,
223            },
224        },
225        dh_kem: DH_KEM_X25519_HKDF_SHA256,
226        hkdf: RING_HKDF_HMAC_SHA256,
227        aead: &aead::AES_256_GCM,
228    };
229
230/// HPKE suite using ECDH X25519 for agreement, HKDF SHA-256 for key derivation, and AEAD
231/// CHACHA20-POLY-1305 for symmetric encryption.
232pub static DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305: &HpkeAwsLcRs<
233    CHACHA_KEY_LEN,
234    SHA256_OUTPUT_LEN,
235> = &HpkeAwsLcRs {
236    suite: HpkeSuite {
237        kem: HpkeKem::DHKEM_X25519_HKDF_SHA256,
238        sym: HpkeSymmetricCipherSuite {
239            kdf_id: HpkeKdf::HKDF_SHA256,
240            aead_id: HpkeAead::CHACHA20_POLY_1305,
241        },
242    },
243    dh_kem: DH_KEM_X25519_HKDF_SHA256,
244    hkdf: RING_HKDF_HMAC_SHA256,
245    aead: &aead::CHACHA20_POLY1305,
246};
247
248/// `HpkeAwsLcRs` holds the concrete instantiations of the algorithms specified by the [HpkeSuite].
249pub struct HpkeAwsLcRs<const KEY_SIZE: usize, const KDF_SIZE: usize> {
250    suite: HpkeSuite,
251    dh_kem: &'static DhKem<KDF_SIZE>,
252    hkdf: &'static dyn HkdfPrkExtract,
253    aead: &'static aead::Algorithm,
254}
255
256impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
257    /// See [RFC 9180 §5.1 "Creating the Encryption Context"][0].
258    ///
259    /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1
260    fn key_schedule(
261        &self,
262        shared_secret: KemSharedSecret<KDF_SIZE>,
263        info: &[u8],
264    ) -> Result<KeySchedule<KEY_SIZE>, Error> {
265        // Note: we use an empty IKM for the `psk_id_hash` and `secret` labelled extractions because
266        // there is no PSK ID in base mode HPKE.
267
268        let suite_id = LabeledSuiteId::Hpke(self.suite);
269        let psk_id_hash = labeled_extract_for_prk(self.hkdf, suite_id, None, Label::PskIdHash, &[]);
270        let info_hash = labeled_extract_for_prk(self.hkdf, suite_id, None, Label::InfoHash, info);
271        let key_schedule_context = [
272            &[0][..], // base mode (0x00)
273            &psk_id_hash,
274            &info_hash,
275        ]
276        .concat();
277
278        let key = AeadKey(self.key_schedule_labeled_expand::<KEY_SIZE>(
279            &shared_secret,
280            &key_schedule_context,
281            Label::Key,
282        ));
283
284        let base_nonce = self.key_schedule_labeled_expand::<NONCE_LEN>(
285            &shared_secret,
286            &key_schedule_context,
287            Label::BaseNonce,
288        );
289
290        Ok(KeySchedule {
291            aead: self.aead,
292            key,
293            base_nonce,
294            seq_num: 0,
295        })
296    }
297
298    fn key_schedule_labeled_expand<const L: usize>(
299        &self,
300        shared_secret: &KemSharedSecret<KDF_SIZE>,
301        key_schedule_context: &[u8],
302        label: Label,
303    ) -> [u8; L] {
304        let suite_id = LabeledSuiteId::Hpke(self.suite);
305        labeled_expand::<L>(
306            suite_id,
307            labeled_extract_for_expand(
308                self.hkdf,
309                suite_id,
310                Some(&shared_secret.0),
311                Label::Secret,
312                &[],
313            ),
314            label,
315            key_schedule_context,
316        )
317    }
318}
319
320impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Hpke for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
321    fn seal(
322        &self,
323        info: &[u8],
324        aad: &[u8],
325        plaintext: &[u8],
326        pub_key: &HpkePublicKey,
327    ) -> Result<(EncapsulatedSecret, Vec<u8>), Error> {
328        let (encap, mut sealer) = self.setup_sealer(info, pub_key)?;
329        Ok((encap, sealer.seal(aad, plaintext)?))
330    }
331
332    fn setup_sealer(
333        &self,
334        info: &[u8],
335        pub_key: &HpkePublicKey,
336    ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error> {
337        let (encap, sealer) = Sealer::new(self, info, pub_key)?;
338        Ok((encap, Box::new(sealer)))
339    }
340
341    fn open(
342        &self,
343        enc: &EncapsulatedSecret,
344        info: &[u8],
345        aad: &[u8],
346        ciphertext: &[u8],
347        secret_key: &HpkePrivateKey,
348    ) -> Result<Vec<u8>, Error> {
349        self.setup_opener(enc, info, secret_key)?
350            .open(aad, ciphertext)
351    }
352
353    fn setup_opener(
354        &self,
355        enc: &EncapsulatedSecret,
356        info: &[u8],
357        secret_key: &HpkePrivateKey,
358    ) -> Result<Box<dyn HpkeOpener + 'static>, Error> {
359        Ok(Box::new(Opener::new(self, enc, info, secret_key)?))
360    }
361
362    fn fips(&self) -> bool {
363        matches!(
364            // We make a FIPS determination based on the suite's DH KEM and AEAD choice.
365            // We don't need to examine the KDF choice because all supported KDFs are FIPS
366            // compatible.
367            (self.suite.kem, self.suite.sym.aead_id),
368            (
369                // Only the NIST "P-curve" DH KEMs are FIPS compatible.
370                HpkeKem::DHKEM_P256_HKDF_SHA256
371                    | HpkeKem::DHKEM_P384_HKDF_SHA384
372                    | HpkeKem::DHKEM_P521_HKDF_SHA512,
373                // Only the AES AEADs are FIPS compatible.
374                HpkeAead::AES_128_GCM | HpkeAead::AES_256_GCM,
375            )
376        )
377    }
378
379    fn generate_key_pair(&self) -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
380        (self.dh_kem.key_generator)()
381    }
382
383    fn suite(&self) -> HpkeSuite {
384        self.suite
385    }
386}
387
388impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
389    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
390        self.suite.fmt(f)
391    }
392}
393
394/// Adapts a [KeySchedule] and [AeadKey] for the role of a [HpkeSealer].
395struct Sealer<const KEY_SIZE: usize, const KDF_SIZE: usize> {
396    key_schedule: KeySchedule<KEY_SIZE>,
397}
398
399impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Sealer<KEY_SIZE, KDF_SIZE> {
400    /// See [RFC 9180 §5.1.1 "Encryption to a Public Key"][0].
401    ///
402    /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.1
403    fn new(
404        suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>,
405        info: &[u8],
406        pub_key: &HpkePublicKey,
407    ) -> Result<(EncapsulatedSecret, Self), Error> {
408        // def SetupBaseS(pkR, info):
409        //   shared_secret, enc = Encap(pkR)
410        //   return enc, KeyScheduleS(mode_base, shared_secret, info,
411        //                            default_psk, default_psk_id)
412
413        let (shared_secret, enc) = suite.dh_kem.encap(pub_key)?;
414        let key_schedule = suite.key_schedule(shared_secret, info)?;
415        Ok((enc, Self { key_schedule }))
416    }
417
418    /// A **test only** constructor that uses a pre-specified ephemeral agreement private key
419    /// instead of one that is randomly generated.
420    #[cfg(test)]
421    fn test_only_new(
422        suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>,
423        info: &[u8],
424        pub_key: &HpkePublicKey,
425        sk_e: &[u8],
426    ) -> Result<(EncapsulatedSecret, Self), Error> {
427        let (shared_secret, enc) = suite
428            .dh_kem
429            .test_only_encap(pub_key, sk_e)?;
430        let key_schedule = suite.key_schedule(shared_secret, info)?;
431        Ok((enc, Self { key_schedule }))
432    }
433}
434
435impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeSealer for Sealer<KEY_SIZE, KDF_SIZE> {
436    fn seal(&mut self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> {
437        // def ContextS.Seal(aad, pt):
438        //   ct = Seal(self.key, self.ComputeNonce(self.seq), aad, pt)
439        //   self.IncrementSeq()
440        //   return ct
441
442        let key = UnboundKey::new(self.key_schedule.aead, &self.key_schedule.key.0)
443            .map_err(unspecified_err)?;
444        let mut sealing_key = SealingKey::new(key, &mut self.key_schedule);
445
446        let mut in_out_buffer = Vec::from(plaintext);
447        sealing_key
448            .seal_in_place_append_tag(Aad::from(aad), &mut in_out_buffer)
449            .map_err(unspecified_err)?;
450
451        Ok(in_out_buffer)
452    }
453}
454
455impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for Sealer<KEY_SIZE, KDF_SIZE> {
456    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
457        f.debug_struct("Sealer").finish()
458    }
459}
460
461/// Adapts a [KeySchedule] and [AeadKey] for the role of a [HpkeOpener].
462struct Opener<const KEY_SIZE: usize, const KDF_SIZE: usize> {
463    key_schedule: KeySchedule<KEY_SIZE>,
464}
465
466impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Opener<KEY_SIZE, KDF_SIZE> {
467    /// See [RFC 9180 §5.1.1 "Encryption to a Public Key"][0].
468    ///
469    /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.1
470    fn new(
471        suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>,
472        enc: &EncapsulatedSecret,
473        info: &[u8],
474        secret_key: &HpkePrivateKey,
475    ) -> Result<Self, Error> {
476        // def SetupBaseR(enc, skR, info):
477        //   shared_secret = Decap(enc, skR)
478        //   return KeyScheduleR(mode_base, shared_secret, info,
479        //                       default_psk, default_psk_id)
480        Ok(Self {
481            key_schedule: suite.key_schedule(suite.dh_kem.decap(enc, secret_key)?, info)?,
482        })
483    }
484}
485
486impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeOpener for Opener<KEY_SIZE, KDF_SIZE> {
487    fn open(&mut self, aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
488        // def ContextR.Open(aad, ct):
489        //   pt = Open(self.key, self.ComputeNonce(self.seq), aad, ct)
490        //   if pt == OpenError:
491        //     raise OpenError
492        //   self.IncrementSeq()
493        //   return pt
494
495        let key = UnboundKey::new(self.key_schedule.aead, &self.key_schedule.key.0)
496            .map_err(unspecified_err)?;
497        let mut opening_key = OpeningKey::new(key, &mut self.key_schedule);
498
499        let mut in_out_buffer = Vec::from(ciphertext);
500        let plaintext = opening_key
501            .open_in_place(Aad::from(aad), &mut in_out_buffer)
502            .map_err(unspecified_err)?;
503
504        Ok(plaintext.to_vec())
505    }
506}
507
508impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for Opener<KEY_SIZE, KDF_SIZE> {
509    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
510        f.debug_struct("Opener").finish()
511    }
512}
513
514/// A Diffie-Hellman (DH) based Key Encapsulation Mechanism (KEM).
515///
516/// See [RFC 9180 §4.1 "DH-Based KEM (DHKEM)"][0].
517///
518/// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1
519struct DhKem<const KDF_SIZE: usize> {
520    id: HpkeKem,
521    agreement_algorithm: &'static agreement::Algorithm,
522    key_generator:
523        &'static (dyn Fn() -> Result<(HpkePublicKey, HpkePrivateKey), Error> + Send + Sync),
524    hkdf: &'static dyn HkdfPrkExtract,
525}
526
527impl<const KDF_SIZE: usize> DhKem<KDF_SIZE> {
528    /// See [RFC 9180 §4.1 "DH-Based KEM (DHKEM)"][0].
529    ///
530    /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1
531    fn encap(
532        &self,
533        recipient: &HpkePublicKey,
534    ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
535        // def Encap(pkR):
536        //   skE, pkE = GenerateKeyPair()
537
538        let sk_e =
539            agreement::PrivateKey::generate(self.agreement_algorithm).map_err(unspecified_err)?;
540        self.encap_impl(recipient, sk_e)
541    }
542
543    /// A test-only encap operation that uses a fixed `test_only_ske` instead of generating
544    /// one randomly.
545    #[cfg(test)]
546    fn test_only_encap(
547        &self,
548        recipient: &HpkePublicKey,
549        test_only_ske: &[u8],
550    ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
551        // For test contexts only, we accept a static sk_e as an argument.
552        let sk_e = agreement::PrivateKey::from_private_key(self.agreement_algorithm, test_only_ske)
553            .map_err(key_rejected_err)?;
554        self.encap_impl(recipient, sk_e)
555    }
556
557    fn encap_impl(
558        &self,
559        recipient: &HpkePublicKey,
560        sk_e: agreement::PrivateKey,
561    ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> {
562        // def Encap(pkR):
563        //   skE, pkE = GenerateKeyPair()
564        //   dh = DH(skE, pkR)
565        //   enc = SerializePublicKey(pkE)
566        //
567        //   pkRm = SerializePublicKey(pkR)
568        //   kem_context = concat(enc, pkRm)
569        //
570        //   shared_secret = ExtractAndExpand(dh, kem_context)
571        //   return shared_secret, enc
572
573        let enc = sk_e
574            .compute_public_key()
575            .map_err(unspecified_err)?;
576        let pk_r = agreement::UnparsedPublicKey::new(self.agreement_algorithm, &recipient.0);
577        let kem_context = [enc.as_ref(), pk_r.bytes()].concat();
578
579        let shared_secret = agreement::agree(&sk_e, &pk_r, aws_lc_rs::error::Unspecified, |dh| {
580            Ok(self.extract_and_expand(dh, &kem_context))
581        })
582        .map_err(unspecified_err)?;
583
584        Ok((
585            KemSharedSecret(shared_secret),
586            EncapsulatedSecret(enc.as_ref().into()),
587        ))
588    }
589
590    /// See [RFC 9180 §4.1 "DH-Based KEM (DHKEM)"][0].
591    ///
592    /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1
593    fn decap(
594        &self,
595        enc: &EncapsulatedSecret,
596        recipient: &HpkePrivateKey,
597    ) -> Result<KemSharedSecret<KDF_SIZE>, Error> {
598        // def Decap(enc, skR):
599        //   pkE = DeserializePublicKey(enc)
600        //   dh = DH(skR, pkE)
601        //
602        //   pkRm = SerializePublicKey(pk(skR))
603        //   kem_context = concat(enc, pkRm)
604        //
605        //   shared_secret = ExtractAndExpand(dh, kem_context)
606        //   return shared_secret
607
608        let pk_e = agreement::UnparsedPublicKey::new(self.agreement_algorithm, &enc.0);
609        let sk_r = agreement::PrivateKey::from_private_key(
610            self.agreement_algorithm,
611            recipient.secret_bytes(),
612        )
613        .map_err(key_rejected_err)?;
614        let pk_rm = sk_r
615            .compute_public_key()
616            .map_err(unspecified_err)?;
617        let kem_context = [&enc.0, pk_rm.as_ref()].concat();
618
619        let shared_secret = agreement::agree(&sk_r, &pk_e, aws_lc_rs::error::Unspecified, |dh| {
620            Ok(self.extract_and_expand(dh, &kem_context))
621        })
622        .map_err(unspecified_err)?;
623
624        Ok(KemSharedSecret(shared_secret))
625    }
626
627    /// See [RFC 9180 §4.1 "DH-Based KEM (DHKEM)"][0].
628    ///
629    /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1
630    fn extract_and_expand(&self, dh: &[u8], kem_context: &[u8]) -> [u8; KDF_SIZE] {
631        // def ExtractAndExpand(dh, kem_context):
632        //   eae_prk = LabeledExtract("", "eae_prk", dh)
633        //   shared_secret = LabeledExpand(eae_prk, "shared_secret",
634        //                                 kem_context, Nsecret)
635        //   return shared_secret
636
637        let suite_id = LabeledSuiteId::Kem(self.id);
638        labeled_expand(
639            suite_id,
640            labeled_extract_for_expand(self.hkdf, suite_id, None, Label::EaePrk, dh),
641            Label::SharedSecret,
642            kem_context,
643        )
644    }
645}
646
647static DH_KEM_P256_HKDF_SHA256: &DhKem<SHA256_OUTPUT_LEN> = &DhKem {
648    id: HpkeKem::DHKEM_P256_HKDF_SHA256,
649    agreement_algorithm: &agreement::ECDH_P256,
650    key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P256),
651    hkdf: RING_HKDF_HMAC_SHA256,
652};
653
654static DH_KEM_P384_HKDF_SHA384: &DhKem<SHA384_OUTPUT_LEN> = &DhKem {
655    id: HpkeKem::DHKEM_P384_HKDF_SHA384,
656    agreement_algorithm: &agreement::ECDH_P384,
657    key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P384),
658    hkdf: RING_HKDF_HMAC_SHA384,
659};
660
661static DH_KEM_P521_HKDF_SHA512: &DhKem<SHA512_OUTPUT_LEN> = &DhKem {
662    id: HpkeKem::DHKEM_P521_HKDF_SHA512,
663    agreement_algorithm: &agreement::ECDH_P521,
664    key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P521),
665    hkdf: RING_HKDF_HMAC_SHA512,
666};
667
668static DH_KEM_X25519_HKDF_SHA256: &DhKem<SHA256_OUTPUT_LEN> = &DhKem {
669    id: HpkeKem::DHKEM_X25519_HKDF_SHA256,
670    agreement_algorithm: &agreement::X25519,
671    key_generator: &generate_x25519_key_pair,
672    hkdf: RING_HKDF_HMAC_SHA256,
673};
674
675/// Generate a NIST P-256, P-384 or P-512 key pair expressed as a raw big-endian fixed-length
676/// integer.
677///
678/// We must disambiguate the [`AsBigEndian`] trait in-use and this function uses
679/// [`AsBigEndian<EcPrivateKeyBin>`], which does not support [`agreement::X25519`].
680/// For generating X25519 keys see [`generate_x25519_key_pair`].
681fn generate_p_curve_key_pair(
682    alg: &'static agreement::Algorithm,
683) -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
684    // We only initialize DH KEM instances that use this function as a key generator
685    // for non-X25519 algorithms. Debug assert this just in case since `AsBigEndian<EcPrivateKeyBin>`
686    // will panic for this algorithm.
687    debug_assert_ne!(alg, &agreement::X25519);
688    let (public_key, private_key) = generate_key_pair(alg)?;
689    let raw_private_key: EcPrivateKeyBin<'_> = private_key
690        .as_be_bytes()
691        .map_err(unspecified_err)?;
692    Ok((
693        public_key,
694        HpkePrivateKey::from(raw_private_key.as_ref().to_vec()),
695    ))
696}
697
698/// Generate a X25519 key pair expressed as a raw big-endian fixed-length
699/// integer.
700///
701/// We must disambiguate the [`AsBigEndian`] trait in-use and this function uses
702/// [`AsBigEndian<Curve25519SeedBin>`], which only supports [`agreement::X25519`].
703/// For generating P-256, P-384 and P-512 keys see [`generate_p_curve_key_pair`].
704fn generate_x25519_key_pair() -> Result<(HpkePublicKey, HpkePrivateKey), Error> {
705    let (public_key, private_key) = generate_key_pair(&agreement::X25519)?;
706    let raw_private_key: Curve25519SeedBin<'_> = private_key
707        .as_be_bytes()
708        .map_err(unspecified_err)?;
709    Ok((
710        public_key,
711        HpkePrivateKey::from(raw_private_key.as_ref().to_vec()),
712    ))
713}
714
715fn generate_key_pair(
716    alg: &'static agreement::Algorithm,
717) -> Result<(HpkePublicKey, agreement::PrivateKey), Error> {
718    let private_key = agreement::PrivateKey::generate(alg).map_err(unspecified_err)?;
719    let public_key = HpkePublicKey(
720        private_key
721            .compute_public_key()
722            .map_err(unspecified_err)?
723            .as_ref()
724            .to_vec(),
725    );
726    Ok((public_key, private_key))
727}
728
729/// KeySchedule holds the derived AEAD key, base nonce, and seq number
730/// common to both a [Sealer] and [Opener].
731struct KeySchedule<const KEY_SIZE: usize> {
732    aead: &'static aead::Algorithm,
733    key: AeadKey<KEY_SIZE>,
734    base_nonce: [u8; NONCE_LEN],
735    seq_num: u32,
736}
737
738impl<const KEY_SIZE: usize> KeySchedule<KEY_SIZE> {
739    /// See [RFC 9180 §5.2 "Encryption and Decryption"][0].
740    ///
741    /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2
742    fn compute_nonce(&self) -> [u8; NONCE_LEN] {
743        // def Context<ROLE>.ComputeNonce(seq):
744        //   seq_bytes = I2OSP(seq, Nn)
745        //   return xor(self.base_nonce, seq_bytes)
746
747        // Each new N-byte nonce is conceptually two parts:
748        //   * N-4 bytes of the base nonce (0s in `nonce` to XOR in as-is).
749        //   * 4 bytes derived from the sequence number XOR the base nonce.
750        let mut nonce = [0; NONCE_LEN];
751        let seq_bytes = self.seq_num.to_be_bytes();
752        nonce[NONCE_LEN - seq_bytes.len()..].copy_from_slice(&seq_bytes);
753
754        for (n, &b) in nonce.iter_mut().zip(&self.base_nonce) {
755            *n ^= b;
756        }
757
758        nonce
759    }
760
761    /// See [RFC 9180 §5.2 "Encryption and Decryption"][0].
762    ///
763    /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2
764    fn increment_seq_num(&mut self) -> Result<(), aws_lc_rs::error::Unspecified> {
765        // def Context<ROLE>.IncrementSeq():
766        //   if self.seq >= (1 << (8*Nn)) - 1:
767        //     raise MessageLimitReachedError
768        //   self.seq += 1
769
770        // Determine the maximum sequence number using the AEAD nonce's length in bits.
771        // Do this as an u128 to prevent overflowing.
772        let max_seq_num = (1u128 << (NONCE_LEN * 8)) - 1;
773
774        // Promote the u32 sequence number to an u128 and compare against the maximum allowed
775        // sequence number.
776        if u128::from(self.seq_num) >= max_seq_num {
777            return Err(aws_lc_rs::error::Unspecified);
778        }
779
780        self.seq_num += 1;
781        Ok(())
782    }
783}
784
785impl<const KEY_SIZE: usize> NonceSequence for &mut KeySchedule<KEY_SIZE> {
786    fn advance(&mut self) -> Result<Nonce, aws_lc_rs::error::Unspecified> {
787        let nonce = self.compute_nonce();
788        self.increment_seq_num()?;
789        Nonce::try_assume_unique_for_key(&nonce)
790    }
791}
792
793/// See [RFC 9180 §4 "Cryptographic Dependencies"][0].
794///
795/// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
796fn labeled_extract_for_expand(
797    hkdf: &'static dyn HkdfPrkExtract,
798    suite_id: LabeledSuiteId,
799    salt: Option<&[u8]>,
800    label: Label,
801    ikm: &[u8],
802) -> Box<dyn HkdfExpander> {
803    // def LabeledExtract(salt, label, ikm):
804    //   labeled_ikm = concat("HPKE-v1", suite_id, label, ikm)
805    //   return Extract(salt, labeled_ikm)
806
807    let labeled_ikm = [&b"HPKE-v1"[..], &suite_id.encoded(), label.as_ref(), ikm].concat();
808    hkdf.extract_from_secret(salt, &labeled_ikm)
809}
810
811/// See [RFC 9180 §4 "Cryptographic Dependencies"][0].
812///
813/// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
814fn labeled_extract_for_prk(
815    hkdf: &'static dyn HkdfPrkExtract,
816    suite_id: LabeledSuiteId,
817    salt: Option<&[u8]>,
818    label: Label,
819    ikm: &[u8],
820) -> Vec<u8> {
821    // def LabeledExtract(salt, label, ikm):
822    //   labeled_ikm = concat("HPKE-v1", suite_id, label, ikm)
823    //   return Extract(salt, labeled_ikm)
824
825    let labeled_ikm = [&b"HPKE-v1"[..], &suite_id.encoded(), label.as_ref(), ikm].concat();
826    hkdf.extract_prk_from_secret(salt, &labeled_ikm)
827}
828
829/// See [RFC 9180 §4 "Cryptographic Dependencies"][0].
830///
831/// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
832fn labeled_expand<const L: usize>(
833    suite_id: LabeledSuiteId,
834    expander: Box<dyn HkdfExpander>,
835    label: Label,
836    kem_context: &[u8],
837) -> [u8; L] {
838    // def LabeledExpand(prk, label, info, L):
839    //   labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id,
840    //                         label, info)
841    //   return Expand(prk, labeled_info, L)
842
843    let output_len = u16::to_be_bytes(L as u16);
844    let info = &[
845        &output_len[..],
846        b"HPKE-v1",
847        &suite_id.encoded(),
848        label.as_ref(),
849        kem_context,
850    ];
851
852    expand(&*expander, info)
853}
854
855/// Label describes the possible labels for use with [labeled_extract_for_expand] and [labeled_expand].
856#[derive(Debug)]
857enum Label {
858    PskIdHash,
859    InfoHash,
860    Secret,
861    Key,
862    BaseNonce,
863    EaePrk,
864    SharedSecret,
865}
866
867impl AsRef<[u8]> for Label {
868    fn as_ref(&self) -> &[u8] {
869        match self {
870            Self::PskIdHash => b"psk_id_hash",
871            Self::InfoHash => b"info_hash",
872            Self::Secret => b"secret",
873            Self::Key => b"key",
874            Self::BaseNonce => b"base_nonce",
875            Self::EaePrk => b"eae_prk",
876            Self::SharedSecret => b"shared_secret",
877        }
878    }
879}
880
881/// LabeledSuiteId describes the possible suite ID values for use with [labeled_extract_for_expand] and
882/// [labeled_expand].
883#[derive(Debug, Copy, Clone)]
884enum LabeledSuiteId {
885    Hpke(HpkeSuite),
886    Kem(HpkeKem),
887}
888
889impl LabeledSuiteId {
890    /// The suite ID encoding depends on the context of use. In the general HPKE context,
891    /// we use a "HPKE" prefix and encode the entire ciphersuite. In the KEM context we use a
892    /// "KEM" prefix and only encode the KEM ID.
893    ///
894    /// See the bottom of [RFC 9180 §4](https://www.rfc-editor.org/rfc/rfc9180.html#section-4)
895    /// for more information.
896    fn encoded(&self) -> Vec<u8> {
897        match self {
898            Self::Hpke(suite) => [
899                &b"HPKE"[..],
900                &u16::from(suite.kem).to_be_bytes(),
901                &u16::from(suite.sym.kdf_id).to_be_bytes(),
902                &u16::from(suite.sym.aead_id).to_be_bytes(),
903            ]
904            .concat(),
905            Self::Kem(kem) => [&b"KEM"[..], &u16::from(*kem).to_be_bytes()].concat(),
906        }
907    }
908}
909
910/// A newtype wrapper for an unbound AEAD key.
911struct AeadKey<const KEY_LEN: usize>([u8; KEY_LEN]);
912
913impl<const KEY_LEN: usize> Drop for AeadKey<KEY_LEN> {
914    fn drop(&mut self) {
915        self.0.zeroize()
916    }
917}
918
919/// A newtype wrapper for a DH KEM shared secret.
920struct KemSharedSecret<const KDF_LEN: usize>([u8; KDF_LEN]);
921
922impl<const KDF_LEN: usize> Drop for KemSharedSecret<KDF_LEN> {
923    fn drop(&mut self) {
924        self.0.zeroize();
925    }
926}
927
928fn key_rejected_err(_e: aws_lc_rs::error::KeyRejected) -> Error {
929    #[cfg(feature = "std")]
930    {
931        Error::Other(OtherError(Arc::new(_e)))
932    }
933    #[cfg(not(feature = "std"))]
934    {
935        Error::Other(OtherError())
936    }
937}
938
939// The `cipher::chacha::KEY_LEN` const is not exported, so we copy it here:
940// https://github.com/aws/aws-lc-rs/blob/0186ef7bb1a4d7e140bae8074a9871f49afedf1b/aws-lc-rs/src/cipher/chacha.rs#L13
941const CHACHA_KEY_LEN: usize = 32;
942
943static RING_HKDF_HMAC_SHA256: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA256);
944static RING_HKDF_HMAC_SHA384: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA384);
945static RING_HKDF_HMAC_SHA512: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA512);
946
947#[cfg(test)]
948mod tests {
949    use alloc::{format, vec};
950
951    use super::*;
952
953    #[test]
954    fn smoke_test() {
955        for suite in ALL_SUPPORTED_SUITES {
956            _ = format!("{suite:?}"); // HpkeAwsLcRs suites should be Debug.
957
958            // We should be able to generate a random keypair.
959            let (pk, sk) = suite.generate_key_pair().unwrap();
960
961            // Info value corresponds to the first RFC 9180 base mode test vector.
962            let info = &[
963                0x4f, 0x64, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x47, 0x72, 0x65, 0x63, 0x69,
964                0x61, 0x6e, 0x20, 0x55, 0x72, 0x6e,
965            ][..];
966
967            // We should be able to set up a sealer.
968            let (enc, mut sealer) = suite.setup_sealer(info, &pk).unwrap();
969
970            _ = format!("{sealer:?}"); // Sealer should be Debug.
971
972            // Setting up a sealer with an invalid public key should fail.
973            let bad_setup_res = suite.setup_sealer(info, &HpkePublicKey(vec![]));
974            assert!(matches!(bad_setup_res.unwrap_err(), Error::Other(_)));
975
976            // We should be able to seal some plaintext.
977            let aad = &[0xC0, 0xFF, 0xEE];
978            let pt = &[0xF0, 0x0D];
979            let ct = sealer.seal(aad, pt).unwrap();
980
981            // We should be able to set up an opener.
982            let mut opener = suite
983                .setup_opener(&enc, info, &sk)
984                .unwrap();
985            _ = format!("{opener:?}"); // Opener should be Debug.
986
987            // Setting up an opener with an invalid private key should fail.
988            let bad_key_res = suite.setup_opener(&enc, info, &HpkePrivateKey::from(vec![]));
989            assert!(matches!(bad_key_res.unwrap_err(), Error::Other(_)));
990
991            // Opening the plaintext should work with the correct opener and aad.
992            let pt_prime = opener.open(aad, &ct).unwrap();
993            assert_eq!(pt_prime, pt);
994
995            // Opening the plaintext with the correct opener and wrong aad should fail.
996            let open_res = opener.open(&[0x0], &ct);
997            assert!(matches!(open_res.unwrap_err(), Error::Other(_)));
998
999            // Opening the plaintext with the wrong opener should fail.
1000            let mut sk_rm_prime = sk.secret_bytes().to_vec();
1001            sk_rm_prime[10] ^= 0xFF; // Corrupt a byte of the private key.
1002            let mut opener_two = suite
1003                .setup_opener(&enc, info, &HpkePrivateKey::from(sk_rm_prime))
1004                .unwrap();
1005            let open_res = opener_two.open(aad, &ct);
1006            assert!(matches!(open_res.unwrap_err(), Error::Other(_)));
1007        }
1008    }
1009
1010    #[cfg(not(feature = "fips"))] // Ensure all supported suites are available to test.
1011    #[test]
1012    fn test_fips() {
1013        let testcases: &[(&dyn Hpke, bool)] = &[
1014            // FIPS compatible.
1015            (DH_KEM_P256_HKDF_SHA256_AES_128, true),
1016            (DH_KEM_P256_HKDF_SHA256_AES_256, true),
1017            (DH_KEM_P384_HKDF_SHA384_AES_128, true),
1018            (DH_KEM_P384_HKDF_SHA384_AES_256, true),
1019            (DH_KEM_P521_HKDF_SHA512_AES_128, true),
1020            (DH_KEM_P521_HKDF_SHA512_AES_256, true),
1021            // AEAD is not FIPS compatible.
1022            (DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305, false),
1023            (DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305, false),
1024            (DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305, false),
1025            // KEM is not FIPS compatible.
1026            (DH_KEM_X25519_HKDF_SHA256_AES_128, false),
1027            (DH_KEM_X25519_HKDF_SHA256_AES_256, false),
1028            (DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305, false),
1029        ];
1030        for (suite, expected) in testcases {
1031            assert_eq!(suite.fips(), *expected);
1032        }
1033    }
1034}
1035
1036#[cfg(test)]
1037mod rfc_tests {
1038    use alloc::string::String;
1039    use std::fs::File;
1040    use std::println;
1041
1042    use serde::Deserialize;
1043
1044    use super::*;
1045
1046    /// Confirm open/seal operations work using the test vectors from [RFC 9180 Appendix A].
1047    ///
1048    /// [RFC 9180 Appendix A]: https://www.rfc-editor.org/rfc/rfc9180#TestVectors
1049    #[test]
1050    fn check_test_vectors() {
1051        for (idx, vec) in test_vectors().into_iter().enumerate() {
1052            let Some(hpke) = vec.applicable() else {
1053                println!("skipping inapplicable vector {idx}");
1054                continue;
1055            };
1056
1057            println!("testing vector {idx}");
1058            let pk_r = HpkePublicKey(hex::decode(vec.pk_rm).unwrap());
1059            let sk_r = HpkePrivateKey::from(hex::decode(vec.sk_rm).unwrap());
1060            let sk_em = hex::decode(vec.sk_em).unwrap();
1061            let info = hex::decode(vec.info).unwrap();
1062            let expected_enc = hex::decode(vec.enc).unwrap();
1063
1064            let (enc, mut sealer) = hpke
1065                .setup_test_sealer(&info, &pk_r, &sk_em)
1066                .unwrap();
1067            assert_eq!(enc.0, expected_enc);
1068
1069            let mut opener = hpke
1070                .setup_opener(&enc, &info, &sk_r)
1071                .unwrap();
1072
1073            for test_encryption in vec.encryptions {
1074                let aad = hex::decode(test_encryption.aad).unwrap();
1075                let pt = hex::decode(test_encryption.pt).unwrap();
1076                let expected_ct = hex::decode(test_encryption.ct).unwrap();
1077
1078                let ciphertext = sealer.seal(&aad, &pt).unwrap();
1079                assert_eq!(ciphertext, expected_ct);
1080
1081                let plaintext = opener.open(&aad, &ciphertext).unwrap();
1082                assert_eq!(plaintext, pt);
1083            }
1084        }
1085    }
1086
1087    trait TestHpke: Hpke {
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    }
1095
1096    impl<const KEY_SIZE: usize, const KDF_SIZE: usize> TestHpke for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> {
1097        fn setup_test_sealer(
1098            &self,
1099            info: &[u8],
1100            pub_key: &HpkePublicKey,
1101            sk_em: &[u8],
1102        ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error> {
1103            let (encap, sealer) = Sealer::test_only_new(self, info, pub_key, sk_em)?;
1104            Ok((encap, Box::new(sealer)))
1105        }
1106    }
1107
1108    static TEST_SUITES: &[&dyn TestHpke] = &[
1109        DH_KEM_P256_HKDF_SHA256_AES_128,
1110        DH_KEM_P256_HKDF_SHA256_AES_256,
1111        #[cfg(not(feature = "fips"))]
1112        DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305,
1113        DH_KEM_P384_HKDF_SHA384_AES_128,
1114        DH_KEM_P384_HKDF_SHA384_AES_256,
1115        #[cfg(not(feature = "fips"))]
1116        DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305,
1117        DH_KEM_P521_HKDF_SHA512_AES_128,
1118        DH_KEM_P521_HKDF_SHA512_AES_256,
1119        #[cfg(not(feature = "fips"))]
1120        DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305,
1121        #[cfg(not(feature = "fips"))]
1122        DH_KEM_X25519_HKDF_SHA256_AES_128,
1123        #[cfg(not(feature = "fips"))]
1124        DH_KEM_X25519_HKDF_SHA256_AES_256,
1125        #[cfg(not(feature = "fips"))]
1126        DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305,
1127    ];
1128
1129    #[derive(Deserialize, Debug)]
1130    struct TestVector {
1131        mode: u8,
1132        kem_id: u16,
1133        kdf_id: u16,
1134        aead_id: u16,
1135        info: String,
1136        #[serde(rename(deserialize = "pkRm"))]
1137        pk_rm: String,
1138        #[serde(rename(deserialize = "skRm"))]
1139        sk_rm: String,
1140        #[serde(rename(deserialize = "skEm"))]
1141        sk_em: String,
1142        enc: String,
1143        encryptions: Vec<TestEncryption>,
1144    }
1145
1146    #[derive(Deserialize, Debug)]
1147    struct TestEncryption {
1148        aad: String,
1149        pt: String,
1150        ct: String,
1151    }
1152
1153    impl TestVector {
1154        fn suite(&self) -> HpkeSuite {
1155            HpkeSuite {
1156                kem: HpkeKem::from(self.kem_id),
1157                sym: HpkeSymmetricCipherSuite {
1158                    kdf_id: HpkeKdf::from(self.kdf_id),
1159                    aead_id: HpkeAead::from(self.aead_id),
1160                },
1161            }
1162        }
1163
1164        fn applicable(&self) -> Option<&'static dyn TestHpke> {
1165            // Only base mode test vectors for supported suites are applicable.
1166            if self.mode != 0 {
1167                return None;
1168            }
1169
1170            Self::lookup_suite(self.suite(), TEST_SUITES)
1171        }
1172
1173        fn lookup_suite(
1174            suite: HpkeSuite,
1175            supported: &[&'static dyn TestHpke],
1176        ) -> Option<&'static dyn TestHpke> {
1177            supported
1178                .iter()
1179                .find(|s| s.suite() == suite)
1180                .copied()
1181        }
1182    }
1183
1184    fn test_vectors() -> Vec<TestVector> {
1185        serde_json::from_reader(
1186            &mut File::open("../rustls-provider-test/tests/rfc-9180-test-vectors.json")
1187                .expect("failed to open test vectors data file"),
1188        )
1189        .expect("failed to deserialize test vectors")
1190    }
1191}