hpke_crypto/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3#![allow(clippy::match_same_arms)]
4#![allow(clippy::must_use_candidate)]
5
6#[cfg(feature = "_backend")]
7pub mod backend;
8pub mod kdf;
9
10extern crate alloc;
11
12#[cfg(feature = "std")]
13extern crate std;
14
15use alloc::boxed::Box;
16use alloc::vec::Vec;
17use core::fmt;
18use core::ops::DerefMut;
19
20use smallvec::SmallVec;
21use subtle::ConstantTimeEq;
22
23/// Cryptographic primitives for HPKE.
24pub trait Crypto: fmt::Debug + Send + Sync {
25    /// Fill the provided buffer with secure random bytes.
26    ///
27    /// # Errors
28    ///
29    /// See [`CryptoError`] for possible errors.
30    fn secure_random_fill(&mut self, buf: &mut [u8]) -> Result<(), CryptoError>;
31
32    /// Helper function to check if a KEM algorithm is supported by this
33    /// backend.
34    fn is_kem_supported(&self, alg: &HpkeKemId) -> bool;
35
36    /// `GenerateKeyPair()`: Randomized algorithm to generate a key pair (skX,
37    /// pkX).
38    ///
39    /// For DH-Based KEMs, this is the generation of a private key skX and
40    /// computation of the corresponding public key pkX.
41    ///
42    /// See [RFC 9180, Section 4] for details.
43    ///
44    /// # Errors
45    ///
46    /// See [`CryptoError`] for possible errors.
47    ///
48    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
49    fn kem_generate_key_pair(&mut self, alg: HpkeKemId) -> Result<HpkeKeyPair, CryptoError>;
50
51    /// `Encap(pkR)`: Randomized algorithm to generate an ephemeral,
52    /// fixed-length symmetric key (the KEM shared secret) and a
53    /// fixed-length encapsulation of that key that can be decapsulated by
54    /// the holder of the private key corresponding to `pkR`.
55    ///
56    /// See [RFC 9180, Section 4] for details.
57    ///
58    /// Currently, we only support DHKEM and `Encap(pkR)` is implemented in
59    /// the core library.
60    ///
61    /// # Errors
62    ///
63    /// See [`CryptoError`] for possible errors.
64    ///
65    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
66    fn kem_encap(
67        &self,
68        _alg: HpkeKemId,
69        _pk_r: HpkePublicKeyRef<'_>,
70    ) -> Result<(SharedSecret, EncapsulatedSecret), CryptoError> {
71        Err(CryptoError::KemOpUnsupported)
72    }
73
74    /// `Decap(enc, skR)`: Deterministic algorithm using the private key `skR`
75    /// to recover the ephemeral symmetric key (the KEM shared secret) from
76    /// its encapsulated representation `enc`.
77    ///
78    /// See [RFC 9180, Section 4] for details.
79    ///
80    /// Currently, we only support DHKEM and `Decap(enc, skR)` is implemented in
81    /// the core library.
82    ///
83    /// # Errors
84    ///
85    /// See [`CryptoError`] for possible errors.
86    ///
87    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
88    fn kem_decap(
89        &self,
90        _alg: HpkeKemId,
91        _enc: EncapsulatedSecret,
92        _sk_r: HpkePrivateKeyRef<'_>,
93    ) -> Result<SharedSecret, CryptoError> {
94        Err(CryptoError::KemOpUnsupported)
95    }
96
97    /// Helper function to check if a KDF algorithm is supported by this
98    /// backend.
99    fn is_kdf_supported(&self, alg: &HpkeKdfId) -> bool;
100
101    /// `Extract(salt, ikm)`: Extract a pseudorandom key of fixed length `Nh`
102    /// bytes from input keying material `ikm` and an optional byte string
103    /// `salt`.
104    ///
105    /// See [RFC 9180, Section 4] for details.
106    ///
107    /// # Errors
108    ///
109    /// See [`CryptoError`] for possible errors.
110    ///
111    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
112    fn kdf_extract(&self, alg: HpkeKdfId, salt: &[u8], ikm: IkmRef<'_>)
113    -> Result<Prk, CryptoError>;
114
115    /// See [`kdf_extract`](Crypto::kdf_extract).
116    ///
117    /// `concat(x0, ..., xN)`: Concatenation of byte strings. `concat(0x01,
118    /// 0x0203, 0x040506) = 0x010203040506`.
119    ///
120    /// The default implementation concatenates the input keying materials and
121    /// calls `kdf_extract`. This is a convenience function, but for those who
122    /// support multiple IKM inputs natively, they can override this
123    /// function.
124    ///
125    /// # Errors
126    ///
127    /// See [`CryptoError`] for possible errors.
128    fn kdf_extract_concated(
129        &self,
130        alg: HpkeKdfId,
131        salt: &[u8],
132        ikms: &[IkmRef<'_>],
133    ) -> Result<Prk, CryptoError> {
134        let mut concated = Vec::new();
135
136        for ikm in ikms {
137            concated.extend_from_slice(ikm);
138        }
139
140        self.kdf_extract(alg, salt, IkmRef::from(&concated))
141    }
142
143    /// `Expand(prk, info, L)`: Expand a pseudorandom key `prk` using optional
144    /// string `info` into `L` bytes of output keying material.
145    ///
146    /// See [RFC 9180, Section 4] for details.
147    ///
148    /// # Errors
149    ///
150    /// See [`CryptoError`] for possible errors.
151    ///
152    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
153    fn kdf_expand(
154        &self,
155        alg: HpkeKdfId,
156        prk: PrkRef<'_>,
157        info: &[u8],
158        l: usize,
159    ) -> Result<Okm, CryptoError>;
160
161    /// See [`kdf_expand`](Crypto::kdf_expand).
162    ///
163    /// `concat(x0, ..., xN)`: Concatenation of byte strings. `concat(0x01,
164    /// 0x0203, 0x040506) = 0x010203040506`.
165    ///
166    /// The default implementation concatenates the info slices and calls
167    /// `kdf_expand`. This is a convenience function, but for those who
168    /// support multiple info inputs natively, they can override this
169    /// function.
170    ///
171    /// # Errors
172    ///
173    /// See [`CryptoError`] for possible errors.
174    fn kdf_expand_multi_info(
175        &self,
176        alg: HpkeKdfId,
177        prk: PrkRef<'_>,
178        infos: &[&[u8]],
179        l: usize,
180    ) -> Result<Okm, CryptoError> {
181        self.kdf_expand(alg, prk, &infos.concat(), l)
182    }
183
184    /// Helper function to check if a AEAD algorithm is supported by this
185    /// backend.
186    fn is_aead_supported(&self, alg: &HpkeAeadId) -> bool;
187
188    /// `Seal(key, nonce, aad, pt)`: Encrypt and authenticate plaintext with
189    /// associated data `aad` using symmetric key `key` and nonce `nonce`,
190    /// yielding ciphertext and tag `ct`.
191    ///
192    /// See [RFC 9180, Section 4] for details.
193    ///
194    /// For in-place encryption, see
195    /// [`aead_seal_in_place`](Crypto::aead_seal_in_place).
196    ///
197    /// # Errors
198    ///
199    /// See [`CryptoError`] for possible errors.
200    ///
201    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
202    fn aead_seal(
203        &self,
204        crypto_info: &HpkeAead,
205        aad: &[u8],
206        plaintext: &[u8],
207    ) -> Result<Vec<u8>, CryptoError> {
208        let mut buffer = plaintext.to_vec();
209        self.aead_seal_in_place(crypto_info, aad, &mut buffer)?;
210        Ok(buffer)
211    }
212
213    /// `Seal(key, nonce, aad, pt)`: Encrypt and authenticate plaintext `pt`
214    /// with associated data `aad` using symmetric key `key` and nonce
215    /// `nonce`, yielding ciphertext and tag `ct`.
216    ///
217    /// This function operates in-place, modifying the `buffer` directly. The
218    /// buffer should contain the plaintext followed by space for the
219    /// authentication tag.
220    ///
221    /// See [RFC 9180, Section 4] for details.
222    ///
223    /// # Errors
224    ///
225    /// See [`CryptoError`] for possible errors.
226    ///
227    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
228    fn aead_seal_in_place(
229        &self,
230        crypto_info: &HpkeAead,
231        aad: &[u8],
232        buffer: &mut Vec<u8>,
233    ) -> Result<(), CryptoError>;
234
235    /// `Open(key, nonce, aad, ct)`: Decrypt ciphertext and tag `ct` using
236    /// associated data `aad` with symmetric key `key` and nonce `nonce`,
237    /// returning plaintext message `pt`.
238    ///
239    /// See [RFC 9180, Section 4] for details.
240    ///
241    /// For in-place decryption, see
242    /// [`aead_open_in_place`](Crypto::aead_open_in_place).
243    ///
244    /// # Errors
245    ///
246    /// See [`CryptoError`] for possible errors.
247    ///
248    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
249    fn aead_open(
250        &self,
251        crypto_info: &HpkeAead,
252        aad: &[u8],
253        ciphertext: &[u8],
254    ) -> Result<Vec<u8>, CryptoError> {
255        let mut buffer = ciphertext.to_vec();
256        self.aead_open_in_place(crypto_info, aad, &mut buffer)?;
257        Ok(buffer)
258    }
259
260    /// `Open(key, nonce, aad, ct)`: Decrypt ciphertext and tag `ct` using
261    /// associated data `aad` with symmetric key `key` and nonce `nonce`,
262    /// returning plaintext message `pt`.
263    ///
264    /// This function operates in-place, modifying the `buffer` directly. The
265    /// buffer should contain the ciphertext followed by the authentication
266    /// tag.
267    ///
268    /// See [RFC 9180, Section 4] for details.
269    ///
270    /// Notes that some implementations may clear the buffer on failure.
271    ///
272    /// # Errors
273    ///
274    /// See [`CryptoError`] for possible errors.
275    ///
276    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
277    fn aead_open_in_place(
278        &self,
279        crypto_info: &HpkeAead,
280        aad: &[u8],
281        buffer: &mut Vec<u8>,
282    ) -> Result<(), CryptoError>;
283
284    /// This is not part of the HPKE spec, but a utility function to
285    /// validate a private key for the KEM and wrap it in a
286    /// [`HpkePrivateKey`] if valid.
287    ///
288    /// # Errors
289    ///
290    /// See [`CryptoError`] for possible errors.
291    fn sk(&self, alg: HpkeKemId, sk: &[u8]) -> Result<HpkePrivateKey, CryptoError>;
292
293    /// The notation `pk(skX)`, depending on its use and the KEM and its
294    /// implementation, is either the computation of the public key using the
295    /// private key, or just syntax expressing the retrieval of the public key,
296    /// assuming it is stored along with the private key object.
297    ///
298    /// See [RFC 9180, Section 4] for details.
299    ///
300    /// # Errors
301    ///
302    /// See [`CryptoError`] for possible errors.
303    ///
304    /// [RFC 9180, Section 4.1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
305    fn pk(&self, alg: HpkeKemId, sk: HpkePrivateKeyRef<'_>) -> Result<HpkePublicKey, CryptoError>;
306
307    /// Perform a non-interactive Diffie-Hellman exchange using the private key
308    /// `skX` and public key `pkY` to produce a Diffie-Hellman shared secret of
309    /// length `Ndh`.
310    ///
311    /// See [RFC 9180, Section 4.1] for details.
312    ///
313    /// Notes that only DH-Based KEM supports this operation.
314    ///
315    /// # Errors
316    ///
317    /// See [`CryptoError`] for possible errors.
318    ///
319    /// [RFC 9180, Section 4.1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1
320    fn dh(
321        &self,
322        alg: HpkeKemId,
323        sk_x: HpkePrivateKeyRef<'_>,
324        pk_y: HpkePublicKeyRef<'_>,
325    ) -> Result<SharedSecret, CryptoError>;
326}
327
328impl<T> Crypto for T
329where
330    T: DerefMut<Target = dyn Crypto> + fmt::Debug + Send + Sync + ?Sized,
331{
332    fn secure_random_fill(&mut self, buf: &mut [u8]) -> Result<(), CryptoError> {
333        (**self).secure_random_fill(buf)
334    }
335
336    fn is_kem_supported(&self, alg: &HpkeKemId) -> bool {
337        (**self).is_kem_supported(alg)
338    }
339
340    fn kem_generate_key_pair(&mut self, alg: HpkeKemId) -> Result<HpkeKeyPair, CryptoError> {
341        (**self).kem_generate_key_pair(alg)
342    }
343
344    fn kem_encap(
345        &self,
346        alg: HpkeKemId,
347        pk_r: HpkePublicKeyRef<'_>,
348    ) -> Result<(SharedSecret, EncapsulatedSecret), CryptoError> {
349        (**self).kem_encap(alg, pk_r)
350    }
351
352    fn kem_decap(
353        &self,
354        alg: HpkeKemId,
355        enc: EncapsulatedSecret,
356        sk_r: HpkePrivateKeyRef<'_>,
357    ) -> Result<SharedSecret, CryptoError> {
358        (**self).kem_decap(alg, enc, sk_r)
359    }
360
361    fn is_kdf_supported(&self, alg: &HpkeKdfId) -> bool {
362        (**self).is_kdf_supported(alg)
363    }
364
365    fn kdf_extract(
366        &self,
367        alg: HpkeKdfId,
368        salt: &[u8],
369        ikm: IkmRef<'_>,
370    ) -> Result<Prk, CryptoError> {
371        (**self).kdf_extract(alg, salt, ikm)
372    }
373
374    fn kdf_extract_concated(
375        &self,
376        alg: HpkeKdfId,
377        salt: &[u8],
378        ikms: &[IkmRef<'_>],
379    ) -> Result<Prk, CryptoError> {
380        (**self).kdf_extract_concated(alg, salt, ikms)
381    }
382
383    fn kdf_expand(
384        &self,
385        alg: HpkeKdfId,
386        prk: PrkRef<'_>,
387        info: &[u8],
388        l: usize,
389    ) -> Result<Okm, CryptoError> {
390        (**self).kdf_expand(alg, prk, info, l)
391    }
392
393    fn kdf_expand_multi_info(
394        &self,
395        alg: HpkeKdfId,
396        prk: PrkRef<'_>,
397        infos: &[&[u8]],
398        l: usize,
399    ) -> Result<Okm, CryptoError> {
400        (**self).kdf_expand_multi_info(alg, prk, infos, l)
401    }
402
403    fn is_aead_supported(&self, alg: &HpkeAeadId) -> bool {
404        (**self).is_aead_supported(alg)
405    }
406
407    fn aead_seal(
408        &self,
409        crypto_info: &HpkeAead,
410        aad: &[u8],
411        plaintext: &[u8],
412    ) -> Result<Vec<u8>, CryptoError> {
413        (**self).aead_seal(crypto_info, aad, plaintext)
414    }
415
416    fn aead_seal_in_place(
417        &self,
418        crypto_info: &HpkeAead,
419        aad: &[u8],
420        buffer: &mut Vec<u8>,
421    ) -> Result<(), CryptoError> {
422        (**self).aead_seal_in_place(crypto_info, aad, buffer)
423    }
424
425    fn aead_open(
426        &self,
427        crypto_info: &HpkeAead,
428        aad: &[u8],
429        ciphertext: &[u8],
430    ) -> Result<Vec<u8>, CryptoError> {
431        (**self).aead_open(crypto_info, aad, ciphertext)
432    }
433
434    fn aead_open_in_place(
435        &self,
436        crypto_info: &HpkeAead,
437        aad: &[u8],
438        buffer: &mut Vec<u8>,
439    ) -> Result<(), CryptoError> {
440        (**self).aead_open_in_place(crypto_info, aad, buffer)
441    }
442
443    fn sk(&self, alg: HpkeKemId, sk: &[u8]) -> Result<HpkePrivateKey, CryptoError> {
444        (**self).sk(alg, sk)
445    }
446
447    fn pk(&self, alg: HpkeKemId, sk: HpkePrivateKeyRef<'_>) -> Result<HpkePublicKey, CryptoError> {
448        (**self).pk(alg, sk)
449    }
450
451    fn dh(
452        &self,
453        alg: HpkeKemId,
454        sk_x: HpkePrivateKeyRef<'_>,
455        pk_y: HpkePublicKeyRef<'_>,
456    ) -> Result<SharedSecret, CryptoError> {
457        (**self).dh(alg, sk_x, pk_y)
458    }
459}
460
461#[derive(Debug)]
462/// Errors thrown by implementations.
463pub enum CryptoError {
464    /// `Expand()` failed due to invalid PRK length.
465    ///
466    /// This error is returned when:
467    ///
468    /// 1. For HKDF-Expand, the PRK length MUST be at least `Nh` bytes.
469    KdfExpandInvalidPrkLen,
470
471    /// `Expand()` failed due to invalid output length.
472    ///
473    /// This error is returned when:
474    ///
475    /// 1. For HKDF-Expand, the requested output length MUST NOT exceed the
476    ///    maximum allowed length (255 * `Nh`).
477    KdfExpandInvalidOutputLen,
478
479    /// The KDF algorithm is unknown or unsupported by the crypto backend.
480    KdfUnsupported,
481
482    /// Failed to derive a key pair from the input keying material.
483    ///
484    /// This error is returned when:
485    ///
486    /// 1. Failed to generate valid private key from the input keying material
487    ///    in 255 iterations.
488    KemDeriveKeyPair,
489
490    /// The `skX` is malformed and cannot be used as a private key for the KEM.
491    KemMalformedSkX,
492
493    /// The `pkX` is malformed and cannot be used as a public key for the KEM.
494    KemMalformedPkX,
495
496    /// The operation is not supported by the KEM algorithm.
497    ///
498    /// This error is returned when the KEM algorithm does not support
499    /// the requested operation, e.g., `Encap` or `Decap`.
500    KemOpUnsupported,
501
502    /// The KEM algorithm is unknown or unsupported by the crypto backend.
503    KemUnsupported,
504
505    /// Invalid key for the AEAD algorithm.
506    AeadInvalidKey,
507
508    /// Invalid nonce for the AEAD algorithm.
509    AeadInvalidNonce,
510
511    /// The cipher text `ct` is invalid for the AEAD algorithm.
512    ///
513    /// This error is returned when:
514    ///
515    /// 1. The cipher text length is less than the tag length.
516    AeadInvalidCt,
517
518    /// Error sealing an AEAD cipher text.
519    AeadSeal,
520
521    /// Error opening an AEAD cipher text.
522    AeadOpen,
523
524    /// Unknown or unsupported AEAD algorithm.
525    AeadUnsupported,
526
527    /// Insufficient randomness to perform the operation.
528    ///
529    /// This error is rarely returned.
530    InsufficientRandomness,
531
532    /// Unspecified error
533    Unspecified,
534
535    /// A crypto library error.
536    Custom(Box<dyn core::error::Error + Send + Sync + 'static>),
537}
538
539impl core::error::Error for CryptoError {
540    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
541        match self {
542            Self::Custom(e) => Some(&**e),
543            _ => None,
544        }
545    }
546}
547
548impl fmt::Display for CryptoError {
549    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550        match self {
551            Self::KdfExpandInvalidPrkLen => write!(f, "KDF expand: invalid PRK length"),
552            Self::KdfExpandInvalidOutputLen => write!(f, "KDF expand: invalid output length"),
553            Self::KdfUnsupported => write!(f, "KDF unsupported"),
554            Self::KemDeriveKeyPair => write!(f, "KEM derive key pair failed"),
555            Self::KemMalformedSkX => write!(f, "KEM malformed private key"),
556            Self::KemMalformedPkX => write!(f, "KEM malformed public key"),
557            Self::KemOpUnsupported => write!(f, "KEM operation unsupported"),
558            Self::KemUnsupported => write!(f, "KEM unsupported"),
559            Self::AeadInvalidKey => write!(f, "AEAD invalid key"),
560            Self::AeadInvalidNonce => write!(f, "AEAD invalid nonce"),
561            Self::AeadInvalidCt => write!(f, "AEAD invalid cipher text"),
562            Self::AeadSeal => write!(f, "AEAD seal error"),
563            Self::AeadOpen => write!(f, "AEAD open error"),
564            Self::AeadUnsupported => write!(f, "AEAD unsupported"),
565            Self::InsufficientRandomness => write!(f, "Insufficient randomness"),
566            Self::Unspecified => write!(f, "Unspecified crypto error"),
567            Self::Custom(e) => write!(f, "Crypto library error: {e}"),
568        }
569    }
570}
571
572// === Algorithm Identifiers ===
573
574#[derive(Debug, Clone, Copy)]
575/// A `ciphersuite` is a triple (KEM, KDF, AEAD) containing a choice of
576/// algorithm for each primitive.
577pub struct HpkeCipherSuite {
578    /// KEM algorithm identifier.
579    pub kem_id: HpkeKemId,
580
581    /// KDF algorithm identifier.
582    pub kdf_id: HpkeKdfId,
583
584    /// AEAD algorithm identifier.
585    pub aead_id: HpkeAeadId,
586}
587
588impl HpkeCipherSuite {
589    /// The value of `suite_id` depends on where the KDF is used; it is assumed
590    /// implicit from the implementation and not passed as a parameter. If used
591    /// inside a KEM algorithm, `suite_id` MUST start with "KEM" and identify
592    /// this KEM algorithm; if used in the remainder of HPKE, it MUST start
593    /// with "HPKE" and identify the entire ciphersuite in use.
594    ///
595    /// The HPKE algorithm identifiers, i.e., the KEM `kem_id`, KDF `kdf_id`,
596    /// and AEAD `aead_id` 2-byte code points, as defined in other places,
597    /// respectively, are assumed implicit from the implementation and
598    /// not passed as parameters. The implicit `suite_id` value used within
599    /// `LabeledExtract` and `LabeledExpand` is defined based on them as
600    /// follows:
601    ///
602    /// ```text
603    /// suite_id = concat(
604    ///   "HPKE",
605    ///   I2OSP(kem_id, 2),
606    ///   I2OSP(kdf_id, 2),
607    ///   I2OSP(aead_id, 2)
608    /// )
609    /// ```
610    ///
611    /// See [RFC 9180, Section 4], [RFC 9180, Section 5.1] for details.
612    ///
613    /// # Examples
614    ///
615    /// ```
616    /// # use hpke_crypto::{HpkeCipherSuite, HpkeKemId, HpkeKdfId, HpkeAeadId};
617    /// let suite = HpkeCipherSuite {
618    ///     kem_id: HpkeKemId::DHKEM_P256_HKDF_SHA256,
619    ///     kdf_id: HpkeKdfId::HKDF_SHA256,
620    ///     aead_id: HpkeAeadId::CHACHA20_POLY1305,
621    /// };
622    /// assert_eq!(suite.suite_id(), [72, 80, 75, 69, 0, 16, 0, 1, 0, 3]);
623    /// ```
624    ///
625    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
626    /// [RFC 9180, Section 5.1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1
627    pub fn suite_id(&self) -> [u8; 10] {
628        let mut suite_id = [0u8; 10];
629
630        suite_id[0..4].copy_from_slice(b"HPKE");
631        suite_id[4..6].copy_from_slice(&self.kem_id.to_int().to_be_bytes());
632        suite_id[6..8].copy_from_slice(&self.kdf_id.to_int().to_be_bytes());
633        suite_id[8..10].copy_from_slice(&self.aead_id.to_int().to_be_bytes());
634
635        suite_id
636    }
637}
638
639macro_rules! enum_builder {
640    (
641        type Error = $error:ident;
642        #[repr($uint:ty)]
643        $(#[$enum_meta:meta])*
644        $vis:vis enum $name:ident
645        {
646            $(
647                $(#[doc = $registry_comment:literal])*
648                $registry_name:ident = $registry_value:literal
649            ),+
650            $(,)?
651        }
652    ) => {
653        #[non_exhaustive]
654        #[allow(clippy::upper_case_acronyms)]
655        #[allow(non_camel_case_types)]
656        #[derive(PartialEq, Eq, Clone, Copy)]
657        #[repr($uint)]
658        $(#[$enum_meta])*
659        $vis enum $name {
660            $(
661                $(#[doc = $registry_comment])*
662                $registry_name = $registry_value,
663            )+
664        }
665
666        impl $name {
667            #[inline]
668            #[allow(unused)]
669            #[allow(clippy::missing_errors_doc)]
670            /// Constructs an enum value from its integer representation.
671            $vis const fn try_from_int(x: $uint) -> Result<Self, $error> {
672                match x {
673                    $(
674                        $registry_value => Ok(Self::$registry_name),
675                    )+
676                    _ => Err($error (x)),
677                }
678            }
679
680            #[inline]
681            #[allow(unused)]
682            /// Returns the integer representation of this value.
683            $vis const fn to_int(self) -> $uint {
684                self as $uint
685            }
686
687            #[inline]
688            #[allow(unused)]
689            /// Returns the big-endian byte representation of this value.
690            $vis const fn to_array(self) -> [u8; core::mem::size_of::<$uint>()] {
691                self.to_int().to_be_bytes()
692            }
693
694            #[inline]
695            #[allow(unused)]
696            /// Returns the string representation of this value.
697            $vis const fn as_str(&self) -> &'static str {
698                match self {
699                    $(
700                        Self::$registry_name => stringify!($registry_name),
701                    )+
702                }
703            }
704        }
705
706        impl From<$name> for $uint {
707            fn from(value: $name) -> Self {
708                value.to_int()
709            }
710        }
711
712        impl TryFrom<$uint> for $name {
713            type Error = $error;
714
715            fn try_from(x: $uint) -> Result<Self, Self::Error> {
716                Self::try_from_int(x)
717            }
718        }
719
720        impl ::core::fmt::Debug for $name {
721            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
722                match self {
723                    $(
724                        Self::$registry_name => f.write_str(stringify!($registry_name)),
725                    )+
726                }
727            }
728        }
729
730        impl ::core::fmt::Display for $name {
731            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
732                write!(f, "{:?}", self)
733            }
734        }
735
736        #[cfg(feature = "serde")]
737        impl serde::Serialize for $name {
738            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
739            where
740                S: serde::Serializer,
741            {
742                self.to_int().serialize(serializer)
743            }
744        }
745
746        #[cfg(feature = "serde")]
747        impl<'a> serde::Deserialize<'a> for $name {
748            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
749            where
750                D: serde::Deserializer<'a>,
751            {
752                let v = <$uint>::deserialize(deserializer)?;
753
754                Self::try_from(v).map_err(serde::de::Error::custom)
755            }
756        }
757    };
758}
759
760#[derive(Debug, Clone, Copy)]
761/// An unknown KEM identifier.
762pub struct UnknownHpkeKemId(pub u16);
763
764impl fmt::Display for UnknownHpkeKemId {
765    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
766        write!(f, "Unknown HPKE KEM ID: {}", self.0)
767    }
768}
769
770enum_builder!(
771    type Error = UnknownHpkeKemId;
772
773    #[repr(u16)]
774    /// HPKE Key Encapsulation Mechanisms (KEMs) identifiers.
775    ///
776    /// See [RFC 9180, Section 7.1].
777    ///
778    /// [RFC 9180, Section 7.1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1
779    pub enum HpkeKemId {
780        /// DHKEM(P-256, HKDF-SHA256)
781        DHKEM_P256_HKDF_SHA256 = 0x0010,
782
783        /// DHKEM(P-384, HKDF-SHA384)
784        DHKEM_P384_HKDF_SHA384 = 0x0011,
785
786        /// DHKEM(P-521, HKDF-SHA512)
787        DHKEM_P521_HKDF_SHA512 = 0x0012,
788
789        /// DHKEM(X25519, HKDF-SHA256)
790        DHKEM_X25519_HKDF_SHA256 = 0x0020,
791
792        /// DHKEM(X448, HKDF-SHA512)
793        DHKEM_X448_HKDF_SHA512 = 0x0021,
794    }
795);
796
797impl HpkeKemId {
798    #[inline]
799    /// Returns the KDF algorithm associated with the DHKEM.
800    ///
801    /// If the KEM is not a DHKEM, returns `None`.
802    pub const fn kdf_id(&self) -> HpkeKdfId {
803        match self {
804            Self::DHKEM_P256_HKDF_SHA256 | Self::DHKEM_X25519_HKDF_SHA256 => HpkeKdfId::HKDF_SHA256,
805            Self::DHKEM_P384_HKDF_SHA384 => HpkeKdfId::HKDF_SHA384,
806            Self::DHKEM_P521_HKDF_SHA512 | Self::DHKEM_X448_HKDF_SHA512 => HpkeKdfId::HKDF_SHA512,
807        }
808    }
809
810    #[inline]
811    /// The value of `suite_id` depends on where the KDF is used; it is assumed
812    /// implicit from the implementation and not passed as a parameter. If used
813    /// inside a KEM algorithm, `suite_id` MUST start with "KEM" and identify
814    /// this KEM algorithm; if used in the remainder of HPKE, it MUST start
815    /// with "HPKE" and identify the entire ciphersuite in use.
816    ///
817    /// The implicit `suite_id` value used within `LabeledExtract` and
818    /// `LabeledExpand` is defined as follows:
819    ///
820    /// ```text
821    /// suite_id = concat("KEM", I2OSP(kem_id, 2))
822    /// ```
823    ///
824    /// See [RFC 9180, Section 4], [RFC 9180, Section 4.1] for details.
825    ///
826    /// # Example
827    ///
828    /// ```
829    /// # use hpke_crypto::{HpkeKemId};
830    /// assert_eq!(
831    ///     HpkeKemId::DHKEM_P521_HKDF_SHA512.suite_id(),
832    ///     [75, 69, 77, 0, 18]
833    /// );
834    /// ```
835    ///
836    /// [RFC 9180, Section 4]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4
837    /// [RFC 9180, Section 4.1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1
838    pub fn suite_id(&self) -> [u8; 5] {
839        let mut suite_id = [0u8; 5];
840
841        suite_id[0..3].copy_from_slice(b"KEM");
842        suite_id[3..5].copy_from_slice(&self.to_int().to_be_bytes());
843
844        suite_id
845    }
846
847    #[inline]
848    /// Returns the length in bytes of a KEM shared secret produced by this KEM
849    /// (Nsecret).
850    ///
851    /// See [RFC 9180, Section 7.1].
852    ///
853    /// [RFC 9180, Section 7.1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1
854    pub const fn n_secret(&self) -> usize {
855        match self {
856            Self::DHKEM_P256_HKDF_SHA256 => 32,
857            Self::DHKEM_P384_HKDF_SHA384 => 48,
858            Self::DHKEM_P521_HKDF_SHA512 => 64,
859            Self::DHKEM_X25519_HKDF_SHA256 => 32,
860            Self::DHKEM_X448_HKDF_SHA512 => 64,
861        }
862    }
863
864    #[inline]
865    /// Returns the length in bytes of an encapsulated key produced by this KEM.
866    /// (Nenc).
867    ///
868    /// See [RFC 9180, Section 7.1].
869    ///
870    /// [RFC 9180, Section 7.1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1
871    pub const fn n_enc(&self) -> usize {
872        match self {
873            Self::DHKEM_P256_HKDF_SHA256 => 65,
874            Self::DHKEM_P384_HKDF_SHA384 => 97,
875            Self::DHKEM_P521_HKDF_SHA512 => 133,
876            Self::DHKEM_X25519_HKDF_SHA256 => 32,
877            Self::DHKEM_X448_HKDF_SHA512 => 56,
878        }
879    }
880
881    #[inline]
882    /// Returns the length in bytes of an encoded public key for this KEM
883    /// (Npk).
884    ///
885    /// See [RFC 9180, Section 7.1].
886    ///
887    /// [RFC 9180, Section 7.1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1
888    pub const fn n_pk(&self) -> usize {
889        match self {
890            Self::DHKEM_P256_HKDF_SHA256 => 65,
891            Self::DHKEM_P384_HKDF_SHA384 => 97,
892            Self::DHKEM_P521_HKDF_SHA512 => 133,
893            Self::DHKEM_X25519_HKDF_SHA256 => 32,
894            Self::DHKEM_X448_HKDF_SHA512 => 56,
895        }
896    }
897
898    #[inline]
899    /// Returns the length in bytes of an encoded private key for this KEM
900    /// (Nsk).
901    ///
902    /// See [RFC 9180, Section 7.1].
903    ///
904    /// [RFC 9180, Section 7.1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1
905    pub const fn n_sk(&self) -> usize {
906        match self {
907            Self::DHKEM_P256_HKDF_SHA256 => 32,
908            Self::DHKEM_P384_HKDF_SHA384 => 48,
909            Self::DHKEM_P521_HKDF_SHA512 => 66,
910            Self::DHKEM_X25519_HKDF_SHA256 => 32,
911            Self::DHKEM_X448_HKDF_SHA512 => 56,
912        }
913    }
914}
915
916#[derive(Debug, Clone, Copy)]
917/// An unknown KDF identifier.
918pub struct UnknownHpkeKdfId(pub u16);
919
920impl fmt::Display for UnknownHpkeKdfId {
921    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
922        write!(f, "Unknown HPKE KDF ID: {}", self.0)
923    }
924}
925
926enum_builder!(
927    type Error = UnknownHpkeKdfId;
928
929    #[repr(u16)]
930    /// HPKE Key Derivation Functions (KDFs) identifiers.
931    ///
932    /// See [RFC 9180, Section 7.2].
933    ///
934    /// [RFC 9180, Section 7.2]: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.2
935    pub enum HpkeKdfId {
936        /// HKDF-SHA256
937        HKDF_SHA256 = 0x0001,
938
939        /// HKDF-SHA384
940        HKDF_SHA384 = 0x0002,
941
942        /// HKDF-SHA512
943        HKDF_SHA512 = 0x0003,
944    }
945);
946
947impl HpkeKdfId {
948    #[inline]
949    /// Returns the length in bytes of the hash output for this KDF (Nh).
950    pub const fn n_hash(&self) -> usize {
951        match self {
952            Self::HKDF_SHA256 => 32,
953            Self::HKDF_SHA384 => 48,
954            Self::HKDF_SHA512 => 64,
955        }
956    }
957}
958
959impl From<HpkeKemId> for HpkeKdfId {
960    #[inline]
961    fn from(kem: HpkeKemId) -> Self {
962        match kem {
963            HpkeKemId::DHKEM_P256_HKDF_SHA256 | HpkeKemId::DHKEM_X25519_HKDF_SHA256 => {
964                Self::HKDF_SHA256
965            }
966            HpkeKemId::DHKEM_P384_HKDF_SHA384 => Self::HKDF_SHA384,
967            HpkeKemId::DHKEM_P521_HKDF_SHA512 | HpkeKemId::DHKEM_X448_HKDF_SHA512 => {
968                Self::HKDF_SHA512
969            }
970        }
971    }
972}
973
974#[derive(Debug, Clone, Copy)]
975/// An unknown AEAD identifier.
976pub struct UnknownAeadAlgorithm(pub u16);
977
978impl fmt::Display for UnknownAeadAlgorithm {
979    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
980        write!(f, "Unknown HPKE AEAD ID: {}", self.0)
981    }
982}
983
984enum_builder!(
985    type Error = UnknownAeadAlgorithm;
986
987    #[repr(u16)]
988    /// HPKE Authenticated Encryption with Associated Data (AEAD) Functions
989    /// identifiers.
990    ///
991    /// See [RFC 9180, Section 7.3].
992    ///
993    /// [RFC 9180, Section 7.3]: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.3
994    pub enum HpkeAeadId {
995        /// `AES-128-GCM`
996        AES_128_GCM = 0x0001,
997
998        /// A`ES-256-GCM`
999        AES_256_GCM = 0x0002,
1000
1001        /// `ChaCha20Poly1305`
1002        CHACHA20_POLY1305 = 0x0003,
1003
1004        /// `Export-only`
1005        EXPORT_ONLY = 0xFFFF,
1006    }
1007);
1008
1009impl HpkeAeadId {
1010    #[inline]
1011    /// Returns the length in bytes of a key for this algorithm (`Nk`).
1012    pub const fn n_key(&self) -> usize {
1013        match self {
1014            Self::AES_128_GCM => 16,
1015            Self::AES_256_GCM => 32,
1016            Self::CHACHA20_POLY1305 => 32,
1017            Self::EXPORT_ONLY => 0,
1018        }
1019    }
1020
1021    #[inline]
1022    /// Returns the length in bytes of a nonce for this algorithm (`Nn`).
1023    pub const fn n_nonce(&self) -> usize {
1024        match self {
1025            Self::AES_128_GCM => 12,
1026            Self::AES_256_GCM => 12,
1027            Self::CHACHA20_POLY1305 => 12,
1028            Self::EXPORT_ONLY => 0,
1029        }
1030    }
1031
1032    #[inline]
1033    /// Returns the length in bytes of the authentication tag for this
1034    /// algorithm (`Nt`).
1035    pub const fn n_tag(&self) -> usize {
1036        match self {
1037            Self::AES_128_GCM => 16,
1038            Self::AES_256_GCM => 16,
1039            Self::CHACHA20_POLY1305 => 16,
1040            Self::EXPORT_ONLY => 0,
1041        }
1042    }
1043
1044    #[inline]
1045    /// Create the AEAD cryptographic material from the given key and nonce.
1046    ///
1047    /// Returns `None` if the AEAD algorithm is `EXPORT_ONLY`.
1048    ///
1049    /// # Errors
1050    ///
1051    /// Invalid key or nonce length.
1052    pub fn new_crypto_info(
1053        &self,
1054        key: &[u8],
1055        nonce: &[u8],
1056    ) -> Result<Option<HpkeAead>, CryptoError> {
1057        Ok(match self {
1058            Self::AES_128_GCM => Some(HpkeAead::Aes128Gcm {
1059                key: key
1060                    .try_into()
1061                    .map_err(|_| CryptoError::AeadInvalidKey)?,
1062                nonce: nonce
1063                    .try_into()
1064                    .map_err(|_| CryptoError::AeadInvalidNonce)?,
1065            }),
1066            Self::AES_256_GCM => Some(HpkeAead::Aes256Gcm {
1067                key: key
1068                    .try_into()
1069                    .map_err(|_| CryptoError::AeadInvalidKey)?,
1070                nonce: nonce
1071                    .try_into()
1072                    .map_err(|_| CryptoError::AeadInvalidNonce)?,
1073            }),
1074            Self::CHACHA20_POLY1305 => Some(HpkeAead::ChaCha20Poly1305 {
1075                key: key
1076                    .try_into()
1077                    .map_err(|_| CryptoError::AeadInvalidKey)?,
1078                nonce: nonce
1079                    .try_into()
1080                    .map_err(|_| CryptoError::AeadInvalidNonce)?,
1081            }),
1082            Self::EXPORT_ONLY => None,
1083        })
1084    }
1085}
1086
1087macro_rules! debug_hex {
1088    ($name:ty, $inner:ident) => {
1089        impl fmt::Debug for $name {
1090            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1091                f.debug_tuple(core::any::type_name::<Self>())
1092                    .field(&const_hex::encode(&self.$inner).as_str())
1093                    .finish()
1094            }
1095        }
1096    };
1097}
1098
1099#[derive(Clone, PartialEq, Eq)]
1100/// A HPKE public/private key pair.
1101pub struct HpkeKeyPair {
1102    inner: SmallVec<u8, 240>,
1103    split_offset: u8,
1104}
1105
1106impl fmt::Debug for HpkeKeyPair {
1107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1108        f.debug_struct(core::any::type_name::<Self>())
1109            .field("sk", &self.sk())
1110            .field("pk", &self.pk())
1111            .finish()
1112    }
1113}
1114
1115impl HpkeKeyPair {
1116    #[inline]
1117    /// Creates a new len-validated [`HpkePublicKey`] of the given KEM
1118    /// algorithm.
1119    ///
1120    /// Notes that this does *not* validate the keys cryptographically. Usually
1121    /// one can avoid using this except when implementing [`Crypto`] trait.
1122    ///
1123    /// # Errors
1124    ///
1125    /// Returns an error if the key's length is invalid for the given KEM
1126    /// algorithm.
1127    pub fn new_unchecked(
1128        alg: HpkeKemId,
1129        sk: impl AsRef<[u8]>,
1130        pk: impl AsRef<[u8]>,
1131    ) -> Result<Self, CryptoError> {
1132        if sk.as_ref().len() != alg.n_sk() {
1133            return Err(CryptoError::KemMalformedSkX);
1134        }
1135
1136        if pk.as_ref().len() != alg.n_pk() {
1137            return Err(CryptoError::KemMalformedPkX);
1138        }
1139
1140        let mut inner = SmallVec::new();
1141        inner.extend_from_slice(sk.as_ref());
1142        inner.extend_from_slice(pk.as_ref());
1143
1144        #[allow(
1145            clippy::cast_possible_truncation,
1146            reason = "`Nsk` must be less than 256 for all defined KEMs"
1147        )]
1148        Ok(Self {
1149            inner,
1150            split_offset: sk.as_ref().len() as u8,
1151        })
1152    }
1153
1154    #[inline]
1155    /// Returns the private key (skX).
1156    pub fn sk(&self) -> HpkePrivateKeyRef<'_> {
1157        HpkePrivateKeyRef::from_inner(&self.inner[..self.split_offset as usize])
1158    }
1159
1160    #[inline]
1161    /// Returns the public key (pkX).
1162    pub fn pk(&self) -> HpkePublicKeyRef<'_> {
1163        HpkePublicKeyRef::from_inner(&self.inner[self.split_offset as usize..])
1164    }
1165}
1166
1167wrapper_lite::wrapper!(
1168    #[wrapper_impl(AsRef<[u8]>)]
1169    #[wrapper_impl(Deref<[u8]>)]
1170    #[derive(Clone, PartialEq, Eq)]
1171    /// A HPKE public key (pkX).
1172    ///
1173    /// Notes that the key is not validated cryptographically.
1174    pub struct HpkePublicKey(SmallVec<u8, 184>);
1175);
1176
1177debug_hex!(HpkePublicKey, inner);
1178
1179impl HpkePublicKey {
1180    #[inline]
1181    /// Creates a new len-validated [`HpkePublicKey`] of the given KEM
1182    /// algorithm.
1183    ///
1184    /// Notes that this does *not* validate the public key itself
1185    /// cryptographically.
1186    ///
1187    /// # Errors
1188    ///
1189    /// Returns an error if the public key's length is invalid for the given
1190    /// KEM algorithm.
1191    pub fn new(alg: HpkeKemId, bytes: &[u8]) -> Result<Self, CryptoError> {
1192        if bytes.len() != alg.n_pk() {
1193            return Err(CryptoError::KemMalformedPkX);
1194        }
1195
1196        Ok(Self {
1197            inner: SmallVec::from(bytes),
1198        })
1199    }
1200}
1201
1202wrapper_lite::wrapper!(
1203    #[wrapper_impl(AsRef<[u8]>)]
1204    #[wrapper_impl(Deref<[u8]>)]
1205    #[wrapper_impl(From)]
1206    #[derive(Clone, Copy, PartialEq, Eq)]
1207    /// A HPKE public key (pkX).
1208    pub struct HpkePublicKeyRef<'a>(&'a [u8]);
1209);
1210
1211debug_hex!(HpkePublicKeyRef<'_>, inner);
1212
1213impl<'a> From<&'a HpkePublicKey> for HpkePublicKeyRef<'a> {
1214    fn from(value: &'a HpkePublicKey) -> Self {
1215        Self::from_inner(&value.inner)
1216    }
1217}
1218
1219impl HpkePublicKeyRef<'_> {
1220    #[inline]
1221    /// Converts this reference to an owned [`HpkePublicKey`].
1222    pub fn to_owned(&self) -> HpkePublicKey {
1223        HpkePublicKey {
1224            inner: SmallVec::from(self.inner),
1225        }
1226    }
1227}
1228
1229wrapper_lite::wrapper!(
1230    #[wrapper_impl(AsRef<[u8]>)]
1231    #[wrapper_impl(Deref<[u8]>)]
1232    #[derive(Eq)]
1233    #[derive(zeroize_derive::ZeroizeOnDrop)]
1234    #[cfg_attr(feature = "hazmat", derive(Clone))]
1235    /// A HPKE private key (skX).
1236    ///
1237    /// Notes that the key is not validated cryptographically.
1238    pub struct HpkePrivateKey(SmallVec<u8, 120>);
1239);
1240
1241impl PartialEq for HpkePrivateKey {
1242    fn eq(&self, other: &Self) -> bool {
1243        self.inner
1244            .ct_eq(other.inner.as_ref())
1245            .into()
1246    }
1247}
1248
1249impl fmt::Debug for HpkePrivateKey {
1250    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1251        #[cfg(feature = "hazmat")]
1252        {
1253            f.debug_tuple(core::any::type_name::<Self>())
1254                .field(&const_hex::encode(self.inner.as_ref()).as_str())
1255                .finish()
1256        }
1257
1258        #[cfg(not(feature = "hazmat"))]
1259        {
1260            f.debug_tuple(core::any::type_name::<Self>())
1261                .finish_non_exhaustive()
1262        }
1263    }
1264}
1265
1266impl HpkePrivateKey {
1267    #[inline]
1268    /// Creates a new [`HpkePrivateKey`] of the given KEM algorithm.
1269    ///
1270    /// Notes that this does *not* validate the private key itself
1271    /// cryptographically.
1272    ///
1273    /// # Errors
1274    ///
1275    /// Returns an error if the private key's length is invalid for the given
1276    /// KEM algorithm.
1277    pub fn new(alg: HpkeKemId, bytes: &[u8]) -> Result<Self, CryptoError> {
1278        if bytes.len() != alg.n_sk() {
1279            return Err(CryptoError::KemMalformedSkX);
1280        }
1281
1282        Ok(Self {
1283            inner: SmallVec::from(bytes),
1284        })
1285    }
1286}
1287
1288wrapper_lite::wrapper!(
1289    #[wrapper_impl(AsRef<[u8]>)]
1290    #[wrapper_impl(Deref<[u8]>)]
1291    #[wrapper_impl(From)]
1292    #[derive(Clone, Copy, Eq)]
1293    /// A HPKE private key (skX).
1294    ///
1295    /// Notes that the key is not validated cryptographically.
1296    pub struct HpkePrivateKeyRef<'a>(&'a [u8]);
1297);
1298
1299impl fmt::Debug for HpkePrivateKeyRef<'_> {
1300    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1301        #[cfg(feature = "hazmat")]
1302        {
1303            f.debug_tuple(core::any::type_name::<Self>())
1304                .field(&const_hex::encode(self.inner).as_str())
1305                .finish()
1306        }
1307
1308        #[cfg(not(feature = "hazmat"))]
1309        {
1310            f.debug_tuple(core::any::type_name::<Self>())
1311                .finish_non_exhaustive()
1312        }
1313    }
1314}
1315
1316impl PartialEq for HpkePrivateKeyRef<'_> {
1317    fn eq(&self, other: &Self) -> bool {
1318        self.inner
1319            .ct_eq(other.inner.as_ref())
1320            .into()
1321    }
1322}
1323
1324impl<'a> From<&'a HpkePrivateKey> for HpkePrivateKeyRef<'a> {
1325    fn from(value: &'a HpkePrivateKey) -> Self {
1326        Self::from_inner(&value.inner)
1327    }
1328}
1329
1330wrapper_lite::wrapper!(
1331    #[wrapper_impl(AsRef<[u8]>)]
1332    #[wrapper_impl(Deref<[u8]>)]
1333    #[wrapper_impl(From)]
1334    #[derive(Clone, PartialEq, Eq)]
1335    /// The shared secret produced by the KEM.
1336    pub struct SharedSecret(SmallVec<u8, 56>);
1337);
1338
1339debug_hex!(SharedSecret, inner);
1340
1341impl SharedSecret {
1342    #[inline]
1343    /// Constructs a new [`SharedSecret`] produced by the KEM.
1344    pub fn new(bytes: &[u8]) -> Self {
1345        Self {
1346            inner: SmallVec::from(bytes),
1347        }
1348    }
1349
1350    #[inline]
1351    /// Constructs a new [`SharedSecret`] from the output keying material
1352    /// (OKM).
1353    pub fn from_okm(okm: Okm) -> Self {
1354        Self { inner: okm.inner }
1355    }
1356}
1357
1358wrapper_lite::wrapper!(
1359    #[wrapper_impl(AsRef<[u8]>)]
1360    #[wrapper_impl(Deref<[u8]>)]
1361    #[wrapper_impl(From)]
1362    #[derive(Clone, PartialEq, Eq)]
1363    /// The shared secret produced by the KEM.
1364    pub struct SharedSecretRef<'a>(&'a [u8]);
1365);
1366
1367debug_hex!(SharedSecretRef<'_>, inner);
1368
1369impl<'a> From<&'a SharedSecret> for SharedSecretRef<'a> {
1370    fn from(value: &'a SharedSecret) -> Self {
1371        Self::from_inner(&value.inner)
1372    }
1373}
1374
1375wrapper_lite::wrapper!(
1376    #[wrapper_impl(AsRef<[u8]>)]
1377    #[wrapper_impl(Deref<[u8]>)]
1378    #[wrapper_impl(From)]
1379    #[derive(Clone, PartialEq, Eq)]
1380    /// The encapsulated secret produced by the KEM.
1381    pub struct EncapsulatedSecret(SmallVec<u8, 184>);
1382);
1383
1384debug_hex!(EncapsulatedSecret, inner);
1385
1386impl EncapsulatedSecret {
1387    #[inline]
1388    /// Constructs a new [`EncapsulatedSecret`] received from the sender for
1389    /// decapsulating the shared secret.
1390    pub fn new(bytes: &[u8]) -> Self {
1391        Self {
1392            inner: SmallVec::from(bytes),
1393        }
1394    }
1395
1396    /// Constructs a new [`EncapsulatedSecret`] from the ephemeral public key
1397    /// (pkE).
1398    pub fn new_from_pk_e(pk: HpkePublicKey) -> Self {
1399        Self { inner: pk.inner }
1400    }
1401}
1402
1403wrapper_lite::wrapper!(
1404    #[wrapper_impl(AsRef<[u8]>)]
1405    #[wrapper_impl(Deref<[u8]>)]
1406    #[wrapper_impl(From)]
1407    #[derive(Clone, PartialEq, Eq)]
1408    /// The encapsulated secret produced by the KEM.
1409    pub struct EncapsulatedSecretRef<'a>(&'a [u8]);
1410);
1411
1412debug_hex!(EncapsulatedSecretRef<'_>, inner);
1413
1414impl<'a> From<&'a EncapsulatedSecret> for EncapsulatedSecretRef<'a> {
1415    fn from(value: &'a EncapsulatedSecret) -> Self {
1416        Self::from_inner(&value.inner)
1417    }
1418}
1419
1420wrapper_lite::wrapper!(
1421    #[wrapper_impl(AsRef<[u8]>)]
1422    #[wrapper_impl(Deref<[u8]>)]
1423    #[wrapper_impl(From)]
1424    #[derive(Clone, Copy, PartialEq, Eq)]
1425    /// The input keying material (IKM), see HKDF-Extract.
1426    pub struct IkmRef<'a>(&'a [u8]);
1427);
1428
1429debug_hex!(IkmRef<'_>, inner);
1430
1431wrapper_lite::wrapper!(
1432    #[wrapper_impl(AsRef<[u8]>)]
1433    #[wrapper_impl(Deref<[u8]>)]
1434    #[derive(Clone, PartialEq, Eq)]
1435    /// A pseudorandom key (PRK) used in the KDF functions.
1436    pub struct Prk(SmallVec<u8, 64>);
1437);
1438
1439debug_hex!(Prk, inner);
1440
1441impl Prk {
1442    #[inline]
1443    /// Construct a new [`Prk`] directly with the given value.
1444    pub fn new_less_safe(bytes: &[u8]) -> Self {
1445        Self {
1446            inner: SmallVec::from(bytes),
1447        }
1448    }
1449}
1450
1451wrapper_lite::wrapper!(
1452    #[wrapper_impl(AsRef<[u8]>)]
1453    #[wrapper_impl(Deref<[u8]>)]
1454    #[wrapper_impl(From)]
1455    #[derive(Clone, PartialEq, Eq)]
1456    /// A pseudorandom key (PRK) used in the KDF functions.
1457    pub struct PrkRef<'a>(&'a [u8]);
1458);
1459
1460debug_hex!(PrkRef<'_>, inner);
1461
1462impl<'a> From<&'a Prk> for PrkRef<'a> {
1463    fn from(value: &'a Prk) -> Self {
1464        Self::from_inner(&value.inner)
1465    }
1466}
1467
1468wrapper_lite::wrapper!(
1469    #[wrapper_impl(AsRef<[u8]>)]
1470    #[wrapper_impl(Deref<[u8]>)]
1471    #[derive(Clone, PartialEq, Eq)]
1472    /// The output keying material (OKM), see HKDF-Expand.
1473    pub struct Okm(SmallVec<u8, 56>);
1474);
1475
1476debug_hex!(Okm, inner);
1477
1478impl Okm {
1479    #[inline]
1480    /// Construct a new empty [`Okm`].
1481    ///
1482    /// Usually one can avoid using this except when implementing [`Crypto`]
1483    /// trait.
1484    pub const fn empty() -> Self {
1485        Self {
1486            inner: SmallVec::new(),
1487        }
1488    }
1489
1490    /// Truncates the internal buffer to the given length.
1491    ///
1492    /// Usually one can avoid using this except when implementing [`Crypto`]
1493    /// trait.
1494    pub fn truncate(&mut self, len: usize) {
1495        self.inner.truncate(len);
1496    }
1497
1498    /// Returns a mutable buffer of the specified length, resizing the internal
1499    /// storage if necessary.
1500    ///
1501    /// Usually one can avoid using this except when implementing [`Crypto`]
1502    /// trait.
1503    pub fn as_mut_buffer(&mut self, len: usize) -> &mut [u8] {
1504        self.inner.resize(len, 0);
1505        &mut self.inner
1506    }
1507}
1508
1509#[non_exhaustive]
1510#[derive(Debug)]
1511#[derive(zeroize_derive::ZeroizeOnDrop)]
1512#[cfg_attr(feature = "hazmat", derive(PartialEq, Eq, Clone))]
1513/// AEAD cryptographic material.
1514pub enum HpkeAead {
1515    /// AES-128-GCM
1516    Aes128Gcm {
1517        /// The AEAD key.
1518        key: [u8; HpkeAeadId::AES_128_GCM.n_key()],
1519
1520        /// The AEAD nonce.
1521        nonce: [u8; HpkeAeadId::AES_128_GCM.n_nonce()],
1522    },
1523
1524    /// AES-256-GCM
1525    Aes256Gcm {
1526        /// The AEAD key.
1527        key: [u8; HpkeAeadId::AES_256_GCM.n_key()],
1528
1529        /// The AEAD nonce.
1530        nonce: [u8; HpkeAeadId::AES_256_GCM.n_nonce()],
1531    },
1532
1533    /// ChaCha20-Poly1305
1534    ChaCha20Poly1305 {
1535        /// The AEAD key.
1536        key: [u8; HpkeAeadId::CHACHA20_POLY1305.n_key()],
1537
1538        /// The AEAD nonce.
1539        nonce: [u8; HpkeAeadId::CHACHA20_POLY1305.n_nonce()],
1540    },
1541}
1542
1543impl HpkeAead {
1544    #[inline]
1545    /// Returns the AEAD algorithm identifier for this cryptographic material.
1546    pub const fn aead_id(&self) -> HpkeAeadId {
1547        match self {
1548            Self::Aes128Gcm { .. } => HpkeAeadId::AES_128_GCM,
1549            Self::Aes256Gcm { .. } => HpkeAeadId::AES_256_GCM,
1550            Self::ChaCha20Poly1305 { .. } => HpkeAeadId::CHACHA20_POLY1305,
1551        }
1552    }
1553
1554    #[allow(clippy::return_self_not_must_use)]
1555    /// Copies the AEAD cryptographic material, updating the nonce using the
1556    /// given function and returns the final copy for the actual AEAD operation.
1557    ///
1558    /// This is for updating the nonce for each AEAD operation.
1559    pub fn copied_updating_nonce<F>(&self, update_nonce_f: F) -> Self
1560    where
1561        F: FnOnce(&mut [u8]),
1562    {
1563        // Manually implement `Clone` to avoid `zeroize` on the original key and the
1564        // base nonce.
1565        match self {
1566            Self::Aes128Gcm { key, nonce } => {
1567                let mut nonce = *nonce;
1568
1569                update_nonce_f(&mut nonce);
1570
1571                Self::Aes128Gcm { key: *key, nonce }
1572            }
1573            Self::Aes256Gcm { key, nonce } => {
1574                let mut nonce = *nonce;
1575
1576                update_nonce_f(&mut nonce);
1577
1578                Self::Aes256Gcm { key: *key, nonce }
1579            }
1580            Self::ChaCha20Poly1305 { key, nonce } => {
1581                let mut nonce = *nonce;
1582
1583                update_nonce_f(&mut nonce);
1584
1585                Self::ChaCha20Poly1305 { key: *key, nonce }
1586            }
1587        }
1588    }
1589
1590    #[inline]
1591    /// Returns the AEAD key.
1592    pub const fn key(&self) -> &[u8] {
1593        match self {
1594            Self::Aes128Gcm { key, .. } => key,
1595            Self::Aes256Gcm { key, .. } => key,
1596            Self::ChaCha20Poly1305 { key, .. } => key,
1597        }
1598    }
1599
1600    #[inline]
1601    /// Returns the AEAD nonce.
1602    pub const fn nonce(&self) -> &[u8] {
1603        match self {
1604            Self::Aes128Gcm { nonce, .. } => nonce,
1605            Self::Aes256Gcm { nonce, .. } => nonce,
1606            Self::ChaCha20Poly1305 { nonce, .. } => nonce,
1607        }
1608    }
1609}
1610
1611impl zeroize::Zeroize for HpkeAead {
1612    fn zeroize(&mut self) {
1613        match self {
1614            Self::Aes128Gcm { key, .. } => {
1615                key.zeroize();
1616            }
1617            Self::Aes256Gcm { key, .. } => {
1618                key.zeroize();
1619            }
1620            Self::ChaCha20Poly1305 { key, .. } => {
1621                key.zeroize();
1622            }
1623        }
1624    }
1625}