bssl_crypto/mlkem.rs
1// Copyright 2024 The BoringSSL Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! ML-KEM
16//!
17//! ML-KEM is a post-quantum key encapsulation mechanism, specified in
18//! [FIPS 203](https://csrc.nist.gov/pubs/fips/203/final).
19//! A KEM works like public-key encryption, except that the encrypted message is
20//! always a random key chosen by the algorithm.
21//!
22//! ```
23//! use bssl_crypto::mlkem;
24//!
25//! // Generate a key pair.
26//! let (serialized_public_key, private_key, private_seed) = mlkem::PrivateKey768::generate();
27//!
28//! // Send `serialized_public_key` to the sender. The sender parses it:
29//! let public_key = mlkem::PublicKey768::parse(&serialized_public_key).unwrap();
30//!
31//! // Generate and encrypt a shared secret key to that public key.
32//! let (ciphertext, shared_key) = public_key.encapsulate();
33//!
34//! // Send `ciphertext` to the holder of the private key.
35//! let shared_key2 = private_key.decapsulate(&ciphertext).unwrap();
36//! assert_eq!(shared_key, shared_key2);
37//!
38//! // `shared_key` would then be used with an algorithm from the `aead` module
39//! // to encrypt and authenticate data. The two keys may not match and so it's
40//! // critical to use an authenticated encryption mechanism to confirm the key.
41//! ```
42
43use crate::{
44 as_cbs, cbb_to_vec, initialized_boxed_struct, initialized_boxed_struct_fallible,
45 with_output_array_fallible, FfiSlice,
46};
47use alloc::{boxed::Box, vec::Vec};
48use core::mem::MaybeUninit;
49
50/// An ML-KEM-768 public key.
51pub struct PublicKey768(Box<bssl_sys::MLKEM768_public_key>);
52
53/// An ML-KEM-768 private key.
54pub struct PrivateKey768(Box<bssl_sys::MLKEM768_private_key>);
55
56/// An ML-KEM-1024 public key.
57///
58/// Use ML-KEM-768 unless you have a good reason to need this larger size.
59pub struct PublicKey1024(Box<bssl_sys::MLKEM1024_public_key>);
60
61/// An ML-KEM-1024 private key.
62///
63/// Use ML-KEM-768 unless you have a good reason to need this larger size.
64pub struct PrivateKey1024(Box<bssl_sys::MLKEM1024_private_key>);
65
66/// The number of bytes in an encoded ML-KEM-768 public key.
67pub const PUBLIC_KEY_BYTES_768: usize = bssl_sys::MLKEM768_PUBLIC_KEY_BYTES as usize;
68
69/// The number of bytes in an ML-KEM seed.
70pub const SEED_BYTES: usize = bssl_sys::MLKEM_SEED_BYTES as usize;
71
72/// The number of bytes in the ML-KEM-768 ciphertext.
73pub const CIPHERTEXT_BYTES_768: usize = bssl_sys::MLKEM768_CIPHERTEXT_BYTES as usize;
74
75/// The number of bytes in an ML-KEM shared secret.
76pub const SHARED_SECRET_BYTES: usize = bssl_sys::MLKEM_SHARED_SECRET_BYTES as usize;
77
78/// The number of bytes in an encoded ML-KEM-1024 public key.
79pub const PUBLIC_KEY_BYTES_1024: usize = bssl_sys::MLKEM1024_PUBLIC_KEY_BYTES as usize;
80
81/// The number of bytes in the ML-KEM-1024 ciphertext.
82pub const CIPHERTEXT_BYTES_1024: usize = bssl_sys::MLKEM1024_CIPHERTEXT_BYTES as usize;
83
84impl PublicKey768 {
85 /// Parse a public key from NIST's defined format.
86 pub fn parse(encoded: &[u8]) -> Option<Self> {
87 let mut cbs = as_cbs(encoded);
88 unsafe {
89 initialized_boxed_struct_fallible(|pub_key| {
90 // Safety: `pub_key` is the correct size via the type system and
91 // is fully written if this function returns 1.
92 bssl_sys::MLKEM768_parse_public_key(pub_key, &mut cbs) == 1 && cbs.len == 0
93 })
94 }
95 .map(Self)
96 }
97
98 /// Return the serialization of this public key.
99 pub fn to_bytes(&self) -> Vec<u8> {
100 unsafe {
101 cbb_to_vec(PUBLIC_KEY_BYTES_768, |cbb| {
102 let ok = bssl_sys::MLKEM768_marshal_public_key(cbb, &*self.0);
103 // `MLKEM768_marshal_public_key` only fails if it cannot
104 // allocate memory, but `cbb_to_vec` handles allocation.
105 assert_eq!(ok, 1);
106 })
107 }
108 }
109
110 /// Generate a secret key and encrypt it to this public key, returning the
111 /// ciphertext and the shared secret key.
112 pub fn encapsulate(&self) -> (Vec<u8>, [u8; SHARED_SECRET_BYTES]) {
113 let mut ciphertext = Box::new_uninit_slice(CIPHERTEXT_BYTES_768);
114 let mut shared_secret = MaybeUninit::<[u8; SHARED_SECRET_BYTES]>::uninit();
115
116 unsafe {
117 // Safety: the two buffer arguments are sized correctly and
118 // always fully written.
119 bssl_sys::MLKEM768_encap(
120 ciphertext.as_mut_ptr() as *mut u8,
121 shared_secret.as_mut_ptr() as *mut u8,
122 &*self.0,
123 );
124 (ciphertext.assume_init().into(), shared_secret.assume_init())
125 }
126 }
127}
128
129impl PrivateKey768 {
130 /// Generates a random public/private key pair returning a serialized public
131 /// key, a private key, and a private seed value that can be used to
132 /// regenerate the same private key in the future.
133 pub fn generate() -> (Vec<u8>, PrivateKey768, [u8; SEED_BYTES]) {
134 let mut public_key = Box::new_uninit_slice(PUBLIC_KEY_BYTES_768);
135 let mut private_key = Box::new(MaybeUninit::uninit());
136 let mut seed = MaybeUninit::<[u8; SEED_BYTES]>::uninit();
137
138 unsafe {
139 // Safety: the two buffer arguments are sized correctly and
140 // always fully written. `private_key` is sized correctly via
141 // the type system.
142 bssl_sys::MLKEM768_generate_key(
143 public_key.as_mut_ptr() as *mut u8,
144 seed.as_mut_ptr() as *mut u8,
145 private_key.as_mut_ptr(),
146 );
147
148 (
149 public_key.assume_init().into(),
150 Self(private_key.assume_init()),
151 seed.assume_init(),
152 )
153 }
154 }
155
156 /// Regenerate a private key from a seed value.
157 pub fn from_seed(seed: &[u8; SEED_BYTES]) -> Self {
158 Self(unsafe {
159 initialized_boxed_struct(|priv_key| {
160 // Safety: `priv_key` is correctly sized by the type system and
161 // is always fully written.
162 let ok = bssl_sys::MLKEM768_private_key_from_seed(
163 priv_key,
164 seed.as_ffi_ptr(),
165 seed.len(),
166 );
167 // Since the seed value has the correct length, this function can
168 // never fail.
169 assert_eq!(ok, 1);
170 })
171 })
172 }
173
174 /// Derives the public key corresponding to this private key.
175 pub fn to_public_key(&self) -> PublicKey768 {
176 PublicKey768(unsafe {
177 initialized_boxed_struct(|pub_key| {
178 bssl_sys::MLKEM768_public_from_private(pub_key, &*self.0);
179 })
180 })
181 }
182
183 /// Decapsulates a shared secret from a ciphertext. This function only
184 /// returns `None` if ciphertext is the wrong length. For invalid
185 /// ciphertexts it returns a key that will always be the same for the
186 /// same `ciphertext` and private key, but which appears to be random
187 /// unless one has access to the private key. These alternatives occur in
188 /// constant time. Any subsequent symmetric encryption using the result
189 /// must use an authenticated encryption scheme in order to discover the
190 /// decapsulation failure.
191 pub fn decapsulate(&self, ciphertext: &[u8]) -> Option<[u8; SHARED_SECRET_BYTES]> {
192 unsafe {
193 with_output_array_fallible(|out, _| {
194 // Safety: `out` is the correct size via the type system and is
195 // always fully written if the return value is one.
196 bssl_sys::MLKEM768_decap(out, ciphertext.as_ffi_ptr(), ciphertext.len(), &*self.0)
197 == 1
198 })
199 }
200 }
201}
202
203impl PublicKey1024 {
204 /// Parse a public key from NIST's defined format.
205 pub fn parse(encoded: &[u8]) -> Option<Self> {
206 let mut cbs = as_cbs(encoded);
207 unsafe {
208 initialized_boxed_struct_fallible(|pub_key| {
209 // Safety: `pub_key` is the correct size via the type system and
210 // is fully written if this function returns 1.
211 bssl_sys::MLKEM1024_parse_public_key(pub_key, &mut cbs) == 1 && cbs.len == 0
212 })
213 }
214 .map(Self)
215 }
216
217 /// Return the serialization of this public key.
218 pub fn to_bytes(&self) -> Vec<u8> {
219 unsafe {
220 cbb_to_vec(PUBLIC_KEY_BYTES_1024, |cbb| {
221 let ok = bssl_sys::MLKEM1024_marshal_public_key(cbb, &*self.0);
222 // `MLKEM1024_marshal_public_key` only fails if it cannot
223 // allocate memory, but `cbb_to_vec` handles allocation.
224 assert_eq!(ok, 1);
225 })
226 }
227 }
228
229 /// Generate a secret key and encrypt it to this public key, returning the
230 /// ciphertext and the shared secret key.
231 pub fn encapsulate(&self) -> (Vec<u8>, [u8; SHARED_SECRET_BYTES]) {
232 let mut ciphertext = Box::new_uninit_slice(CIPHERTEXT_BYTES_1024);
233 let mut shared_secret = MaybeUninit::<[u8; SHARED_SECRET_BYTES]>::uninit();
234
235 unsafe {
236 // Safety: the two buffer arguments are sized correctly and
237 // always fully written.
238 bssl_sys::MLKEM1024_encap(
239 ciphertext.as_mut_ptr() as *mut u8,
240 shared_secret.as_mut_ptr() as *mut u8,
241 &*self.0,
242 );
243 (ciphertext.assume_init().into(), shared_secret.assume_init())
244 }
245 }
246}
247
248impl PrivateKey1024 {
249 /// Generates a random public/private key pair returning a serialized public
250 /// key, a private key, and a private seed value that can be used to
251 /// regenerate the same private key in the future.
252 pub fn generate() -> (Vec<u8>, PrivateKey1024, [u8; SEED_BYTES]) {
253 let mut public_key = Box::new_uninit_slice(PUBLIC_KEY_BYTES_1024);
254 let mut private_key = Box::new(MaybeUninit::uninit());
255 let mut seed = MaybeUninit::<[u8; SEED_BYTES]>::uninit();
256
257 unsafe {
258 // Safety: the two buffer arguments are sized correctly and
259 // always fully written. `private_key` is sized correctly via
260 // the type system.
261 bssl_sys::MLKEM1024_generate_key(
262 public_key.as_mut_ptr() as *mut u8,
263 seed.as_mut_ptr() as *mut u8,
264 private_key.as_mut_ptr(),
265 );
266
267 (
268 public_key.assume_init().into(),
269 Self(private_key.assume_init()),
270 seed.assume_init(),
271 )
272 }
273 }
274
275 /// Regenerate a private key from a seed value.
276 pub fn from_seed(seed: &[u8; SEED_BYTES]) -> Self {
277 Self(unsafe {
278 initialized_boxed_struct(|priv_key| {
279 // Safety: `priv_key` is correctly sized by the type system and
280 // is always fully written.
281 let ok = bssl_sys::MLKEM1024_private_key_from_seed(
282 priv_key,
283 seed.as_ffi_ptr(),
284 seed.len(),
285 );
286 // Since the seed value has the correct length, this function can
287 // never fail.
288 assert_eq!(ok, 1);
289 })
290 })
291 }
292
293 /// Derives the public key corresponding to this private key.
294 pub fn to_public_key(&self) -> PublicKey1024 {
295 PublicKey1024(unsafe {
296 initialized_boxed_struct(|pub_key| {
297 bssl_sys::MLKEM1024_public_from_private(pub_key, &*self.0);
298 })
299 })
300 }
301
302 /// Decapsulates a shared secret from a ciphertext. This function only
303 /// returns `None` if ciphertext is the wrong length. For invalid
304 /// ciphertexts it returns a key that will always be the same for the
305 /// same `ciphertext` and private key, but which appears to be random
306 /// unless one has access to the private key. These alternatives occur in
307 /// constant time. Any subsequent symmetric encryption using the result
308 /// must use an authenticated encryption scheme in order to discover the
309 /// decapsulation failure.
310 pub fn decapsulate(&self, ciphertext: &[u8]) -> Option<[u8; SHARED_SECRET_BYTES]> {
311 unsafe {
312 with_output_array_fallible(|out, _| {
313 // Safety: `out` is the correct size via the type system and is
314 // always fully written if the return value is one.
315 bssl_sys::MLKEM1024_decap(out, ciphertext.as_ffi_ptr(), ciphertext.len(), &*self.0)
316 == 1
317 })
318 }
319 }
320}
321
322#[cfg(test)]
323mod test {
324 use super::*;
325
326 #[test]
327 fn basic_768() {
328 let (serialized_public_key, _private_key, private_seed) = PrivateKey768::generate();
329 let public_key = PublicKey768::parse(&serialized_public_key).unwrap();
330 let (ciphertext, shared_key) = public_key.encapsulate();
331 let private_key2 = PrivateKey768::from_seed(&private_seed);
332 let shared_key2 = private_key2.decapsulate(&ciphertext).unwrap();
333 assert_eq!(shared_key, shared_key2);
334 }
335
336 #[test]
337 fn basic_1024() {
338 let (serialized_public_key, _private_key, private_seed) = PrivateKey1024::generate();
339 let public_key = PublicKey1024::parse(&serialized_public_key).unwrap();
340 let (ciphertext, shared_key) = public_key.encapsulate();
341 let private_key2 = PrivateKey1024::from_seed(&private_seed);
342 let shared_key2 = private_key2.decapsulate(&ciphertext).unwrap();
343 assert_eq!(shared_key, shared_key2);
344 }
345
346 #[test]
347 fn wrong_length_ciphertext() {
348 let (_serialized_public_key, private_key, _private_seed) = PrivateKey768::generate();
349 assert!(matches!(private_key.decapsulate(&[0u8, 1, 2, 3]), None));
350
351 let (_serialized_public_key, private_key, _private_seed) = PrivateKey1024::generate();
352 assert!(matches!(private_key.decapsulate(&[0u8, 1, 2, 3]), None));
353 }
354
355 #[test]
356 fn wrong_length_public_key() {
357 assert!(matches!(PublicKey768::parse(&[0u8, 1, 2, 3]), None));
358 assert!(matches!(PublicKey1024::parse(&[0u8, 1, 2, 3]), None));
359 }
360
361 #[test]
362 fn marshal_public_key_768() {
363 let (serialized_public_key, private_key, _) = PrivateKey768::generate();
364 let public_key = PublicKey768::parse(&serialized_public_key).unwrap();
365 assert_eq!(serialized_public_key, public_key.to_bytes());
366 assert_eq!(
367 serialized_public_key,
368 private_key.to_public_key().to_bytes()
369 );
370 }
371
372 #[test]
373 fn marshal_public_key_1024() {
374 let (serialized_public_key, private_key, _) = PrivateKey1024::generate();
375 let public_key = PublicKey1024::parse(&serialized_public_key).unwrap();
376 assert_eq!(serialized_public_key, public_key.to_bytes());
377 assert_eq!(
378 serialized_public_key,
379 private_key.to_public_key().to_bytes()
380 );
381 }
382}