-
Notifications
You must be signed in to change notification settings - Fork 230
kem: Kem trait for the whole algorithm type family
#2243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,74 +13,82 @@ 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<K>`]. | ||
| pub type DecapsulationKey<K> = <K as Kem>::DecapsulationKey; | ||
|
|
||
| /// KEM encryption key (i.e. public key) which encrypts shared secrets into ciphertexts which | ||
| /// can be decrypted by [`DecapsulationKey<K>`]. | ||
| pub type EncapsulationKey<K> = <K as Kem>::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<K> = array::Array<u8, <K as Kem>::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 [`KemParams`], such as an encapsulator or decapsulator. | ||
| pub type Ciphertext<K> = array::Array<u8, <K as KemParams>::CiphertextSize>; | ||
| /// an encrypted [`SharedKey`] that can be decrypted using [`Decapsulate::decapsulate`]. | ||
| pub type Ciphertext<K> = array::Array<u8, <K as Kem>::CiphertextSize>; | ||
|
|
||
| /// Shared secret: plaintext produced after decapsulation by [`Decapsulate::decapsulate`] which is | ||
| /// also returned by [`Encapsulate::encapsulate`]. | ||
| /// Key encapsulation mechanism. | ||
| /// | ||
| /// `K` is expected to be a type that impls [`KemParams`], such as an encapsulator or decapsulator. | ||
| pub type SharedSecret<K> = array::Array<u8, <K as KemParams>::SharedSecretSize>; | ||
| /// 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: TryDecapsulate<Self> + Generate; | ||
|
|
||
| /// 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`]. | ||
| /// KEM encryption key (i.e. public key) which encrypts shared secrets into ciphertexts which | ||
| /// can be decrypted by [`Kem::DecapsulationKey`]. | ||
| type EncapsulationKey: Encapsulate<Self> + Clone + Debug + Eq; | ||
|
|
||
| /// 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 [`Decapsulate::decapsulate`]. | ||
| type SharedSecretSize: ArraySize; | ||
| /// Generate a random KEM keypair using the provided random number generator. | ||
| fn generate_keypair_from_rng<R: CryptoRng>( | ||
| rng: &mut R, | ||
| ) -> (Self::DecapsulationKey, Self::EncapsulationKey) { | ||
| let dk = Self::DecapsulationKey::generate_from_rng(rng); | ||
| let ek = dk.as_ref().clone(); | ||
| (dk, ek) | ||
| } | ||
|
|
||
| /// Generate a random KEM keypair using the system's secure RNG. | ||
| #[cfg(feature = "getrandom")] | ||
| fn generate_keypair() -> (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 { | ||
| /// Encapsulates a fresh [`SharedSecret`] generated using the supplied random number | ||
| pub trait Encapsulate<K: Kem>: TryKeyInit + KeyExport { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that I think about it, these generic parameters are important for binding an impl of Unlike
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm also kind of torn using a generic here. There will probably be only one impl per type, so we don't need overlapping bounds. It means that everyone needs to notate it in the downstream bounds, which is a little annoying, but they'll need to bound on If we moved it to an associated type it would get out of the way, but then we should probably get rid of the We would, however, still have to worry about both
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, it's kind of annoying having multiple generics here. A thought: what about putting all methods inside
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's nice about the current approach is it's flexible to permit a But also: we still need types to hold the state of decoded decapsulation/encapsulation keys. This trait is intended to be impl'd on a ZST.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh, ok. So some simplifications that remain are: absorb |
||
| /// Encapsulates a fresh [`SharedKey`] generated using the supplied random number | ||
| /// generator `R`. | ||
| fn encapsulate_with_rng<R>(&self, rng: &mut R) -> (Ciphertext<Self>, SharedSecret<Self>) | ||
| fn encapsulate_with_rng<R>(&self, rng: &mut R) -> (Ciphertext<K>, SharedKey<K>) | ||
| where | ||
| R: CryptoRng + ?Sized; | ||
|
|
||
| /// Encapsulate a fresh shared secret generated using the system's secure RNG. | ||
| #[cfg(feature = "getrandom")] | ||
| fn encapsulate(&self) -> (Ciphertext<Self>, SharedSecret<Self>) { | ||
| fn encapsulate(&self) -> (Ciphertext<K>, SharedKey<K>) { | ||
| 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 = <Self::Encapsulator as KemParams>::CiphertextSize, | ||
| SharedSecretSize = <Self::Encapsulator as KemParams>::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<K: Decapsulator> KemParams for K { | ||
| type CiphertextSize = <K::Encapsulator as KemParams>::CiphertextSize; | ||
| type SharedSecretSize = <K::Encapsulator as KemParams>::SharedSecretSize; | ||
| } | ||
|
|
||
| /// Decapsulator for encapsulated keys, with an associated `Encapsulator` bounded by the | ||
| /// [`Encapsulate`] trait. | ||
| /// | ||
|
|
@@ -90,15 +98,15 @@ impl<K: Decapsulator> 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<Error = Infallible> { | ||
| pub trait Decapsulate<K: Kem>: TryDecapsulate<K, Error = Infallible> { | ||
| /// Decapsulates the given [`Ciphertext`] a.k.a. "encapsulated key". | ||
| fn decapsulate(&self, ct: &Ciphertext<Self>) -> SharedSecret<Self>; | ||
| fn decapsulate(&self, ct: &Ciphertext<K>) -> SharedKey<K>; | ||
|
|
||
| /// Decapsulate the given byte slice containing a [`Ciphertext`] a.k.a. "encapsulated key". | ||
| /// | ||
| /// # Errors | ||
| /// - If the length of `ct` is not equal to `<Self as Kem>::CiphertextSize`. | ||
| fn decapsulate_slice(&self, ct: &[u8]) -> Result<SharedSecret<Self>, TryFromSliceError> { | ||
| fn decapsulate_slice(&self, ct: &[u8]) -> Result<SharedKey<K>, TryFromSliceError> { | ||
| ct.try_into().map(|ct| self.decapsulate(&ct)) | ||
| } | ||
| } | ||
|
|
@@ -108,32 +116,33 @@ pub trait Decapsulate: Decapsulator + TryDecapsulate<Error = Infallible> { | |
| /// | ||
| /// Prefer to implement the [`Decapsulate`] trait if possible. See that trait's documentation for | ||
| /// more information. | ||
| pub trait TryDecapsulate: Decapsulator { | ||
| pub trait TryDecapsulate<K: Kem>: AsRef<K::EncapsulationKey> { | ||
| /// Decapsulation error | ||
| type Error: core::error::Error; | ||
|
|
||
| /// Decapsulates the given [`Ciphertext`] a.k.a. "encapsulated key". | ||
| fn try_decapsulate(&self, ct: &Ciphertext<Self>) -> Result<SharedSecret<Self>, Self::Error>; | ||
| fn try_decapsulate(&self, ct: &Ciphertext<K>) -> Result<SharedKey<K>, 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 `<Self as Kem>::CiphertextSize`. | ||
| fn try_decapsulate_slice(&self, ct: &[u8]) -> Result<SharedSecret<Self>, Self::Error> | ||
| fn try_decapsulate_slice(&self, ct: &[u8]) -> Result<SharedKey<K>, Self::Error> | ||
| where | ||
| Self::Error: From<TryFromSliceError>, | ||
| { | ||
| self.try_decapsulate(ct.try_into()?) | ||
| } | ||
| } | ||
|
|
||
| impl<D> TryDecapsulate for D | ||
| impl<D, K> TryDecapsulate<K> for D | ||
| where | ||
| D: Decapsulate, | ||
| D: Decapsulate<K>, | ||
| K: Kem, | ||
| { | ||
| type Error = Infallible; | ||
|
|
||
| fn try_decapsulate(&self, ct: &Ciphertext<Self>) -> Result<SharedSecret<Self>, Infallible> { | ||
| fn try_decapsulate(&self, ct: &Ciphertext<K>) -> Result<SharedKey<K>, Infallible> { | ||
| Ok(self.decapsulate(ct)) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the same bounds as
elliptic_curve::Curve. They're important when a type that implsKemis used as a generic parameter on other types, so custom derive works because it doesn't freak out about the bounds of the ZST it's generic around.