Skip to main content

ml_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/meta/master/logo.svg",
6    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
7)]
8#![allow(non_snake_case)] // Allow notation matching the spec
9#![allow(clippy::clone_on_copy)] // Be explicit about moving data
10
11//! # Usage
12//!
13//! This crate implements the Module-Lattice-based Key Encapsulation Method (ML-KEM) algorithm
14//! being standardized by NIST in FIPS 203.  ML-KEM is a KEM in the sense that it creates a
15//! (decapsulation key, encapsulation key) pair, such that anyone can use the encapsulation key to
16//! establish a shared key with the holder of the decapsulation key.  ML-KEM is the first KEM
17//! algorithm standardized by NIST that is designed to be resistant to attacks using quantum
18//! computers.
19//!
20#![cfg_attr(feature = "getrandom", doc = "```")]
21#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
22//! // NOTE: requires the `getrandom` feature is enabled
23//!
24//! use ml_kem::{
25//!     MlKem768,
26//!     kem::{Decapsulate, Encapsulate, Kem}
27//! };
28//!
29//! // Generate a decapsulation/encapsulation keypair
30//! let (dk, ek) = MlKem768::generate_keypair();
31//!
32//! // Encapsulate a shared key to the holder of the decapsulation key, receive the shared
33//! // secret `k_send` and the encapsulated form `ct`.
34//! let (ct, k_send) = ek.encapsulate();
35//!
36//! // Decapsulate the shared key
37//! let k_recv = dk.decapsulate(&ct);
38//!
39//! // We've now established a shared key
40//! assert_eq!(k_send, k_recv);
41//! ```
42//!
43//! [RFC 9180]: https://www.rfc-editor.org/info/rfc9180
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 6. The ML-KEM Key-Encapsulation Mechanism (Decapsulation)
57mod decapsulation_key;
58
59/// Section 6. The ML-KEM Key-Encapsulation Mechanism (Encapsulation)
60mod encapsulation_key;
61
62/// Section 5. The K-PKE Component Scheme
63mod pke;
64
65/// Section 7. Parameter Sets
66mod param;
67
68// PKCS#8 key encoding support (doc comments in module)
69pub mod pkcs8;
70
71pub use array::{self, ArraySize};
72pub use decapsulation_key::DecapsulationKey;
73#[allow(deprecated)]
74pub use decapsulation_key::ExpandedKeyEncoding;
75pub use encapsulation_key::EncapsulationKey;
76pub use kem::{
77    self, Ciphertext, Decapsulate, Encapsulate, FromSeed, Generate, InvalidKey, Kem, Key,
78    KeyExport, KeyInit, KeySizeUser, TryKeyInit,
79};
80pub use ml_kem_512::MlKem512;
81pub use ml_kem_768::MlKem768;
82pub use ml_kem_1024::MlKem1024;
83pub use param::{ExpandedDecapsulationKey, ParameterSet};
84
85use array::{
86    Array,
87    sizes::{U2, U3, U4, U5, U10, U11, U32, U64},
88};
89use core::fmt::Debug;
90
91/// A 32-byte array, defined here for brevity because it is used several times
92pub type B32 = Array<u8, U32>;
93
94/// ML-KEM seeds are decapsulation (private) keys, which are consistently 64-bytes across all
95/// security levels, and are the preferred serialization for representing such keys.
96pub type Seed = Array<u8, U64>;
97
98/// ML-KEM-512 is the parameter set for security category 1, corresponding to key search on a block
99/// cipher with a 128-bit key.
100pub mod ml_kem_512 {
101    use crate::{
102        Debug, ParameterSet, U2, U3, U4, U10,
103        kem::Kem,
104        param::{self, EncodedUSize, EncodedVSize},
105    };
106    use array::{sizes::U32, typenum::Sum};
107
108    /// `MlKem512` is the parameter set for security category 1, corresponding to key search on a
109    /// block cipher with a 128-bit key.
110    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
111    pub struct MlKem512;
112
113    impl ParameterSet for MlKem512 {
114        type K = U2;
115        type Eta1 = U3;
116        type Eta2 = U2;
117        type Du = U10;
118        type Dv = U4;
119    }
120
121    impl Kem for MlKem512 {
122        type DecapsulationKey = DecapsulationKey;
123        type EncapsulationKey = EncapsulationKey;
124        type CiphertextSize = Sum<EncodedUSize<Self>, EncodedVSize<Self>>;
125        type SharedKeySize = U32;
126    }
127
128    /// An ML-KEM-512 `DecapsulationKey` which provides the ability to generate a new key pair, and
129    /// decapsulate an encapsulated shared key.
130    pub type DecapsulationKey = crate::DecapsulationKey<MlKem512>;
131
132    /// An ML-KEM-512 `EncapsulationKey` provides the ability to encapsulate a shared key so that it
133    /// can only be decapsulated by the holder of the corresponding decapsulation key.
134    pub type EncapsulationKey = crate::EncapsulationKey<MlKem512>;
135
136    /// Encoded ML-KEM-512 ciphertexts.
137    pub type Ciphertext = kem::Ciphertext<MlKem512>;
138
139    /// Legacy expanded decapsulation keys. Prefer seeds instead.
140    #[doc(hidden)]
141    #[deprecated(since = "0.3.0", note = "use `Seed` instead")]
142    pub type ExpandedDecapsulationKey = param::ExpandedDecapsulationKey<MlKem512>;
143}
144
145/// ML-KEM-768 is the parameter set for security category 3, corresponding to key search on a block
146/// cipher with a 192-bit key.
147pub mod ml_kem_768 {
148    use crate::{
149        Debug, ParameterSet, U2, U3, U4, U10,
150        kem::Kem,
151        param::{self, EncodedUSize, EncodedVSize},
152    };
153    use array::sizes::U32;
154    use array::typenum::Sum;
155
156    /// `MlKem768` is the parameter set for security category 3, corresponding to key search on a
157    /// block cipher with a 192-bit key.
158    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
159    pub struct MlKem768;
160
161    impl ParameterSet for MlKem768 {
162        type K = U3;
163        type Eta1 = U2;
164        type Eta2 = U2;
165        type Du = U10;
166        type Dv = U4;
167    }
168
169    impl Kem for MlKem768 {
170        type DecapsulationKey = DecapsulationKey;
171        type EncapsulationKey = EncapsulationKey;
172        type CiphertextSize = Sum<EncodedUSize<Self>, EncodedVSize<Self>>;
173        type SharedKeySize = U32;
174    }
175
176    /// An ML-KEM-768 `DecapsulationKey` which provides the ability to generate a new key pair, and
177    /// decapsulate an encapsulated shared key.
178    pub type DecapsulationKey = crate::DecapsulationKey<MlKem768>;
179
180    /// An ML-KEM-768 `EncapsulationKey` provides the ability to encapsulate a shared key so that it
181    /// can only be decapsulated by the holder of the corresponding decapsulation key.
182    pub type EncapsulationKey = crate::EncapsulationKey<MlKem768>;
183
184    /// Encoded ML-KEM-512 ciphertexts.
185    pub type Ciphertext = kem::Ciphertext<MlKem768>;
186
187    /// Legacy expanded decapsulation keys. Prefer seeds instead.
188    #[doc(hidden)]
189    #[deprecated(since = "0.3.0", note = "use `Seed` instead")]
190    pub type ExpandedDecapsulationKey = param::ExpandedDecapsulationKey<MlKem768>;
191}
192
193/// ML-KEM-1024 is the parameter set for security category 5, corresponding to key search on a block
194/// cipher with a 256-bit key.
195pub mod ml_kem_1024 {
196    use crate::{
197        Debug, ParameterSet, U2, U4, U5, U11,
198        kem::Kem,
199        param::{self, EncodedUSize, EncodedVSize},
200    };
201    use array::{sizes::U32, typenum::Sum};
202
203    /// `MlKem1024` is the parameter set for security category 5, corresponding to key search on a
204    /// block cipher with a 256-bit key.
205    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
206    pub struct MlKem1024;
207
208    impl ParameterSet for MlKem1024 {
209        type K = U4;
210        type Eta1 = U2;
211        type Eta2 = U2;
212        type Du = U11;
213        type Dv = U5;
214    }
215
216    impl Kem for MlKem1024 {
217        type DecapsulationKey = DecapsulationKey;
218        type EncapsulationKey = EncapsulationKey;
219        type CiphertextSize = Sum<EncodedUSize<Self>, EncodedVSize<Self>>;
220        type SharedKeySize = U32;
221    }
222
223    /// An ML-KEM-1024 `DecapsulationKey` which provides the ability to generate a new key pair, and
224    /// decapsulate an encapsulated shared key.
225    pub type DecapsulationKey = crate::DecapsulationKey<MlKem1024>;
226
227    /// An ML-KEM-1024 `EncapsulationKey` provides the ability to encapsulate a shared key so that
228    /// it can only be decapsulated by the holder of the corresponding decapsulation key.
229    pub type EncapsulationKey = crate::EncapsulationKey<MlKem1024>;
230
231    /// Encoded ML-KEM-512 ciphertexts.
232    pub type Ciphertext = kem::Ciphertext<MlKem1024>;
233
234    /// Legacy expanded decapsulation keys. Prefer seeds instead.
235    #[doc(hidden)]
236    #[deprecated(since = "0.3.0", note = "use `Seed` instead")]
237    pub type ExpandedDecapsulationKey = param::ExpandedDecapsulationKey<MlKem1024>;
238}
239
240/// An ML-KEM-512 `DecapsulationKey` which provides the ability to generate a new key pair, and
241/// decapsulate an encapsulated shared key.
242pub type DecapsulationKey512 = ml_kem_512::DecapsulationKey;
243
244/// An ML-KEM-512 `EncapsulationKey` provides the ability to encapsulate a shared key so that it
245/// can only be decapsulated by the holder of the corresponding decapsulation key.
246pub type EncapsulationKey512 = ml_kem_512::EncapsulationKey;
247
248/// An ML-KEM-768 `DecapsulationKey` which provides the ability to generate a new key pair, and
249/// decapsulate an encapsulated shared key.
250pub type DecapsulationKey768 = ml_kem_768::DecapsulationKey;
251
252/// An ML-KEM-768 `EncapsulationKey` provides the ability to encapsulate a shared key so that it
253/// can only be decapsulated by the holder of the corresponding decapsulation key.
254pub type EncapsulationKey768 = ml_kem_768::EncapsulationKey;
255
256/// An ML-KEM-1024 `DecapsulationKey` which provides the ability to generate a new key pair, and
257/// decapsulate an encapsulated shared key.
258pub type DecapsulationKey1024 = ml_kem_1024::DecapsulationKey;
259
260/// An ML-KEM-1024 `EncapsulationKey` provides the ability to encapsulate a shared key so that it
261/// can only be decapsulated by the holder of the corresponding decapsulation key.
262pub type EncapsulationKey1024 = ml_kem_1024::EncapsulationKey;
263
264/// Shared key established by using ML-KEM, returned from both encapsulation and decapsulation.
265pub type SharedKey = Array<u8, U32>;
266
267#[cfg(test)]
268#[cfg(feature = "getrandom")]
269mod test {
270    use super::*;
271    use crate::{MlKem512, MlKem768, MlKem1024, param::KemParams};
272    use ::kem::{Encapsulate, Generate, InvalidKey, TryDecapsulate};
273    use array::typenum::Unsigned;
274    use getrandom::SysRng;
275    use rand_core::{TryRng, UnwrapErr};
276
277    fn round_trip_test<K>()
278    where
279        K: Kem,
280    {
281        let (dk, ek) = K::generate_keypair();
282        let (ct, k_send) = ek.encapsulate();
283        let k_recv = dk.try_decapsulate(&ct).unwrap();
284        assert_eq!(k_send, k_recv);
285    }
286
287    #[test]
288    fn round_trip() {
289        round_trip_test::<MlKem512>();
290        round_trip_test::<MlKem768>();
291        round_trip_test::<MlKem1024>();
292    }
293
294    fn seed_test<P>()
295    where
296        P: KemParams,
297    {
298        let mut rng = UnwrapErr(SysRng);
299        let mut seed = Seed::default();
300        rng.try_fill_bytes(&mut seed).unwrap();
301
302        let dk = DecapsulationKey::<P>::from_seed(seed.clone());
303        let seed_encoded = dk.to_seed().unwrap();
304        assert_eq!(seed, seed_encoded);
305
306        let ek_original = dk.encapsulation_key();
307        let ek_encoded = ek_original.to_bytes();
308        let ek_decoded = EncapsulationKey::new(&ek_encoded).unwrap();
309        assert_eq!(ek_original, &ek_decoded);
310    }
311
312    #[test]
313    fn seed() {
314        seed_test::<MlKem512>();
315        seed_test::<MlKem768>();
316        seed_test::<MlKem1024>();
317    }
318
319    #[allow(deprecated)]
320    fn expanded_key_test<P>()
321    where
322        P: KemParams,
323    {
324        let mut rng = UnwrapErr(SysRng);
325        let dk_original = DecapsulationKey::<P>::generate_from_rng(&mut rng);
326        let dk_encoded = dk_original.to_expanded_bytes();
327        let dk_decoded = DecapsulationKey::from_expanded_bytes(&dk_encoded).unwrap();
328        assert_eq!(dk_original, dk_decoded);
329    }
330
331    #[test]
332    fn expanded_key() {
333        expanded_key_test::<MlKem512>();
334        expanded_key_test::<MlKem768>();
335        expanded_key_test::<MlKem1024>();
336    }
337
338    fn invalid_hash_expanded_key_test<P>()
339    where
340        P: KemParams,
341    {
342        let mut rng = UnwrapErr(SysRng);
343        let dk_original = DecapsulationKey::<P>::generate_from_rng(&mut rng);
344
345        #[allow(deprecated)]
346        let mut dk_encoded = dk_original.to_expanded_bytes();
347
348        // Corrupt the hash value
349        let hash_offset = P::NttVectorSize::USIZE + P::EncryptionKeySize::USIZE;
350        dk_encoded[hash_offset] ^= 0xFF;
351
352        #[allow(deprecated)]
353        let dk_decoded: Result<DecapsulationKey<P>, InvalidKey> =
354            DecapsulationKey::from_expanded_bytes(&dk_encoded);
355
356        assert!(dk_decoded.is_err());
357    }
358
359    #[test]
360    fn invalid_hash_expanded_key() {
361        invalid_hash_expanded_key_test::<MlKem512>();
362        invalid_hash_expanded_key_test::<MlKem768>();
363        invalid_hash_expanded_key_test::<MlKem1024>();
364    }
365
366    fn key_inequality_test<P>()
367    where
368        P: KemParams,
369    {
370        let mut rng = UnwrapErr(SysRng);
371
372        // Generate two different keys
373        let dk1 = DecapsulationKey::<P>::generate_from_rng(&mut rng);
374        let dk2 = DecapsulationKey::<P>::generate_from_rng(&mut rng);
375
376        let ek1 = dk1.encapsulation_key();
377        let ek2 = dk2.encapsulation_key();
378
379        // Verify inequality (catches PartialEq mutation that returns true unconditionally)
380        assert_ne!(dk1, dk2);
381        assert_ne!(ek1, ek2);
382    }
383
384    #[test]
385    fn key_inequality() {
386        key_inequality_test::<MlKem512>();
387        key_inequality_test::<MlKem768>();
388        key_inequality_test::<MlKem1024>();
389    }
390}