ml_kem/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
6    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
7)]
8#![warn(clippy::pedantic)] // Be pedantic by default
9#![warn(clippy::integer_division_remainder_used)] // Be judicious about using `/` and `%`
10#![allow(non_snake_case)] // Allow notation matching the spec
11#![allow(clippy::clone_on_copy)] // Be explicit about moving data
12#![deny(missing_docs)] // Require all public interfaces to be documented
13
14//! # Usage
15//!
16//! This crate implements the Module-Latice-based Key Encapsulation Method (ML-KEM) algorithm
17//! being standardized by NIST in FIPS 203.  ML-KEM is a KEM in the sense that it creates an
18//! (decapsulation key, encapsulation key) pair, such that anyone can use the encapsulation key to
19//! establish a shared key with the holder of the decapsulation key.  ML-KEM is the first KEM
20//! algorithm standardized by NIST that is designed to be resistant to attacks using quantum
21//! computers.
22//!
23//! ```
24//! # use ml_kem::*;
25//! # use ::kem::{Decapsulate, Encapsulate};
26//! let mut rng = rand::thread_rng();
27//!
28//! // Generate a (decapsulation key, encapsulation key) pair
29//! let (dk, ek) = MlKem768::generate(&mut rng);
30//!
31//! // Encapsulate a shared key to the holder of the decapsulation key, receive the shared
32//! // secret `k_send` and the encapsulated form `ct`.
33//! let (ct, k_send) = ek.encapsulate(&mut rng).unwrap();
34//!
35//! // Decapsulate the shared key and verify that it was faithfully received.
36//! let k_recv = dk.decapsulate(&ct).unwrap();
37//! assert_eq!(k_send, k_recv);
38//! ```
39//!
40//! [RFC 9180]: https://www.rfc-editor.org/info/rfc9180
41
42/// The inevitable utility module
43mod util;
44
45/// Section 2.4. Interpreting the Pseudocode
46/// Section 4.2.2. Sampling algorithms
47/// Section 4.3. The Number-Theoretic Transform
48mod algebra;
49
50/// Section 4.1. Crytographic Functions
51mod crypto;
52
53/// Section 4.2.1. Conversion and Compression Algorithms, Compression and decompression
54mod compress;
55
56/// Section 4.2.1. Conversion and Compression Algorithms, Encoding and decoding
57mod encode;
58
59/// Section 5. The K-PKE Component Scheme
60mod pke;
61
62/// Section 6. The ML-KEM Key-Encapsulation Mechanism
63pub mod kem;
64
65/// Section 7. Parameter Sets
66mod param;
67
68use ::kem::{Decapsulate, Encapsulate};
69use core::fmt::Debug;
70use hybrid_array::{
71    typenum::{U10, U11, U2, U3, U4, U5},
72    Array,
73};
74use rand_core::CryptoRngCore;
75
76pub use hybrid_array as array;
77
78#[cfg(feature = "deterministic")]
79pub use util::B32;
80
81pub use param::{ArraySize, ParameterSet};
82
83/// An object that knows what size it is
84pub trait EncodedSizeUser {
85    /// The size of an encoded object
86    type EncodedSize: ArraySize;
87
88    /// Parse an object from its encoded form
89    fn from_bytes(enc: &Encoded<Self>) -> Self;
90
91    /// Serialize an object to its encoded form
92    fn as_bytes(&self) -> Encoded<Self>;
93}
94
95/// A byte array encoding a value the indicated size
96pub type Encoded<T> = Array<u8, <T as EncodedSizeUser>::EncodedSize>;
97
98/// A value that can be encapsulated to.  Note that this interface is not safe: In order for the
99/// KEM to be secure, the `m` input must be randomly generated.
100#[cfg(feature = "deterministic")]
101pub trait EncapsulateDeterministic<EK, SS> {
102    /// Encapsulation error
103    type Error: Debug;
104
105    /// Encapsulates a fresh shared secret.
106    ///
107    /// # Errors
108    ///
109    /// Will vary depending on the underlying implementation.
110    fn encapsulate_deterministic(&self, m: &B32) -> Result<(EK, SS), Self::Error>;
111}
112
113/// A generic interface to a Key Encapsulation Method
114pub trait KemCore {
115    /// The size of a shared key generated by this KEM
116    type SharedKeySize: ArraySize;
117
118    /// The size of a ciphertext encapsulating a shared key
119    type CiphertextSize: ArraySize;
120
121    /// A decapsulation key for this KEM
122    type DecapsulationKey: Decapsulate<Ciphertext<Self>, SharedKey<Self>>
123        + EncodedSizeUser
124        + Debug
125        + PartialEq;
126
127    /// An encapsulation key for this KEM
128    #[cfg(not(feature = "deterministic"))]
129    type EncapsulationKey: Encapsulate<Ciphertext<Self>, SharedKey<Self>>
130        + EncodedSizeUser
131        + Debug
132        + PartialEq;
133
134    /// An encapsulation key for this KEM
135    #[cfg(feature = "deterministic")]
136    type EncapsulationKey: Encapsulate<Ciphertext<Self>, SharedKey<Self>>
137        + EncapsulateDeterministic<Ciphertext<Self>, SharedKey<Self>>
138        + EncodedSizeUser
139        + Debug
140        + PartialEq;
141
142    /// Generate a new (decapsulation, encapsulation) key pair
143    fn generate(rng: &mut impl CryptoRngCore) -> (Self::DecapsulationKey, Self::EncapsulationKey);
144
145    /// Generate a new (decapsulation, encapsulation) key pair deterministically
146    #[cfg(feature = "deterministic")]
147    fn generate_deterministic(d: &B32, z: &B32)
148        -> (Self::DecapsulationKey, Self::EncapsulationKey);
149}
150
151/// `MlKem512` is the parameter set for security category 1, corresponding to key search on a block
152/// cipher with a 128-bit key.
153#[derive(Default, Clone, Debug, PartialEq)]
154pub struct MlKem512Params;
155
156impl ParameterSet for MlKem512Params {
157    type K = U2;
158    type Eta1 = U3;
159    type Eta2 = U2;
160    type Du = U10;
161    type Dv = U4;
162}
163
164/// `MlKem768` is the parameter set for security category 3, corresponding to key search on a block
165/// cipher with a 192-bit key.
166#[derive(Default, Clone, Debug, PartialEq)]
167pub struct MlKem768Params;
168
169impl ParameterSet for MlKem768Params {
170    type K = U3;
171    type Eta1 = U2;
172    type Eta2 = U2;
173    type Du = U10;
174    type Dv = U4;
175}
176
177/// `MlKem1024` is the parameter set for security category 5, corresponding to key search on a block
178/// cipher with a 256-bit key.
179#[derive(Default, Clone, Debug, PartialEq)]
180pub struct MlKem1024Params;
181
182impl ParameterSet for MlKem1024Params {
183    type K = U4;
184    type Eta1 = U2;
185    type Eta2 = U2;
186    type Du = U11;
187    type Dv = U5;
188}
189
190/// A shared key produced by the KEM `K`
191pub type SharedKey<K> = Array<u8, <K as KemCore>::SharedKeySize>;
192
193/// A ciphertext produced by the KEM `K`
194pub type Ciphertext<K> = Array<u8, <K as KemCore>::CiphertextSize>;
195
196/// ML-KEM with the parameter set for security category 1, corresponding to key search on a block
197/// cipher with a 128-bit key.
198pub type MlKem512 = kem::Kem<MlKem512Params>;
199
200/// ML-KEM with the parameter set for security category 3, corresponding to key search on a block
201/// cipher with a 192-bit key.
202pub type MlKem768 = kem::Kem<MlKem768Params>;
203
204/// ML-KEM with the parameter set for security category 5, corresponding to key search on a block
205/// cipher with a 256-bit key.
206pub type MlKem1024 = kem::Kem<MlKem1024Params>;
207
208#[cfg(test)]
209mod test {
210    use super::*;
211
212    fn round_trip_test<K>()
213    where
214        K: KemCore,
215    {
216        let mut rng = rand::thread_rng();
217
218        let (dk, ek) = K::generate(&mut rng);
219
220        let (ct, k_send) = ek.encapsulate(&mut rng).unwrap();
221        let k_recv = dk.decapsulate(&ct).unwrap();
222        assert_eq!(k_send, k_recv);
223    }
224
225    #[test]
226    fn round_trip() {
227        round_trip_test::<MlKem512>();
228        round_trip_test::<MlKem768>();
229        round_trip_test::<MlKem1024>();
230    }
231}