bouncycastle_mlkem/lib.rs
1//! This crate implements the Module Lattice Key-Encapsulation Mechanism (ML-KEM) as per FIPS 203.
2//!
3//! # Usage
4//!
5//! This crate has been designed to serve a wide range of use cases, from people dabbling in
6//! cryptography for the first time, to cryptographic protocol designers who need access to the internal
7//! functionality of the ML-KEM algorithm, to embedded systems developers who want access to memory
8//! and performance optimized functions.
9//!
10//! This page gives examples of simple usage for generating keys and performing encapsulation and decapsulation operations.
11//!
12//! More examples on advanced usage can be found on the [mlkem] page.
13//!
14//! # Primer on KEM algorithms
15//!
16//! Since Key-Encapsulation Mechanisms behave differently from the Diffie-Hellman key exchanges that many people are familiar with,
17//! we start with a brief primer on KEMs.
18//!
19//! The core operation of a KEM is "encapsulation" which performs a mathematical operation against a KEM public key and yields
20//! a shared secret key, and a ciphertext. Internally, the encapsulation routine will use a cryptographic random number generator
21//! to ensure that the shared secret key and ciphertext are strongly unique to this encapsulation operation.
22//! The receiving party can then perform a "decapsulation" using the corresponding private key to obtain
23//! the same shared secret key from the ciphertext.
24//!
25//! Some sources, including FIPS 203 refer to the public key as the "encapsulation key `ek`" and the private key
26//! as the "decapsulation key `dk`". These are used interchangeable with the public key `pk` and private key (or secret key) `sk`.
27//!
28//! The three operations of a KEM algorithm are:
29//!
30//! * `keygen() -> (pk, sk)`
31//! * `encaps(pk) -> (ss, ct)`
32//! * `decaps(sk, ct) -> (ss)`
33//!
34//! Since both `keygen` and `encaps` require a source of randomness, it is also common for a cryptographic
35//! library to expose deterministic versions, which are often labelled as "internal" since they should
36//! be used carefully as their misuse can catastrophically reduce the algorithm's security.
37//! For ML-KEM in particular, these are:
38//!
39//! * `ML-KEM.keygen_internal(seed)` requires a seed either written as `seed: [u8;64]` sometimes decomposed into `(d: [u8;32], z: [u8;32])`.
40//! * `ML-KEM.encaps_internal(pk, m)` requires a random message `m: [u8;32]`.
41//!
42//! Using these functions without sufficiently random values for `seed` and `m` is ill-advised.
43//!
44//!
45//! ## Generating Keys
46//!
47//! ```rust
48//! use bouncycastle_mlkem::MLKEM768;
49//! use bouncycastle_core::traits::KEM;
50//!
51//! let (pk, sk) = MLKEM768::keygen().unwrap();
52//! ```
53//! That's it. That will use the library's default OS-backend RNG.
54//!
55//! Commonly with the ML-KEM algorithm, a 64-byte seed is used as the private key, and expanded into
56//! a full private key as needed. This is offered through the library's [KeyMaterialTrait] object:
57//!
58//! ```rust
59//! use bouncycastle_core::key_material::{KeyMaterial512, KeyType, KeyMaterialTrait};
60//! use bouncycastle_mlkem::{MLKEM768, MLKEMTrait};
61//! use bouncycastle_hex as hex;
62//!
63//! let seed = KeyMaterial512::from_bytes_as_type(
64//! &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
65//! 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap(),
66//! KeyType::Seed,
67//! ).unwrap();
68//!
69//! let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap();
70//! ```
71//!
72//! See [MLKEM] and [MLKEM::decaps_from_seed] for an API that uses a merged
73//! keygen-and-decaps function to that allows you to store the private key only as a 64-byte seed.
74//!
75//! ## Encapsulating and Decapsulating
76//!
77//! ```rust
78//! use bouncycastle_mlkem::{MLKEM768, MLKEMTrait};
79//! use bouncycastle_core::traits::KEM;
80//! use bouncycastle_core::errors::KEMError;
81//!
82//! let (pk, sk) = MLKEM768::keygen().unwrap();
83//!
84//! // Create the shared secret and ciphertext using the public key
85//! let (ss, ct) = MLKEM768::encaps(&pk).unwrap();
86//!
87//! // Recover the shared secret using the private key
88//! let ss1 = match MLKEM768::decaps(&sk, &ct) {
89//! Err(KEMError) => panic!("Error decapsulating"),
90//! Ok(ss) => ss,
91//! };
92//!
93//! assert_eq!(ss, ss1);
94//! ```
95//! And that's the basic usage!
96//!
97//! # Memory Footprint
98//!
99//! The following table lists the size of the on-disk bytes encoding and the in-memory struct size of the
100//! standard key objects:
101//!
102//! | Key Object | PK size on disk | PK size in memory | SK Size on disk | SK size in memory |
103//! |------------|-----------------|-------------------|-----------------|-------------------|
104//! | ML-KEM-512 | 800 | 1056 | 1632 | 2178 |
105//! | ML-KEM-768 | 1184 | 1568 | 2400 | 3202 |
106//! | ML-KEM-1024 | 1568 | 2080 | 3168 | 4226 |
107//!
108//! The following table lists the size of the on-disk bytes encoding and the in-memory struct size of the
109//! expanded key objects that pre-expand the public matrix A for faster repeated encaps() and decaps() operations:
110//!
111//! | Key Object | PK size on disk | PK size in memory | SK Size on disk | SK size in memory |
112//! |----------------------|-----------------|-------------------|-----------------|-------------------|
113//! | ML-KEM-512_expanded | 800 | 3104 | 1632 | 4226 |
114//! | ML-KEM-768_expanded | 1184 | 6176 | 2400 | 7810 |
115//! | ML-KEM-1024_expanded | 1568 | 10272 | 3168 | 12418 |
116//!
117//! All values are in bytes. The "in memory" sizes are measured by rust's `std::mem::size_of`.
118//! Values in parentheses are the usual sizes in our un-optimized implementation in the \[bouncycastle_mldsa] crate.
119//!
120//! # Security
121//! All functionality exposed by this crate is considered secure to use.
122//! In other words, this crate does not contain any "hazmat" except for the obvious points about
123//! handling your private keys properly: if you post your private key to github, or you generate
124//! production keys from a weak seed, I can't help you, that's on you.
125//! It is worth mentioning, however, that if using a [MLKEM::keygen_from_seed], then it is your
126//! responsibility to ensure that the seed is cryptographically random and unpredictable.
127//! And also that [MLKEM::encaps_internal] requires you to provide the randomness, so the ciphertext
128//! will only be as strong as the randomness that you provide.
129//!
130//! A note about cryptographic side-channel attacks: considerable effort has been expended to attempt
131//! to make this implementation constant-time, which generally means that the core mathematical algorithm
132//! code that handles secret data uses bitshift-and-xor type constructions instead of if-and-loop
133//! constructions. That should give this implementation reasonably good resistance to timing and
134//! power analysis key extraction attacks, however: A) this is a "best-effort" and not formally verified,
135//! and B) the Rust compiler does not guarantee constant-time behaviour no matter how clever your code,
136//! so like all Safe Rust code (ie Rust code that does not include inline assembly), we are at the mercy
137//! of the Rust compiler's optimizer for whether our bitshift-and-xor code actually remains
138//! constant-time after compilation.
139
140#![no_std]
141#![forbid(missing_docs)]
142#![forbid(unsafe_code)]
143#![allow(incomplete_features)] // needed because currently generic_const_exprs is experimental
144#![feature(generic_const_exprs)]
145#![feature(adt_const_params)]
146// These are because I'm matching variable names exactly against FIPS 204, for example both 'K' and 'k',
147// or 'A' and 'a' are used and have specific meanings.
148// But need to tell the rust linter to not care.
149#![allow(non_snake_case)]
150#![allow(non_upper_case_globals)]
151// so I can use private traits to hide internal stuff that needs to be generic within the
152// MLKEM implementation, but I don't want accessed from outside, such as FIPS-internal functions.
153#![allow(private_bounds)]
154
155// imports needed just for docs
156#[allow(unused_imports)]
157use bouncycastle_core::key_material::KeyMaterialTrait;
158
159// Exposed only for testing purposes
160pub mod aux_functions;
161mod matrix;
162pub mod mlkem;
163mod mlkem_keys;
164// Exposed only for testing purposes
165pub mod polynomial;
166
167/*** Exported types ***/
168pub use mlkem::{MLKEM, MLKEM512, MLKEM768, MLKEM1024, MLKEMTrait};
169pub use mlkem_keys::{
170 MLKEM512PrivateKey, MLKEM768PrivateKey, MLKEM1024PrivateKey, MLKEMPrivateKey,
171};
172pub use mlkem_keys::{
173 MLKEM512PrivateKeyExpanded, MLKEM768PrivateKeyExpanded, MLKEM1024PrivateKeyExpanded,
174 MLKEMPrivateKeyExpanded,
175};
176pub use mlkem_keys::{MLKEM512PublicKey, MLKEM768PublicKey, MLKEM1024PublicKey, MLKEMPublicKey};
177pub use mlkem_keys::{
178 MLKEM512PublicKeyExpanded, MLKEM768PublicKeyExpanded, MLKEM1024PublicKeyExpanded,
179 MLKEMPublicKeyExpanded,
180};
181pub use mlkem_keys::{MLKEMPrivateKeyTrait, MLKEMPublicKeyTrait};
182
183/*** Exported constants ***/
184pub use mlkem::ML_KEM_512_NAME;
185pub use mlkem::ML_KEM_768_NAME;
186pub use mlkem::ML_KEM_1024_NAME;
187
188pub use mlkem::{MLKEM_RND_LEN, MLKEM_SEED_LEN, MLKEM_SS_LEN};
189
190pub use mlkem::{MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM512_SK_LEN};
191pub use mlkem::{MLKEM768_CT_LEN, MLKEM768_PK_LEN, MLKEM768_SK_LEN};
192pub use mlkem::{MLKEM1024_CT_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN};
193
194pub use matrix::Matrix;