From 6ab8c0eaf19234210b6276cf97492afe68737204 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Wed, 28 Jan 2026 17:27:49 -0700 Subject: [PATCH 1/4] [WIP] kem: `Kem` trait for the whole algorithm type family Adds a `Kem` trait intended to be impl'd by ZSTs that describe the whole type family, including the decapsulation key, encapsulation key, and the sizes of the ciphertext and shared secret previously defined on the `KemParams` trait (which this subsumes). The remaining traits have been changed to be generic around `K: Kem` and now use it to source their constants or relevant types. `DecapsulationKey` and `EncapsulationKey` type aliases have been added, similar to the ones in `elliptic-curve`, which take care of doing `K as Kem` for you when accessing the associated types. The design of this trait largely matches the bespoke traits that people were defining different but similarly shaped-versions of in individual crates in https://github.com/RustCrypto/KEMs --- kem/src/lib.rs | 93 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 37 deletions(-) diff --git a/kem/src/lib.rs b/kem/src/lib.rs index cbb57226b..6da84b512 100644 --- a/kem/src/lib.rs +++ b/kem/src/lib.rs @@ -13,72 +13,90 @@ pub use common::{ }; use common::array::{self, ArraySize}; +use core::fmt::Debug; use core::{array::TryFromSliceError, convert::Infallible}; use rand_core::CryptoRng; #[cfg(feature = "getrandom")] use common::getrandom::{SysRng, rand_core::UnwrapErr}; +/// KEM decryption key (i.e. private key) which can decrypt encrypted shared secret ciphertexts +/// which were encrypted by [`EncapsulationKey`]. +pub type DecapsulationKey = ::DecapsulationKey; + +/// KEM encryption key (i.e. public key) which encrypts shared secrets into ciphertexts which +/// can be decrypted by [`DecapsulationKey`]. +pub type EncapsulationKey = ::EncapsulationKey; + /// Ciphertext message (a.k.a. "encapsulated key") produced by [`Encapsulate::encapsulate`] which is /// an encrypted [`SharedSecret`] that can be decrypted using [`Decapsulate::decapsulate`]. /// -/// `K` is expected to be a type that impls [`KemParams`], such as an encapsulator or decapsulator. -pub type Ciphertext = array::Array::CiphertextSize>; +/// `K` is expected to be a type that impls [`Kem`], such as an encapsulator or decapsulator. +pub type Ciphertext = array::Array::CiphertextSize>; /// Shared secret: plaintext produced after decapsulation by [`Decapsulate::decapsulate`] which is /// also returned by [`Encapsulate::encapsulate`]. /// -/// `K` is expected to be a type that impls [`KemParams`], such as an encapsulator or decapsulator. -pub type SharedSecret = array::Array::SharedSecretSize>; +/// `K` is expected to be a type that impls [`Kem`], such as an encapsulator or decapsulator. +pub type SharedSecret = array::Array::SharedSecretSize>; -/// Key encapsulation mechanism parameters: sizes of the ciphertext and decrypted plaintext. +/// Key encapsulation mechanism. /// -/// This trait is impl'd by types that impl either [`Encapsulate`] or [`Decapsulate`] and defines -/// the sizes of the encapsulated key and shared secret. -pub trait KemParams { - /// Size of the ciphertext (a.k.a. "encapsulated key") produced by [`Encapsulate::encapsulate`]. +/// This trait describes the entire type family used by a KEM. +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: Decapsulate + 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 + for<'a> From<&'a Self::DecapsulationKey>; + + /// Size of the ciphertext (a.k.a. "encapsulated key") produced by [`Self::EncapsulationKey`]. type CiphertextSize: ArraySize; - /// Size of the shared secret after decapsulation by [`Decapsulate::decapsulate`]. + /// Size of the shared secret after decapsulation by [`Self::DecapsulationKey`]. type SharedSecretSize: ArraySize; + + /// Generate a random KEM keypair using the provided random number generator. + fn generate_keypair_from_rng( + rng: &mut R, + ) -> (Self::DecapsulationKey, Self::EncapsulationKey) { + let dk = Self::DecapsulationKey::generate_from_rng(rng); + let ek = Self::EncapsulationKey::from(&dk); + (dk, ek) + } + + /// Generate a random KEM keypair using the system's secure RNG. + #[cfg(feature = "getrandom")] + fn generate_keypair(&self) -> (Self::DecapsulationKey, Self::EncapsulationKey) { + Self::generate_keypair_from_rng(&mut UnwrapErr(SysRng)) + } } /// Encapsulator for shared secrets. /// /// 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: KemParams + TryKeyInit + KeyExport { +pub trait Encapsulate: TryKeyInit + KeyExport { /// Encapsulates a fresh [`SharedSecret`] generated using the supplied random number /// generator `R`. - fn encapsulate_with_rng(&self, rng: &mut R) -> (Ciphertext, SharedSecret) + fn encapsulate_with_rng(&self, rng: &mut R) -> (Ciphertext, SharedSecret) where R: CryptoRng + ?Sized; /// Encapsulate a fresh shared secret generated using the system's secure RNG. #[cfg(feature = "getrandom")] - fn encapsulate(&self) -> (Ciphertext, SharedSecret) { + fn encapsulate(&self) -> (Ciphertext, SharedSecret) { self.encapsulate_with_rng(&mut UnwrapErr(SysRng)) } } /// Trait for decapsulators, which is a supertrait bound of both [`Decapsulate`] and /// [`TryDecapsulate`]. -pub trait Decapsulator: - KemParams< - CiphertextSize = ::CiphertextSize, - SharedSecretSize = ::SharedSecretSize, - > -{ - /// Encapsulator which corresponds to this decapsulator. - type Encapsulator: Encapsulate + Clone + KemParams; - +pub trait Decapsulator { /// Retrieve the encapsulator associated with this decapsulator. - fn encapsulator(&self) -> &Self::Encapsulator; -} - -impl KemParams for K { - type CiphertextSize = ::CiphertextSize; - type SharedSecretSize = ::SharedSecretSize; + fn encapsulator(&self) -> &K::EncapsulationKey; } /// Decapsulator for encapsulated keys, with an associated `Encapsulator` bounded by the @@ -90,15 +108,15 @@ impl KemParams for K { /// /// 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: Decapsulator + TryDecapsulate { +pub trait Decapsulate: Decapsulator + TryDecapsulate { /// Decapsulates the given [`Ciphertext`] a.k.a. "encapsulated key". - fn decapsulate(&self, ct: &Ciphertext) -> SharedSecret; + fn decapsulate(&self, ct: &Ciphertext) -> SharedSecret; /// 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)) } } @@ -108,18 +126,18 @@ pub trait Decapsulate: Decapsulator + TryDecapsulate { /// /// Prefer to implement the [`Decapsulate`] trait if possible. See that trait's documentation for /// more information. -pub trait TryDecapsulate: Decapsulator { +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, { @@ -127,13 +145,14 @@ pub trait TryDecapsulate: Decapsulator { } } -impl TryDecapsulate for D +impl TryDecapsulate for D where - D: Decapsulate, + D: Decapsulate, + K: Kem, { type Error = Infallible; - fn try_decapsulate(&self, ct: &Ciphertext) -> Result, Infallible> { + fn try_decapsulate(&self, ct: &Ciphertext) -> Result, Infallible> { Ok(self.decapsulate(ct)) } } From 1c1a1d033a45868681730a3534c57a271a8bccc2 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 30 Jan 2026 09:05:49 -0700 Subject: [PATCH 2/4] Tweaks based on attempted usage - Bound `DecapsulationKey` on `TryDecapsulate` as it's the supertrait - Remove `From<&Self::DecapsulationKey>` bound - Remove `Decapsulator` trait, replace it with an `AsRef` bound on `TryDecapsulate` --- kem/src/lib.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/kem/src/lib.rs b/kem/src/lib.rs index 6da84b512..8d99f012d 100644 --- a/kem/src/lib.rs +++ b/kem/src/lib.rs @@ -46,11 +46,11 @@ pub type SharedSecret = array::Array::SharedSecretSize>; 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: Decapsulate + 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 + for<'a> From<&'a Self::DecapsulationKey>; + type EncapsulationKey: Encapsulate + Clone; /// Size of the ciphertext (a.k.a. "encapsulated key") produced by [`Self::EncapsulationKey`]. type CiphertextSize: ArraySize; @@ -63,7 +63,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 = Self::EncapsulationKey::from(&dk); + let ek = dk.as_ref().clone(); (dk, ek) } @@ -92,13 +92,6 @@ pub trait Encapsulate: TryKeyInit + KeyExport { } } -/// Trait for decapsulators, which is a supertrait bound of both [`Decapsulate`] and -/// [`TryDecapsulate`]. -pub trait Decapsulator { - /// Retrieve the encapsulator associated with this decapsulator. - fn encapsulator(&self) -> &K::EncapsulationKey; -} - /// Decapsulator for encapsulated keys, with an associated `Encapsulator` bounded by the /// [`Encapsulate`] trait. /// @@ -108,7 +101,7 @@ pub trait Decapsulator { /// /// 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: Decapsulator + TryDecapsulate { +pub trait Decapsulate: TryDecapsulate { /// Decapsulates the given [`Ciphertext`] a.k.a. "encapsulated key". fn decapsulate(&self, ct: &Ciphertext) -> SharedSecret; @@ -126,7 +119,7 @@ pub trait Decapsulate: Decapsulator + TryDecapsulate: Decapsulator { +pub trait TryDecapsulate: AsRef { /// Decapsulation error type Error: core::error::Error; From 05372af5eb21aca46cb1ea05772258737ab25913 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 30 Jan 2026 09:37:01 -0700 Subject: [PATCH 3/4] Rename SharedSecret -> SharedKey --- kem/src/lib.rs | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/kem/src/lib.rs b/kem/src/lib.rs index 8d99f012d..f35df71dd 100644 --- a/kem/src/lib.rs +++ b/kem/src/lib.rs @@ -21,25 +21,22 @@ use rand_core::CryptoRng; use common::getrandom::{SysRng, rand_core::UnwrapErr}; /// KEM decryption key (i.e. private key) which can decrypt encrypted shared secret ciphertexts -/// which were encrypted by [`EncapsulationKey`]. +/// which were encrypted by [`EncapsulationKey`]. pub type DecapsulationKey = ::DecapsulationKey; /// KEM encryption key (i.e. public key) which encrypts shared secrets into ciphertexts which /// can be decrypted by [`DecapsulationKey`]. pub type EncapsulationKey = ::EncapsulationKey; +/// Shared key: plaintext produced after decapsulation by [`Decapsulate::decapsulate`] which is +/// also returned by [`Encapsulate::encapsulate`], which is the shared secret resulting from the +/// key encapsulation algorithm. +pub type SharedKey = array::Array::SharedKeySize>; + /// Ciphertext message (a.k.a. "encapsulated key") produced by [`Encapsulate::encapsulate`] which is -/// an encrypted [`SharedSecret`] that can be decrypted using [`Decapsulate::decapsulate`]. -/// -/// `K` is expected to be a type that impls [`Kem`], such as an encapsulator or decapsulator. +/// an encrypted [`SharedKey`] that can be decrypted using [`Decapsulate::decapsulate`]. pub type Ciphertext = array::Array::CiphertextSize>; -/// Shared secret: plaintext produced after decapsulation by [`Decapsulate::decapsulate`] which is -/// also returned by [`Encapsulate::encapsulate`]. -/// -/// `K` is expected to be a type that impls [`Kem`], such as an encapsulator or decapsulator. -pub type SharedSecret = array::Array::SharedSecretSize>; - /// Key encapsulation mechanism. /// /// This trait describes the entire type family used by a KEM. @@ -52,12 +49,12 @@ pub trait Kem: Copy + Clone + Debug + Default + Eq + Ord + Send + Sync + 'static /// can be decrypted by [`Kem::DecapsulationKey`]. type EncapsulationKey: Encapsulate + Clone; + /// Size of the shared key/secret returned by both encapsulation and decapsulation. + type SharedKeySize: ArraySize; + /// Size of the ciphertext (a.k.a. "encapsulated key") produced by [`Self::EncapsulationKey`]. type CiphertextSize: ArraySize; - /// Size of the shared secret after decapsulation by [`Self::DecapsulationKey`]. - type SharedSecretSize: ArraySize; - /// Generate a random KEM keypair using the provided random number generator. fn generate_keypair_from_rng( rng: &mut R, @@ -79,15 +76,15 @@ 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 { - /// Encapsulates a fresh [`SharedSecret`] generated using the supplied random number + /// Encapsulates a fresh [`SharedKey`] generated using the supplied random number /// generator `R`. - fn encapsulate_with_rng(&self, rng: &mut R) -> (Ciphertext, SharedSecret) + 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, SharedSecret) { + fn encapsulate(&self) -> (Ciphertext, SharedKey) { self.encapsulate_with_rng(&mut UnwrapErr(SysRng)) } } @@ -103,13 +100,13 @@ pub trait Encapsulate: TryKeyInit + KeyExport { /// also impl the [`Generate`] trait to support key generation. pub trait Decapsulate: TryDecapsulate { /// Decapsulates the given [`Ciphertext`] a.k.a. "encapsulated key". - fn decapsulate(&self, ct: &Ciphertext) -> SharedSecret; + 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)) } } @@ -124,13 +121,13 @@ pub trait TryDecapsulate: AsRef { 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, { @@ -145,7 +142,7 @@ where { type Error = Infallible; - fn try_decapsulate(&self, ct: &Ciphertext) -> Result, Infallible> { + fn try_decapsulate(&self, ct: &Ciphertext) -> Result, Infallible> { Ok(self.decapsulate(ct)) } } From 587b17b9cf4bb3e563737d8643b632ff52ea12b9 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 30 Jan 2026 10:55:52 -0700 Subject: [PATCH 4/4] generate_keypair shouldn't have a parameter --- kem/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kem/src/lib.rs b/kem/src/lib.rs index f35df71dd..da02d50aa 100644 --- a/kem/src/lib.rs +++ b/kem/src/lib.rs @@ -47,7 +47,7 @@ pub trait Kem: Copy + Clone + Debug + Default + Eq + Ord + Send + Sync + 'static /// KEM encryption key (i.e. public key) which encrypts shared secrets into ciphertexts which /// can be decrypted by [`Kem::DecapsulationKey`]. - type EncapsulationKey: Encapsulate + Clone; + type EncapsulationKey: Encapsulate + Clone + Debug + Eq; /// Size of the shared key/secret returned by both encapsulation and decapsulation. type SharedKeySize: ArraySize; @@ -66,7 +66,7 @@ pub trait Kem: Copy + Clone + Debug + Default + Eq + Ord + Send + Sync + 'static /// Generate a random KEM keypair using the system's secure RNG. #[cfg(feature = "getrandom")] - fn generate_keypair(&self) -> (Self::DecapsulationKey, Self::EncapsulationKey) { + fn generate_keypair() -> (Self::DecapsulationKey, Self::EncapsulationKey) { Self::generate_keypair_from_rng(&mut UnwrapErr(SysRng)) } }