diff --git a/kem/src/lib.rs b/kem/src/lib.rs index ac2be9b0a..697ae2b95 100644 --- a/kem/src/lib.rs +++ b/kem/src/lib.rs @@ -12,44 +12,131 @@ pub use common::{ self, Generate, InvalidKey, Key, KeyExport, KeyInit, KeySizeUser, TryKeyInit, typenum::consts, }; +use common::array::{self, ArraySize}; +use core::{array::TryFromSliceError, convert::Infallible}; use rand_core::TryCryptoRng; #[cfg(feature = "getrandom")] use {common::getrandom, rand_core::TryRngCore}; +/// 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>; + +/// 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>; + +/// Key encapsulation mechanism parameters: sizes of the ciphertext and decrypted plaintext. +/// +/// 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`]. + type CiphertextSize: ArraySize; + + /// Size of the shared secret after decapsulation by [`Decapsulate::decapsulate`]. + type SharedSecretSize: ArraySize; +} + /// 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: TryKeyInit + KeyExport { - /// Encapsulates a fresh shared secret - fn encapsulate_with_rng(&self, rng: &mut R) -> Result<(EK, SS), R::Error> - where - R: TryCryptoRng + ?Sized; +pub trait Encapsulate: KemParams + TryKeyInit + KeyExport { + /// Encapsulates a fresh [`SharedSecret`] generated using the supplied random number + /// generator `R`. + fn encapsulate_with_rng( + &self, + rng: &mut R, + ) -> Result<(Ciphertext, SharedSecret), R::Error>; /// Encapsulate a fresh shared secret generated using the system's secure RNG. #[cfg(feature = "getrandom")] - fn encapsulate(&self) -> (EK, SS) { + fn encapsulate(&self) -> (Ciphertext, SharedSecret) { match self.encapsulate_with_rng(&mut getrandom::SysRng.unwrap_err()) { Ok(ret) => ret, } } } -/// Decapsulator for an encapsulated keys, with an associated encapsulator. +/// 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; + + /// Retrieve the encapsulator associated with this decapsulator. + fn encapsulator(&self) -> &Self::Encapsulator; +} + +impl KemParams for K { + type CiphertextSize = ::CiphertextSize; + type SharedSecretSize = ::SharedSecretSize; +} + +/// Decapsulator for encapsulated keys, with an associated `Encapsulator` bounded by the +/// [`Encapsulate`] trait. /// /// Often, this will just be a secret key. But, as with [`Encapsulate`], it can be a bundle /// of secret keys, or it can include a sender's private key for authenticated encapsulation. +/// It could also be a hardware device like an HSM, TPM, or SEP. /// /// 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 { - /// Encapsulator which corresponds to this decapsulator. - type Encapsulator: Encapsulate; +pub trait Decapsulate: Decapsulator + TryDecapsulate { + /// Decapsulates the given [`Ciphertext`] a.k.a. "encapsulated key". + fn decapsulate(&self, ct: &Ciphertext) -> SharedSecret; - /// Decapsulates the given encapsulated key - fn decapsulate(&self, encapsulated_key: &EK) -> SS; + /// 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> { + ct.try_into().map(|ct| self.decapsulate(&ct)) + } +} - /// Retrieve the encapsulator associated with this decapsulator. - fn encapsulator(&self) -> Self::Encapsulator; +/// Decapsulator for encapsulated keys with failure handling, with an associated `Encapsulator` +/// bounded by the [`Encapsulate`] trait. +/// +/// Prefer to implement the [`Decapsulate`] trait if possible. See that trait's documentation for +/// more information. +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>; + + /// 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> + where + Self::Error: From, + { + self.try_decapsulate(ct.try_into()?) + } +} + +impl TryDecapsulate for D +where + D: Decapsulate, +{ + type Error = Infallible; + + fn try_decapsulate(&self, ct: &Ciphertext) -> Result, Infallible> { + Ok(self.decapsulate(ct)) + } }