kem/lib.rs
1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
7)]
8#![forbid(unsafe_code)]
9#![warn(missing_docs, unused_qualifications, missing_debug_implementations)]
10
11//! # Usage
12//!
13//! There are two roles in a KEM:
14//!
15//! - Encapsulator: the holder of the public key (a.k.a. encapsulation key), which provides an
16//! operation that simultaneously generates both the plaintext and ciphertext of a random
17//! "shared key" whose ciphertext (a.k.a. encapsulation) can be sent to the party with the
18//! decapsulator key.
19//! - Decapsulator: holder secret/private key (a.k.a. decapsulation key), which can be used to
20//! decrypt the encrypted (a.k.a. encapsulated) "shared key" which was randomly generated by
21//! the encapsulator.
22//!
23//! The following example illustrates the workflow of using this crate's traits with a hypothetical
24//! KEM named `K` :
25//!
26#![cfg_attr(feature = "getrandom", doc = "```")]
27#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
28//! // NOTE: requires the `getrandom` feature is enabled
29//!
30//! use kem::{Decapsulate, Encapsulate, Kem};
31//!
32//! # pub fn kem_workflow_example<K: Kem<DecapsulationKey: Decapsulate>>() {
33//! // Generate a decapsulation/encapsulation keypair. The first one is the secret one.
34//! let (dk, ek) = K::generate_keypair();
35//!
36//! // Encapsulator:
37//! //
38//! // Randomly generates and encapsulates/encrypts a shared key which can be decrypted by the
39//! // holder of the decapsulation key, obtaining the ciphertext and plaintext of the shared key.
40//! let (ct, k_send) = ek.encapsulate();
41//!
42//! // Decapsulator:
43//! //
44//! // Decapsulates the encrypted/encapsulated shared key, obtaining its plaintext.
45//! let k_recv = dk.decapsulate(&ct);
46//!
47//! // We've now established a shared key.
48//! assert_eq!(k_send, k_recv);
49//! # }
50//! ```
51//!
52//! ## Serialization
53//!
54//! The [`KeyInit`] and [`KeyExport`] traits can be used to load and store encoded decapsulation
55//! and encapsulation keys from their byte serialization.
56//!
57//! Decapsulation keys are often initialized from a compact representation known as a [`Seed`].
58//! The [`FromSeed`] trait provides an extension to the [`Kem`] trait for initializing keypairs
59//! from a seed value. We recommend the [`KeyInit`] and [`KeyExport`] trait impls on
60//! decapsulation keys operate on seed values when there is a choice of multiple key formats
61//! (e.g. expanded decapsulation keys).
62//!
63//! [RFC 9180]: https://www.rfc-editor.org/info/rfc9180
64
65pub use common::{
66 self, Generate, InvalidKey, Key, KeyExport, KeyInit, KeySizeUser, TryKeyInit, typenum::consts,
67};
68
69use common::array::{self, ArraySize};
70use core::fmt::Debug;
71use core::{array::TryFromSliceError, convert::Infallible};
72use rand_core::CryptoRng;
73
74#[cfg(feature = "getrandom")]
75use common::getrandom::{SysRng, rand_core::UnwrapErr};
76
77/// Seed value which can be used to deterministically initialize a KEM keypair.
78pub type Seed<K> = array::Array<u8, <K as FromSeed>::SeedSize>;
79
80/// KEM decryption key (i.e. private key) which can decrypt encrypted shared secret ciphertexts
81/// which were encrypted by [`EncapsulationKey<K>`].
82pub type DecapsulationKey<K> = <K as Kem>::DecapsulationKey;
83
84/// KEM encryption key (i.e. public key) which encrypts shared secrets into ciphertexts which
85/// can be decrypted by [`DecapsulationKey<K>`].
86pub type EncapsulationKey<K> = <K as Kem>::EncapsulationKey;
87
88/// Shared key: plaintext produced after decapsulation by [`Decapsulate::decapsulate`] which is
89/// also returned by [`Encapsulate::encapsulate`], which is the shared secret resulting from the
90/// key encapsulation algorithm.
91pub type SharedKey<K> = array::Array<u8, <K as Kem>::SharedKeySize>;
92
93/// Ciphertext message (a.k.a. "encapsulated key") produced by [`Encapsulate::encapsulate`] which is
94/// an encrypted [`SharedKey`] that can be decrypted using [`Decapsulate::decapsulate`].
95pub type Ciphertext<K> = array::Array<u8, <K as Kem>::CiphertextSize>;
96
97/// Key encapsulation mechanism.
98///
99/// This trait describes the entire type family used by a KEM.
100pub trait Kem: Copy + Clone + Debug + Default + Eq + Ord + Send + Sync + 'static {
101 /// KEM decryption key (i.e. private key) which can decrypt encrypted shared secret ciphertexts
102 /// which were encrypted by [`Kem::EncapsulationKey`].
103 type DecapsulationKey: TryDecapsulate<Kem = Self> + Generate;
104
105 /// KEM encryption key (i.e. public key) which encrypts shared secrets into ciphertexts which
106 /// can be decrypted by [`Kem::DecapsulationKey`].
107 type EncapsulationKey: Encapsulate<Kem = Self> + Clone + Debug + Eq;
108
109 /// Size of the shared key/secret returned by both encapsulation and decapsulation.
110 type SharedKeySize: ArraySize;
111
112 /// Size of the ciphertext (a.k.a. "encapsulated key") produced by [`Self::EncapsulationKey`].
113 type CiphertextSize: ArraySize;
114
115 /// Generate a random KEM keypair using the provided random number generator.
116 fn generate_keypair_from_rng<R: CryptoRng>(
117 rng: &mut R,
118 ) -> (DecapsulationKey<Self>, EncapsulationKey<Self>) {
119 let dk = DecapsulationKey::<Self>::generate_from_rng(rng);
120 let ek = dk.encapsulation_key().clone();
121 (dk, ek)
122 }
123
124 /// Generate a random KEM keypair using the system's secure RNG.
125 #[cfg(feature = "getrandom")]
126 fn generate_keypair() -> (DecapsulationKey<Self>, EncapsulationKey<Self>) {
127 Self::generate_keypair_from_rng(&mut UnwrapErr(SysRng))
128 }
129}
130
131/// Initialize a KEM from a [`Seed`].
132///
133/// Many KEMs support a fully deterministic and infallible initialization from a short seed value.
134///
135/// This trait is blanket impl'd for any [`Kem`] whose [`DecapsulationKey`] impls the [`KeyInit`]
136/// trait.
137pub trait FromSeed: Kem {
138 /// Size of the seed value in bytes.
139 type SeedSize: ArraySize;
140
141 /// Using the provided seed value, create a KEM keypair.
142 fn from_seed(seed: &Seed<Self>) -> (DecapsulationKey<Self>, EncapsulationKey<Self>);
143}
144
145impl<K> FromSeed for K
146where
147 K: Kem,
148 K::DecapsulationKey: KeyInit,
149{
150 type SeedSize = <K::DecapsulationKey as KeySizeUser>::KeySize;
151
152 fn from_seed(seed: &Seed<Self>) -> (DecapsulationKey<Self>, EncapsulationKey<Self>) {
153 let dk = DecapsulationKey::<Self>::new(seed);
154 let ek = dk.encapsulation_key().clone();
155 (dk, ek)
156 }
157}
158
159/// Decapsulator with an associated encapsulation key which can be used for encrypting shared keys
160/// that this decapsulator can decrypt.
161pub trait Decapsulator {
162 /// KEM algorithm this decapsulator is for.
163 type Kem: Kem;
164
165 /// Encapsulation key which can encrypt ciphertexts this decapsulator can decrypt.
166 fn encapsulation_key(&self) -> &EncapsulationKey<Self::Kem>;
167}
168
169/// Decapsulator for encapsulated keys, with an associated `Encapsulator` bounded by the
170/// [`Encapsulate`] trait.
171///
172/// Often, this will just be a secret key. But, as with [`Encapsulate`], it can be a bundle
173/// of secret keys, or it can include a sender's private key for authenticated encapsulation.
174/// It could also be a hardware device like an HSM, TPM, or SEP.
175///
176/// When possible (i.e. for software / non-HSM implementations) types which impl this trait should
177/// also impl the [`Generate`] trait to support key generation.
178pub trait Decapsulate: TryDecapsulate<Error = Infallible> {
179 /// Decapsulates the given [`Ciphertext`] a.k.a. "encapsulated key".
180 fn decapsulate(&self, ct: &Ciphertext<Self::Kem>) -> SharedKey<Self::Kem>;
181
182 /// Decapsulate the given byte slice containing a [`Ciphertext`] a.k.a. "encapsulated key".
183 ///
184 /// # Errors
185 /// - If the length of `ct` is not equal to `<Self as Kem>::CiphertextSize`.
186 fn decapsulate_slice(&self, ct: &[u8]) -> Result<SharedKey<Self::Kem>, TryFromSliceError> {
187 ct.try_into().map(|ct| self.decapsulate(&ct))
188 }
189}
190
191/// Decapsulator for encapsulated keys with failure handling, with an associated `Encapsulator`
192/// bounded by the [`Encapsulate`] trait.
193///
194/// Prefer to implement the [`Decapsulate`] trait if possible. See that trait's documentation for
195/// more information.
196pub trait TryDecapsulate: Decapsulator {
197 /// Decapsulation error
198 type Error: core::error::Error;
199
200 /// Decapsulates the given [`Ciphertext`] a.k.a. "encapsulated key".
201 fn try_decapsulate(
202 &self,
203 ct: &Ciphertext<Self::Kem>,
204 ) -> Result<SharedKey<Self::Kem>, Self::Error>;
205
206 /// Decapsulate the given byte slice containing a [`Ciphertext`] a.k.a. "encapsulated key".
207 ///
208 /// # Errors
209 /// - If the length of `ct` is not equal to `<Self as Kem>::CiphertextSize`.
210 fn try_decapsulate_slice(&self, ct: &[u8]) -> Result<SharedKey<Self::Kem>, Self::Error>
211 where
212 Self::Error: From<TryFromSliceError>,
213 {
214 self.try_decapsulate(ct.try_into()?)
215 }
216}
217
218impl<D> TryDecapsulate for D
219where
220 D: Decapsulate + Decapsulator,
221{
222 type Error = Infallible;
223
224 fn try_decapsulate(
225 &self,
226 ct: &Ciphertext<Self::Kem>,
227 ) -> Result<SharedKey<Self::Kem>, Infallible> {
228 Ok(self.decapsulate(ct))
229 }
230}
231
232/// Encapsulator for shared secrets.
233///
234/// Often, this will just be a public key. However, it can also be a bundle of public keys, or it
235/// can include a sender's private key for authenticated encapsulation.
236pub trait Encapsulate: TryKeyInit + KeyExport {
237 /// KEM algorithm this encapsulator is for.
238 type Kem: Kem;
239
240 /// Encapsulates a fresh [`SharedKey`] generated using the supplied random number
241 /// generator `R`.
242 fn encapsulate_with_rng<R>(&self, rng: &mut R) -> (Ciphertext<Self::Kem>, SharedKey<Self::Kem>)
243 where
244 R: CryptoRng + ?Sized;
245
246 /// Encapsulate a fresh shared secret generated using the system's secure RNG.
247 #[cfg(feature = "getrandom")]
248 fn encapsulate(&self) -> (Ciphertext<Self::Kem>, SharedKey<Self::Kem>) {
249 self.encapsulate_with_rng(&mut UnwrapErr(SysRng))
250 }
251}