Skip to main content

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}