spideroak_crypto/
kem.rs

1//! Key Encapsulation Mechanisms.
2
3use core::{array::TryFromSliceError, borrow::Borrow, fmt, marker::PhantomData, result::Result};
4
5use crate::{
6    csprng::{Csprng, Random},
7    hpke::KemId,
8    import::{Import, ImportError},
9    kdf::{Kdf, KdfError, Prk},
10    keys::{PublicKey, RawSecretBytes, SecretKey},
11    signer::PkError,
12    zeroize::{zeroize_flat_type, ZeroizeOnDrop},
13};
14
15/// An error from a [`Kem`].
16#[derive(Debug, Eq, PartialEq)]
17pub enum KemError {
18    /// The imported secret key is invalid.
19    InvalidDecapKeyFormat,
20    /// The imported public key is invalid.
21    InvalidEncapKeyFormat,
22    /// KEM encapsulation failed.
23    Encap,
24    /// Unable to decapsulate the ephemeral symmetric key.
25    Decapsulation,
26    /// A DHKEM operation failed.
27    DhKem(DhKemError),
28    /// A public key could not be imported.
29    Import(ImportError),
30    /// A public key could not be read.
31    PublicKey(PkError),
32}
33
34impl fmt::Display for KemError {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            Self::InvalidDecapKeyFormat => write!(f, "invalid secret key data"),
38            Self::InvalidEncapKeyFormat => write!(f, "invalid public key data"),
39            Self::Encap => write!(f, "encapsulation failed"),
40            Self::Decapsulation => write!(f, "unable to decapsulate symmetric key"),
41            Self::DhKem(err) => write!(f, "{}", err),
42            Self::Import(err) => write!(f, "{}", err),
43            Self::PublicKey(err) => write!(f, "{}", err),
44        }
45    }
46}
47
48impl core::error::Error for KemError {
49    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
50        match self {
51            Self::DhKem(err) => Some(err),
52            Self::Import(err) => Some(err),
53            _ => None,
54        }
55    }
56}
57
58impl From<DhKemError> for KemError {
59    fn from(err: DhKemError) -> Self {
60        Self::DhKem(err)
61    }
62}
63
64impl From<ImportError> for KemError {
65    fn from(err: ImportError) -> Self {
66        Self::Import(err)
67    }
68}
69
70impl From<PkError> for KemError {
71    fn from(err: PkError) -> Self {
72        Self::PublicKey(err)
73    }
74}
75
76/// A Key Encapsulation Mechanism (KEM).
77///
78/// # Requirements
79///
80/// The KEM must:
81///
82/// * Have at least a 128-bit security level.
83#[allow(non_snake_case)]
84pub trait Kem {
85    /// A local secret (private) key used to decapsulate secrets.
86    type DecapKey: DecapKey<EncapKey = Self::EncapKey>;
87    /// A remote public key used to encapsulate secrets.
88    type EncapKey: EncapKey;
89
90    /// An ephemeral, fixed-length symmetric key.
91    ///
92    /// The key must be at least 128 bits.
93    type Secret: RawSecretBytes + ZeroizeOnDrop;
94
95    /// An encapsulated [`Self::Secret`].
96    type Encap: Borrow<[u8]> + for<'a> Import<&'a [u8]>;
97
98    /// A randomized algorithm that generates an ephemeral,
99    /// fixed-length symmetric key and an 'encapsulation' of that
100    /// key that can be decrypted by the holder of the private
101    /// half of the public key.
102    fn encap<R: Csprng>(
103        rng: &mut R,
104        pkR: &Self::EncapKey,
105    ) -> Result<(Self::Secret, Self::Encap), KemError>;
106
107    /// A deterministic algorithm that generates an ephemeral,
108    /// fixed-length symmetric key and an 'encapsulation' of that
109    /// key that can be decrypted by the holder of the private
110    /// half of the public key.
111    ///
112    /// # Warning
113    ///
114    /// The security of this function relies on choosing the
115    /// correct value for `skE`. It is a catastrophic error if
116    /// you do not ensure all of the following properties:
117    ///
118    /// - it must be cryptographically secure
119    /// - it must never be reused with the same `pkR`
120    fn encap_deterministically(
121        pkR: &Self::EncapKey,
122        skE: Self::DecapKey,
123    ) -> Result<(Self::Secret, Self::Encap), KemError>;
124
125    /// A deterministic algorithm that recovers the ephemeral
126    /// symmetric key from its encapsulated representation.
127    fn decap(enc: &Self::Encap, skR: &Self::DecapKey) -> Result<Self::Secret, KemError>;
128
129    /// An authenticated, randomized algorithm that generates an
130    /// ephemeral, fixed-length symmetric key and an
131    /// 'encapsulation' of that key that can be decrypted by the
132    /// holder of the private half of the public key.
133    ///
134    /// This function is identical to [`Kem::encap`] except that
135    /// it uses `skS` to encode an assurance that the shared
136    /// secret was generated by the holder of `skS`.
137    fn auth_encap<R: Csprng>(
138        rng: &mut R,
139        pkR: &Self::EncapKey,
140        skS: &Self::DecapKey,
141    ) -> Result<(Self::Secret, Self::Encap), KemError>;
142
143    /// An authenticated, deterministic algorithm that generates
144    /// an ephemeral, fixed-length symmetric key and an
145    /// 'encapsulation' of that key that can be decrypted by the
146    /// holder of the private half of the public key.
147    ///
148    /// This function is identical to
149    /// [`Kem::encap_deterministically`] except that it uses
150    /// `skS` to encode an assurance that the shared secret was
151    /// generated by the holder of `skS`.
152    ///
153    /// # Warning
154    ///
155    /// The security of this function relies on choosing the
156    /// correct value for `skE`. It is a catastrophic error if
157    /// you do not ensure all of the following properties:
158    ///
159    /// - it must be cryptographically secure
160    /// - it must never be reused with the same `pkR`
161    fn auth_encap_deterministically(
162        pkR: &Self::EncapKey,
163        skS: &Self::DecapKey,
164        skE: Self::DecapKey,
165    ) -> Result<(Self::Secret, Self::Encap), KemError>;
166
167    /// An authenticated, deterministic algorithm that recovers
168    /// the ephemeral symmetric key from its encapsulated
169    /// representation.
170    ///
171    /// This function is identical to [`Kem::decap`] except that
172    /// it uses `pkS` to ensure that the shared secret was
173    /// generated by the holder of the private half of `pkS`.
174    fn auth_decap(
175        enc: &Self::Encap,
176        skR: &Self::DecapKey,
177        pkS: &Self::EncapKey,
178    ) -> Result<Self::Secret, KemError>;
179}
180
181/// An asymmetric private key used to decapsulate keys.
182pub trait DecapKey: SecretKey + Random {
183    /// The corresponding public key.
184    type EncapKey: EncapKey;
185
186    /// Returns the public half of the key.
187    fn public(&self) -> Result<Self::EncapKey, PkError>;
188}
189
190/// An asymmetric public key used to encapsulate keys.
191pub trait EncapKey: PublicKey {}
192
193/// An error from an [`Ecdh`].
194#[derive(Debug, Eq, PartialEq)]
195pub enum EcdhError {
196    /// An unknown or internal error has occurred.
197    Other(&'static str),
198}
199
200impl fmt::Display for EcdhError {
201    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202        match self {
203            Self::Other(msg) => write!(f, "{}", msg),
204        }
205    }
206}
207
208impl core::error::Error for EcdhError {}
209
210/// Elliptic Curve Diffie Hellman key exchange.
211///
212/// # Requirements
213///
214/// The algorithm must:
215///
216/// * Have at least a 128-bit security level
217pub trait Ecdh {
218    /// The size in bytes of a scalar.
219    const SCALAR_SIZE: usize;
220
221    /// An ECDH private key.
222    type PrivateKey: DecapKey<EncapKey = Self::PublicKey>;
223    /// An ECDH public key.
224    ///
225    /// For NIST's prime order curves (P-256, P-384, and P-521),
226    /// its [`PublicKey::export`] method must return the
227    /// uncompressed format described in [SEC] section 2.3.3.
228    ///
229    /// For X25519 and X448, its [`PublicKey::export`] method
230    /// must return the point as-is.
231    ///
232    /// For all other elliptic curves the format is unspecified.
233    ///
234    /// [SEC]: https://secg.org/sec1-v2.pdf
235    type PublicKey: EncapKey;
236
237    /// The shared secret (Diffie-Hellman value) computed as the
238    /// result of the key exchange ([`Self::ecdh`]).
239    type SharedSecret: Borrow<[u8]> + ZeroizeOnDrop;
240
241    /// Performs ECDH with the local secret key and remote public
242    /// key.
243    fn ecdh(
244        local: &Self::PrivateKey,
245        remote: &Self::PublicKey,
246    ) -> Result<Self::SharedSecret, EcdhError>;
247}
248
249/// An ECDH shared secret.
250pub struct SharedSecret<const N: usize>([u8; N]);
251
252impl<const N: usize> SharedSecret<N> {
253    /// Returns a pointer to the shared secret.
254    pub fn as_mut_ptr(&mut self) -> *mut u8 {
255        self.0.as_mut_ptr()
256    }
257
258    /// Returns the length of the shared secret.
259    #[allow(clippy::len_without_is_empty)]
260    pub const fn len(&self) -> usize {
261        self.0.len()
262    }
263}
264
265impl<const N: usize> Default for SharedSecret<N> {
266    fn default() -> Self {
267        Self([0u8; N])
268    }
269}
270
271impl<const N: usize> Borrow<[u8]> for SharedSecret<N> {
272    fn borrow(&self) -> &[u8] {
273        &self.0
274    }
275}
276
277impl<const N: usize> TryFrom<&[u8]> for SharedSecret<N> {
278    type Error = TryFromSliceError;
279
280    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
281        Ok(Self(data.try_into()?))
282    }
283}
284
285impl<const N: usize> fmt::Debug for SharedSecret<N> {
286    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287        f.debug_tuple("SharedSecret").finish_non_exhaustive()
288    }
289}
290
291impl<const N: usize> ZeroizeOnDrop for SharedSecret<N> {}
292impl<const N: usize> Drop for SharedSecret<N> {
293    fn drop(&mut self) {
294        // SAFETY:
295        // - `self.0` does not contain references or dynamically
296        //   sized data.
297        // - `self.0` does not have a `Drop` impl.
298        // - `self.0` is not used after this function returns.
299        // - The bit pattern of all zeros is valid for `self.0`.
300        unsafe { zeroize_flat_type(&mut self.0) }
301    }
302}
303
304/// An error from a DHKEM.
305#[derive(Debug, Eq, PartialEq)]
306pub enum DhKemError {
307    /// An ECDH operation failed.
308    Ecdh(EcdhError),
309    /// A KDF operation failed.
310    Kdf(KdfError),
311    /// A DH key could not be imported.
312    Import(ImportError),
313}
314
315impl fmt::Display for DhKemError {
316    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317        match self {
318            Self::Ecdh(err) => write!(f, "{}", err),
319            Self::Kdf(err) => write!(f, "{}", err),
320            Self::Import(err) => write!(f, "{}", err),
321        }
322    }
323}
324
325impl core::error::Error for DhKemError {
326    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
327        match self {
328            Self::Ecdh(err) => Some(err),
329            Self::Kdf(err) => Some(err),
330            Self::Import(err) => Some(err),
331        }
332    }
333}
334
335/// Implements [`Kem`] for an [`Ecdh`] and [`Kdf`].
336pub struct DhKem<E, F> {
337    id: KemId,
338    _e: PhantomData<E>,
339    _f: PhantomData<F>,
340}
341
342#[allow(non_snake_case)]
343impl<E: Ecdh, F: Kdf> DhKem<E, F> {
344    /// Creates a new [`DhKem`] with the provided [`KemId`].
345    pub fn new(id: KemId) -> Self {
346        Self {
347            id,
348            _e: PhantomData,
349            _f: PhantomData,
350        }
351    }
352
353    /// See [`Kem::encap`].
354    pub fn encap<R: Csprng>(
355        &self,
356        rng: &mut R,
357        pkR: &E::PublicKey,
358    ) -> Result<(Prk<F::PrkSize>, PubKeyData<E>), KemError> {
359        let skE = E::PrivateKey::random(rng);
360        self.encap_deterministically(pkR, skE)
361    }
362
363    /// See [`Kem::encap_deterministically`].
364    pub fn encap_deterministically(
365        &self,
366        pkR: &E::PublicKey,
367        skE: E::PrivateKey,
368    ) -> Result<(Prk<F::PrkSize>, PubKeyData<E>), KemError> {
369        // def Encap(pkR):
370        //   skE, pkE = GenerateKeyPair()
371        //   dh = DH(skE, pkR)
372        //   enc = SerializePublicKey(pkE)
373        //
374        //   pkRm = SerializePublicKey(pkR)
375        //   kem_context = concat(enc, pkRm)
376        //
377        //   shared_secret = ExtractAndExpand(dh, kem_context)
378        //   return shared_secret, enc
379        let pkE = skE.public()?;
380        let dh = (E::ecdh(&skE, pkR).map_err(DhKemError::Ecdh)?, None);
381        let enc = pkE.export();
382
383        let pkRm = pkR.export();
384
385        let shared_secret =
386            Self::extract_and_expand(&dh, &enc, &pkRm, None, self.id).map_err(DhKemError::Kdf)?;
387        Ok((shared_secret, enc))
388    }
389
390    /// See [`Kem::decap`].
391    pub fn decap(
392        &self,
393        enc: &PubKeyData<E>,
394        skR: &E::PrivateKey,
395    ) -> Result<Prk<F::PrkSize>, KemError> {
396        // def Decap(enc, skR):
397        //   pkE = DeserializePublicKey(enc)
398        //   dh = DH(skR, pkE)
399        //
400        //   pkRm = SerializePublicKey(pk(skR))
401        //   kem_context = concat(enc, pkRm)
402        //
403        //   shared_secret = ExtractAndExpand(dh, kem_context)
404        //   return shared_secret
405        let pkE = E::PublicKey::import(enc.borrow())?;
406        let dh = (E::ecdh(skR, &pkE).map_err(DhKemError::Ecdh)?, None);
407
408        let pkRm = skR.public()?.export();
409
410        let shared_secret =
411            Self::extract_and_expand(&dh, enc, &pkRm, None, self.id).map_err(DhKemError::Kdf)?;
412        Ok(shared_secret)
413    }
414
415    /// See [`Kem::auth_encap`].
416    pub fn auth_encap<R: Csprng>(
417        &self,
418        rng: &mut R,
419        pkR: &E::PublicKey,
420        skS: &E::PrivateKey,
421    ) -> Result<(Prk<F::PrkSize>, PubKeyData<E>), KemError> {
422        let skE = E::PrivateKey::random(rng);
423        self.auth_encap_deterministically(pkR, skS, skE)
424    }
425
426    /// See [`Kem::auth_encap_deterministically`].
427    pub fn auth_encap_deterministically(
428        &self,
429        pkR: &E::PublicKey,
430        skS: &E::PrivateKey,
431        skE: E::PrivateKey,
432    ) -> Result<(Prk<F::PrkSize>, PubKeyData<E>), KemError> {
433        // def AuthEncap(pkR, skS):
434        //   skE, pkE = GenerateKeyPair()
435        //   dh = concat(DH(skE, pkR), DH(skS, pkR))
436        //   enc = SerializePublicKey(pkE)
437        //
438        //   pkRm = SerializePublicKey(pkR)
439        //   pkSm = SerializePublicKey(pk(skS))
440        //   kem_context = concat(enc, pkRm, pkSm)
441        //
442        //   shared_secret = ExtractAndExpand(dh, kem_context)
443        //   return shared_secret, enc
444        let pkE = skE.public()?;
445        let dh = (
446            E::ecdh(&skE, pkR).map_err(DhKemError::Ecdh)?,
447            Some(E::ecdh(skS, pkR).map_err(DhKemError::Ecdh)?),
448        );
449        let enc = pkE.export();
450
451        let pkRm = pkR.export();
452        let pkSm = skS.public()?.export();
453
454        let shared_secret = Self::extract_and_expand(&dh, &enc, &pkRm, Some(&pkSm), self.id)
455            .map_err(DhKemError::Kdf)?;
456        Ok((shared_secret, enc))
457    }
458
459    /// See [`Kem::auth_decap`].
460    pub fn auth_decap(
461        &self,
462        enc: &PubKeyData<E>,
463        skR: &E::PrivateKey,
464        pkS: &E::PublicKey,
465    ) -> Result<Prk<F::PrkSize>, KemError> {
466        // def AuthDecap(enc, skR, pkS):
467        //   pkE = DeserializePublicKey(enc)
468        //   dh = concat(DH(skR, pkE), DH(skR, pkS))
469        //
470        //   pkRm = SerializePublicKey(pk(skR))
471        //   pkSm = SerializePublicKey(pkS)
472        //   kem_context = concat(enc, pkRm, pkSm)
473        //
474        //   shared_secret = ExtractAndExpand(dh, kem_context)
475        //   return shared_secret
476        let pkE = E::PublicKey::import(enc.borrow())?;
477        let dh = (
478            E::ecdh(skR, &pkE).map_err(DhKemError::Ecdh)?,
479            Some(E::ecdh(skR, pkS).map_err(DhKemError::Ecdh)?),
480        );
481
482        let pkRm = skR.public()?.export();
483        let pkSm = pkS.export();
484
485        let shared_secret = Self::extract_and_expand(&dh, enc, &pkRm, Some(&pkSm), self.id)
486            .map_err(DhKemError::Kdf)?;
487        Ok(shared_secret)
488    }
489
490    /// Performs `ExtractAndExpand`.
491    fn extract_and_expand(
492        dh: &(E::SharedSecret, Option<E::SharedSecret>),
493        enc: &PubKeyData<E>,
494        pkRm: &PubKeyData<E>,
495        pkSm: Option<&PubKeyData<E>>,
496        id: KemId,
497    ) -> Result<Prk<F::PrkSize>, KdfError> {
498        // def LabeledExtract(salt, label, ikm):
499        //   ...
500        // def LabeledExpand(prk, label, info, L):
501        //   ...
502        //
503        // def ExtractAndExpand(dh, kem_context):
504        //   eae_prk = LabeledExtract("", "eae_prk", dh)
505        //   shared_secret = LabeledExpand(eae_prk, "shared_secret",
506        //                                 kem_context, Nsecret)
507        //   return shared_secret
508        let mut out = Prk::<F::PrkSize>::default();
509
510        // NB: `labeled_ikm` and `labeled_info` are slices
511        // instead of arrays to cut down on stack usage.
512        // Additionally, `labeled_info` needs to be `Clone`, and
513        // it's significantly cheaper to clone `slice::Iter` that
514        // it is to clone `array::IntoIter`.
515
516        //  labeled_ikm = concat("HPKE-v1", suite_id, label, ikm)
517        let labeled_ikm: &[&[u8]] = &[
518            b"HPKE-v1",
519            // suite_id = concat("KEM", I2OSP(kem_id, 2))
520            b"KEM",
521            &id.to_be_bytes(),
522            // label
523            b"eae_prk",
524            // ikm
525            dh.0.borrow(),
526            dh.1.as_ref().map_or(&[], |v| v.borrow()),
527        ];
528
529        //  labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id,
530        //                 label, info)
531        let labeled_info: &[&[u8]] = &[
532            &(F::PRK_SIZE as u16).to_be_bytes()[..],
533            b"HPKE-v1",
534            // suite_id = concat("KEM", I2OSP(kem_id, 2))
535            b"KEM",
536            &id.to_be_bytes(),
537            // label
538            b"shared_secret",
539            // kem_context
540            enc.borrow(),
541            pkRm.borrow(),
542            pkSm.map_or(&[], |v| v.borrow()),
543        ];
544
545        F::extract_and_expand_multi(
546            out.as_bytes_mut(),
547            labeled_ikm.iter().copied(),
548            &[],
549            labeled_info.iter().copied(),
550        )?;
551        Ok(out)
552    }
553}
554
555impl<E, F> fmt::Debug for DhKem<E, F> {
556    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557        f.debug_struct("DhKem").field("id", &self.id).finish()
558    }
559}
560
561type PubKeyData<T> = <<T as Ecdh>::PublicKey as PublicKey>::Data;
562
563/// Implement [`Kem`] for an [`Ecdh`].
564///
565/// - `name`: The name of the resulting [`Kem`] impl.
566/// - `doc`: A string to use for documentation.
567/// - `ecdh`: The [`Ecdh`].
568/// - `kdf`: The [`Kdf`][crate::kdf::Kdf] to use.
569/// - `sk`: The [`DecapKey`] to use.
570/// - `pk`: The [`EncapKey`] to use.
571///
572/// # Example
573///
574/// ```rust,ignore
575/// use spideroak_crypto::dhkem_impl;
576/// dhkem_impl! {
577///     DhKemP256HkdfSha256,
578///     "DHKEM(P256, HKDF-SHA256)",
579///     P256,
580///     HkdfSha256,
581///     P256PrivateKey,
582///     P256PublicKey,
583/// }
584/// ```
585#[macro_export]
586macro_rules! dhkem_impl {
587    (
588        $name:ident,
589        $doc:expr,
590        $kem_id:expr,
591        $ecdh:ty,
592        $kdf:ty,
593        $sk:ident,
594        $pk:ident
595        $(, oid = $oid:ident)?
596        $(,)?
597    ) => {
598        #[doc = concat!($doc, ".")]
599        #[derive(Debug)]
600        pub struct $name;
601
602        impl $name {
603            const KEM_ID: $crate::hpke::KemId = $kem_id;
604        }
605
606        #[allow(non_snake_case)]
607        impl $crate::kem::Kem for $name {
608            type DecapKey = $sk;
609            type EncapKey = $pk;
610            type Secret = $crate::kdf::Prk<<$kdf as $crate::kdf::Kdf>::PrkSize>;
611            type Encap = <$pk as $crate::keys::PublicKey>::Data;
612
613            fn encap<R: $crate::csprng::Csprng>(
614                rng: &mut R,
615                pkR: &Self::EncapKey,
616            ) -> ::core::result::Result<(Self::Secret, Self::Encap), $crate::kem::KemError> {
617                $crate::kem::DhKem::<$ecdh, $kdf>::new(Self::KEM_ID).encap(rng, pkR)
618            }
619
620            fn encap_deterministically(
621                pkR: &Self::EncapKey,
622                skE: Self::DecapKey,
623            ) -> ::core::result::Result<(Self::Secret, Self::Encap), $crate::kem::KemError> {
624                $crate::kem::DhKem::<$ecdh, $kdf>::new(Self::KEM_ID)
625                    .encap_deterministically(pkR, skE)
626            }
627
628            fn decap(
629                enc: &Self::Encap,
630                skR: &Self::DecapKey,
631            ) -> ::core::result::Result<Self::Secret, $crate::kem::KemError> {
632                $crate::kem::DhKem::<$ecdh, $kdf>::new(Self::KEM_ID).decap(enc, skR)
633            }
634
635            fn auth_encap<R: $crate::csprng::Csprng>(
636                rng: &mut R,
637                pkR: &Self::EncapKey,
638                skS: &Self::DecapKey,
639            ) -> ::core::result::Result<(Self::Secret, Self::Encap), $crate::kem::KemError> {
640                $crate::kem::DhKem::<$ecdh, $kdf>::new(Self::KEM_ID).auth_encap(rng, pkR, skS)
641            }
642
643            fn auth_encap_deterministically(
644                pkR: &Self::EncapKey,
645                skS: &Self::DecapKey,
646                skE: Self::DecapKey,
647            ) -> ::core::result::Result<(Self::Secret, Self::Encap), $crate::kem::KemError> {
648                $crate::kem::DhKem::<$ecdh, $kdf>::new(Self::KEM_ID)
649                    .auth_encap_deterministically(pkR, skS, skE)
650            }
651
652            fn auth_decap(
653                enc: &Self::Encap,
654                skR: &Self::DecapKey,
655                pkS: &Self::EncapKey,
656            ) -> ::core::result::Result<Self::Secret, $crate::kem::KemError> {
657                $crate::kem::DhKem::<$ecdh, $kdf>::new(Self::KEM_ID).auth_decap(enc, skR, pkS)
658            }
659        }
660
661        impl $crate::hpke::HpkeKem for $name {
662            const ID: $crate::hpke::KemId = Self::KEM_ID;
663        }
664
665        $(impl $crate::oid::Identified for $name {
666            const OID: &'static $crate::oid::Oid = $oid;
667        })?
668    };
669}
670pub(crate) use dhkem_impl;