From a3f9de83d63003f1513b1f56bf56e9bc21791c96 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 5 Feb 2026 08:42:08 -0700 Subject: [PATCH] kem: move `Kem` for `*Decapsulate`/`Encapsulate` to associated type Also adds (back) a `Decapsulator` trait. This is effectively a followup to #2243 which added the `Kem` trait but made it a generic argument to `*Decapsulate`/`Encapsulate`, and removed the previous `Decapsulator` trait. Having it be a generic argument is annoying: these types should simply know what KEM algorithm they implement. The only reason I didn't do it that way at the time was I was having trouble making it work, and that was a fairly large refactor to begin with. This adds an associated `Kem` to `Encapsulate`, but for `Decapsulate`/`TryDecapsulate` we need a common trait to hang it off of, so this adds back the `Decapsulator` trait. It's not so bad as the previous hack introduced in #2243 of using `AsRef` to obtain an `EncapsulationKey` from a `Decapsulator` has been replaced with a semantically clear `Decapsulator::encapsulation_key` method. --- kem/src/lib.rs | 50 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/kem/src/lib.rs b/kem/src/lib.rs index da02d50aa..ccdadc51d 100644 --- a/kem/src/lib.rs +++ b/kem/src/lib.rs @@ -43,11 +43,11 @@ pub type Ciphertext = array::Array::CiphertextSize>; pub trait Kem: Copy + Clone + Debug + Default + Eq + Ord + Send + Sync + 'static { /// KEM decryption key (i.e. private key) which can decrypt encrypted shared secret ciphertexts /// which were encrypted by [`Kem::EncapsulationKey`]. - type DecapsulationKey: TryDecapsulate + Generate; + type DecapsulationKey: TryDecapsulate + Generate; /// KEM encryption key (i.e. public key) which encrypts shared secrets into ciphertexts which /// can be decrypted by [`Kem::DecapsulationKey`]. - type EncapsulationKey: Encapsulate + Clone + Debug + Eq; + type EncapsulationKey: Encapsulate + Clone + Debug + Eq; /// Size of the shared key/secret returned by both encapsulation and decapsulation. type SharedKeySize: ArraySize; @@ -60,7 +60,7 @@ pub trait Kem: Copy + Clone + Debug + Default + Eq + Ord + Send + Sync + 'static rng: &mut R, ) -> (Self::DecapsulationKey, Self::EncapsulationKey) { let dk = Self::DecapsulationKey::generate_from_rng(rng); - let ek = dk.as_ref().clone(); + let ek = dk.encapsulation_key().clone(); (dk, ek) } @@ -75,20 +75,33 @@ pub trait Kem: Copy + Clone + Debug + Default + Eq + Ord + Send + Sync + 'static /// /// Often, this will just be a public key. However, it can also be a bundle of public keys, or it /// can include a sender's private key for authenticated encapsulation. -pub trait Encapsulate: TryKeyInit + KeyExport { +pub trait Encapsulate: TryKeyInit + KeyExport { + /// KEM algorithm this encapsulator is for. + type Kem: Kem; + /// Encapsulates a fresh [`SharedKey`] generated using the supplied random number /// generator `R`. - fn encapsulate_with_rng(&self, rng: &mut R) -> (Ciphertext, SharedKey) + fn encapsulate_with_rng(&self, rng: &mut R) -> (Ciphertext, SharedKey) where R: CryptoRng + ?Sized; /// Encapsulate a fresh shared secret generated using the system's secure RNG. #[cfg(feature = "getrandom")] - fn encapsulate(&self) -> (Ciphertext, SharedKey) { + fn encapsulate(&self) -> (Ciphertext, SharedKey) { self.encapsulate_with_rng(&mut UnwrapErr(SysRng)) } } +/// Decapsulator with an associated encapsulation key which can be used for encrypting shared keys +/// that this decapsulator can decrypt. +pub trait Decapsulator { + /// KEM algorithm this decapsulator is for. + type Kem: Kem; + + /// Encapsulation key which can encrypt ciphertexts this decapsulator can decrypt. + fn encapsulation_key(&self) -> &EncapsulationKey; +} + /// Decapsulator for encapsulated keys, with an associated `Encapsulator` bounded by the /// [`Encapsulate`] trait. /// @@ -98,15 +111,15 @@ pub trait Encapsulate: TryKeyInit + KeyExport { /// /// When possible (i.e. for software / non-HSM implementations) types which impl this trait should /// also impl the [`Generate`] trait to support key generation. -pub trait Decapsulate: TryDecapsulate { +pub trait Decapsulate: TryDecapsulate { /// Decapsulates the given [`Ciphertext`] a.k.a. "encapsulated key". - fn decapsulate(&self, ct: &Ciphertext) -> SharedKey; + fn decapsulate(&self, ct: &Ciphertext) -> SharedKey; /// Decapsulate the given byte slice containing a [`Ciphertext`] a.k.a. "encapsulated key". /// /// # Errors /// - If the length of `ct` is not equal to `::CiphertextSize`. - fn decapsulate_slice(&self, ct: &[u8]) -> Result, TryFromSliceError> { + fn decapsulate_slice(&self, ct: &[u8]) -> Result, TryFromSliceError> { ct.try_into().map(|ct| self.decapsulate(&ct)) } } @@ -116,18 +129,21 @@ pub trait Decapsulate: TryDecapsulate { /// /// Prefer to implement the [`Decapsulate`] trait if possible. See that trait's documentation for /// more information. -pub trait TryDecapsulate: AsRef { +pub trait TryDecapsulate: Decapsulator { /// Decapsulation error type Error: core::error::Error; /// Decapsulates the given [`Ciphertext`] a.k.a. "encapsulated key". - fn try_decapsulate(&self, ct: &Ciphertext) -> Result, Self::Error>; + fn try_decapsulate( + &self, + ct: &Ciphertext, + ) -> Result, Self::Error>; /// Decapsulate the given byte slice containing a [`Ciphertext`] a.k.a. "encapsulated key". /// /// # Errors /// - If the length of `ct` is not equal to `::CiphertextSize`. - fn try_decapsulate_slice(&self, ct: &[u8]) -> Result, Self::Error> + fn try_decapsulate_slice(&self, ct: &[u8]) -> Result, Self::Error> where Self::Error: From, { @@ -135,14 +151,16 @@ pub trait TryDecapsulate: AsRef { } } -impl TryDecapsulate for D +impl TryDecapsulate for D where - D: Decapsulate, - K: Kem, + D: Decapsulate + Decapsulator, { type Error = Infallible; - fn try_decapsulate(&self, ct: &Ciphertext) -> Result, Infallible> { + fn try_decapsulate( + &self, + ct: &Ciphertext, + ) -> Result, Infallible> { Ok(self.decapsulate(ct)) } }