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, MLKEMTrait};
49//!
50//! let (pk, sk) = MLKEM768::keygen().unwrap();
51//! ```
52//! That's it. That will use the library's default OS-backend RNG.
53//!
54//! Commonly with the ML-KEM algorithm, a 64-byte seed is used as the private key, and expanded into
55//! a full private key as needed. This is offered through the library's [`KeyMaterialTrait`] object:
56//!
57//! ```rust
58//! use bouncycastle_core::key_material::{KeyMaterial512, KeyType, KeyMaterialTrait};
59//! use bouncycastle_mlkem::{MLKEM768, MLKEMTrait};
60//! use bouncycastle_hex as hex;
61//!
62//! let seed = KeyMaterial512::from_bytes_as_type(
63//! &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
64//! 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap(),
65//! KeyType::Seed,
66//! ).unwrap();
67//!
68//! let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap();
69//! ```
70//!
71//! See [`MLKEM`] and [`MLKEM::decaps_from_seed`] for an API that uses a merged
72//! keygen-and-decaps function to that allows you to store the private key only as a 64-byte seed.
73//!
74//! ## Encapsulating and Decapsulating
75//!
76//! ```rust
77//! use bouncycastle_mlkem::{MLKEM768, MLKEMTrait};
78//! use bouncycastle_core::traits::{KEMEncapsulator, KEMDecapsulator};
79//! use bouncycastle_core::errors::KEMError;
80//!
81//! let (pk, sk) = MLKEM768::keygen().unwrap();
82//!
83//! // Create the shared secret and ciphertext using the public key
84//! let (ss, ct) = MLKEM768::encaps(&pk).unwrap();
85//!
86//! // Recover the shared secret using the private key
87//! let ss1 = match MLKEM768::decaps(&sk, &ct) {
88//! Err(KEMError) => panic!("Error decapsulating"),
89//! Ok(ss) => ss,
90//! };
91//!
92//! assert_eq!(ss, ss1);
93//! ```
94//! And that's the basic usage!
95//!
96//! # Memory Footprint
97//!
98//! The following table lists the size of the on-disk bytes encoding and the in-memory struct size of the
99//! standard key objects:
100//!
101//! | Key Object | PK size on disk | PK size in memory | SK Size on disk | SK size in memory |
102//! |------------|-----------------|-------------------|-----------------|-------------------|
103//! | ML-KEM-512 | 800 | 1056 | 1632 | 2178 |
104//! | ML-KEM-768 | 1184 | 1568 | 2400 | 3202 |
105//! | ML-KEM-1024 | 1568 | 2080 | 3168 | 4226 |
106//!
107//! The following table lists the size of the on-disk bytes encoding and the in-memory struct size of the
108//! expanded key objects that pre-expand the public matrix A for faster repeated encaps() and decaps() operations:
109//!
110//! | Key Object | PK size on disk | PK size in memory | SK Size on disk | SK size in memory |
111//! |----------------------|-----------------|-------------------|-----------------|-------------------|
112//! | ML-KEM-512_expanded | 800 | 3104 | 1632 | 4226 |
113//! | ML-KEM-768_expanded | 1184 | 6176 | 2400 | 7810 |
114//! | ML-KEM-1024_expanded | 1568 | 10272 | 3168 | 12418 |
115//!
116//! All values are in bytes. The "in memory" sizes are measured by rust's `std::mem::size_of`.
117//! Values in parentheses are the usual sizes in the un-optimized implementation in the \[bouncycastle_mldsa] crate.
118//!
119//! # 🚨 Security 🚨
120//!
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, that use is unsupported
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), the Rust compiler's optimizer
137//! determines whether the bitshift-and-xor code actually remains
138//! constant-time after compilation.
139
140#![no_std]
141#![forbid(unsafe_code)]
142#![forbid(missing_docs)]
143// These are because variable names are matched exactly against FIPS 204, for example both 'K' and 'k',
144// or 'A' and 'a' are used and have specific meanings.
145// But need to tell the rust linter to not care.
146#![allow(non_snake_case)]
147#![allow(non_upper_case_globals)]
148// so private traits can hide internal items that need to be generic within the
149// MLKEM implementation, but should not be accessed from outside, such as FIPS-internal functions.
150#![allow(private_bounds)]
151
152// imports needed just for docs
153#[allow(unused_imports)]
154use bouncycastle_core::key_material::KeyMaterialTrait;
155
156pub mod aux_functions;
157mod matrix;
158pub mod mlkem;
159mod mlkem_keys;
160pub mod polynomial;
161
162/*** Exported types ***/
163pub use mlkem::{MLKEM, MLKEM512, MLKEM768, MLKEM1024, MLKEMTrait};
164pub use mlkem_keys::{
165 MLKEM512PrivateKey, MLKEM768PrivateKey, MLKEM1024PrivateKey, MLKEMPrivateKey,
166};
167pub use mlkem_keys::{
168 MLKEM512PrivateKeyExpanded, MLKEM768PrivateKeyExpanded, MLKEM1024PrivateKeyExpanded,
169 MLKEMPrivateKeyExpanded,
170};
171pub use mlkem_keys::{MLKEM512PublicKey, MLKEM768PublicKey, MLKEM1024PublicKey, MLKEMPublicKey};
172pub use mlkem_keys::{
173 MLKEM512PublicKeyExpanded, MLKEM768PublicKeyExpanded, MLKEM1024PublicKeyExpanded,
174 MLKEMPublicKeyExpanded,
175};
176pub use mlkem_keys::{MLKEMPrivateKeyTrait, MLKEMPublicKeyTrait};
177
178/*** Exported constants ***/
179pub use mlkem::ML_KEM_512_NAME;
180pub use mlkem::ML_KEM_768_NAME;
181pub use mlkem::ML_KEM_1024_NAME;
182
183pub use mlkem::{MLKEM_RND_LEN, MLKEM_SEED_LEN, MLKEM_SS_LEN};
184
185pub use mlkem::{MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM512_SK_LEN};
186pub use mlkem::{MLKEM768_CT_LEN, MLKEM768_PK_LEN, MLKEM768_SK_LEN};
187pub use mlkem::{MLKEM1024_CT_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN};
188
189pub use matrix::Matrix;
190
191// re-export just so it's visible to unit tests
192pub use polynomial::Polynomial;