| /* Copyright 2024 The BoringSSL Authors |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| //! ML-KEM |
| //! |
| //! ML-KEM is a post-quantum key encapsulation mechanism, specified in |
| //! [FIPS 203](https://csrc.nist.gov/pubs/fips/203/final). |
| //! A KEM works like public-key encryption, except that the encrypted message is |
| //! always a random key chosen by the algorithm. |
| //! |
| //! ``` |
| //! use bssl_crypto::mlkem; |
| //! |
| //! // Generate a key pair. |
| //! let (serialized_public_key, private_key, private_seed) = mlkem::PrivateKey768::generate(); |
| //! |
| //! // Send `serialized_public_key` to the sender. The sender parses it: |
| //! let public_key = mlkem::PublicKey768::parse(&serialized_public_key).unwrap(); |
| //! |
| //! // Generate and encrypt a shared secret key to that public key. |
| //! let (ciphertext, shared_key) = public_key.encapsulate(); |
| //! |
| //! // Send `ciphertext` to the holder of the private key. |
| //! let shared_key2 = private_key.decapsulate(&ciphertext).unwrap(); |
| //! assert_eq!(shared_key, shared_key2); |
| //! |
| //! // `shared_key` would then be used with an algorithm from the `aead` module |
| //! // to encrypt and authenticate data. The two keys may not match and so it's |
| //! // critical to use an authenticated encryption mechanism to confirm the key. |
| //! ``` |
| |
| use crate::{ |
| as_cbs, cbb_to_vec, initialized_boxed_struct, initialized_boxed_struct_fallible, |
| with_output_array_fallible, FfiSlice, |
| }; |
| use alloc::{boxed::Box, vec::Vec}; |
| use core::mem::MaybeUninit; |
| |
| /// An ML-KEM-768 public key. |
| pub struct PublicKey768(Box<bssl_sys::MLKEM768_public_key>); |
| |
| /// An ML-KEM-768 private key. |
| pub struct PrivateKey768(Box<bssl_sys::MLKEM768_private_key>); |
| |
| /// An ML-KEM-1024 public key. |
| /// |
| /// Use ML-KEM-768 unless you have a good reason to need this larger size. |
| pub struct PublicKey1024(Box<bssl_sys::MLKEM1024_public_key>); |
| |
| /// An ML-KEM-1024 private key. |
| /// |
| /// Use ML-KEM-768 unless you have a good reason to need this larger size. |
| pub struct PrivateKey1024(Box<bssl_sys::MLKEM1024_private_key>); |
| |
| /// The number of bytes in an encoded ML-KEM-768 public key. |
| pub const PUBLIC_KEY_BYTES_768: usize = bssl_sys::MLKEM768_PUBLIC_KEY_BYTES as usize; |
| |
| /// The number of bytes in an ML-KEM seed. |
| pub const SEED_BYTES: usize = bssl_sys::MLKEM_SEED_BYTES as usize; |
| |
| /// The number of bytes in the ML-KEM-768 ciphertext. |
| pub const CIPHERTEXT_BYTES_768: usize = bssl_sys::MLKEM768_CIPHERTEXT_BYTES as usize; |
| |
| /// The number of bytes in an ML-KEM shared secret. |
| pub const SHARED_SECRET_BYTES: usize = bssl_sys::MLKEM_SHARED_SECRET_BYTES as usize; |
| |
| /// The number of bytes in an encoded ML-KEM-1024 public key. |
| pub const PUBLIC_KEY_BYTES_1024: usize = bssl_sys::MLKEM1024_PUBLIC_KEY_BYTES as usize; |
| |
| /// The number of bytes in the ML-KEM-1024 ciphertext. |
| pub const CIPHERTEXT_BYTES_1024: usize = bssl_sys::MLKEM1024_CIPHERTEXT_BYTES as usize; |
| |
| impl PublicKey768 { |
| /// Parse a public key from NIST's defined format. |
| pub fn parse(encoded: &[u8]) -> Option<Self> { |
| let mut cbs = as_cbs(encoded); |
| unsafe { |
| initialized_boxed_struct_fallible(|pub_key| { |
| // Safety: `pub_key` is the correct size via the type system and |
| // is fully written if this function returns 1. |
| bssl_sys::MLKEM768_parse_public_key(pub_key, &mut cbs) == 1 && cbs.len == 0 |
| }) |
| } |
| .map(Self) |
| } |
| |
| /// Return the serialization of this public key. |
| pub fn to_bytes(&self) -> Vec<u8> { |
| unsafe { |
| cbb_to_vec(PUBLIC_KEY_BYTES_768, |cbb| { |
| let ok = bssl_sys::MLKEM768_marshal_public_key(cbb, &*self.0); |
| // `MLKEM768_marshal_public_key` only fails if it cannot |
| // allocate memory, but `cbb_to_vec` handles allocation. |
| assert_eq!(ok, 1); |
| }) |
| } |
| } |
| |
| /// Generate a secret key and encrypt it to this public key, returning the |
| /// ciphertext and the shared secret key. |
| pub fn encapsulate(&self) -> (Vec<u8>, [u8; SHARED_SECRET_BYTES]) { |
| let mut ciphertext = Box::new_uninit_slice(CIPHERTEXT_BYTES_768); |
| let mut shared_secret = MaybeUninit::<[u8; SHARED_SECRET_BYTES]>::uninit(); |
| |
| unsafe { |
| // Safety: the two buffer arguments are sized correctly and |
| // always fully written. |
| bssl_sys::MLKEM768_encap( |
| ciphertext.as_mut_ptr() as *mut u8, |
| shared_secret.as_mut_ptr() as *mut u8, |
| &*self.0, |
| ); |
| (ciphertext.assume_init().into(), shared_secret.assume_init()) |
| } |
| } |
| } |
| |
| impl PrivateKey768 { |
| /// Generates a random public/private key pair returning a serialized public |
| /// key, a private key, and a private seed value that can be used to |
| /// regenerate the same private key in the future. |
| pub fn generate() -> (Vec<u8>, PrivateKey768, [u8; SEED_BYTES]) { |
| let mut public_key = Box::new_uninit_slice(PUBLIC_KEY_BYTES_768); |
| let mut private_key = Box::new(MaybeUninit::uninit()); |
| let mut seed = MaybeUninit::<[u8; SEED_BYTES]>::uninit(); |
| |
| unsafe { |
| // Safety: the two buffer arguments are sized correctly and |
| // always fully written. `private_key` is sized correctly via |
| // the type system. |
| bssl_sys::MLKEM768_generate_key( |
| public_key.as_mut_ptr() as *mut u8, |
| seed.as_mut_ptr() as *mut u8, |
| private_key.as_mut_ptr(), |
| ); |
| |
| ( |
| public_key.assume_init().into(), |
| Self(private_key.assume_init()), |
| seed.assume_init(), |
| ) |
| } |
| } |
| |
| /// Regenerate a private key from a seed value. |
| pub fn from_seed(seed: &[u8; SEED_BYTES]) -> Self { |
| Self(unsafe { |
| initialized_boxed_struct(|priv_key| { |
| // Safety: `priv_key` is correctly sized by the type system and |
| // is always fully written. |
| let ok = bssl_sys::MLKEM768_private_key_from_seed( |
| priv_key, |
| seed.as_ffi_ptr(), |
| seed.len(), |
| ); |
| // Since the seed value has the correct length, this function can |
| // never fail. |
| assert_eq!(ok, 1); |
| }) |
| }) |
| } |
| |
| /// Derives the public key corresponding to this private key. |
| pub fn to_public_key(&self) -> PublicKey768 { |
| PublicKey768(unsafe { |
| initialized_boxed_struct(|pub_key| { |
| bssl_sys::MLKEM768_public_from_private(pub_key, &*self.0); |
| }) |
| }) |
| } |
| |
| /// Decapsulates a shared secret from a ciphertext. This function only |
| /// returns `None` if ciphertext is the wrong length. For invalid |
| /// ciphertexts it returns a key that will always be the same for the |
| /// same `ciphertext` and private key, but which appears to be random |
| /// unless one has access to the private key. These alternatives occur in |
| /// constant time. Any subsequent symmetric encryption using the result |
| /// must use an authenticated encryption scheme in order to discover the |
| /// decapsulation failure. |
| pub fn decapsulate(&self, ciphertext: &[u8]) -> Option<[u8; SHARED_SECRET_BYTES]> { |
| unsafe { |
| with_output_array_fallible(|out, _| { |
| // Safety: `out` is the correct size via the type system and is |
| // always fully written if the return value is one. |
| bssl_sys::MLKEM768_decap(out, ciphertext.as_ffi_ptr(), ciphertext.len(), &*self.0) |
| == 1 |
| }) |
| } |
| } |
| } |
| |
| impl PublicKey1024 { |
| /// Parse a public key from NIST's defined format. |
| pub fn parse(encoded: &[u8]) -> Option<Self> { |
| let mut cbs = as_cbs(encoded); |
| unsafe { |
| initialized_boxed_struct_fallible(|pub_key| { |
| // Safety: `pub_key` is the correct size via the type system and |
| // is fully written if this function returns 1. |
| bssl_sys::MLKEM1024_parse_public_key(pub_key, &mut cbs) == 1 && cbs.len == 0 |
| }) |
| } |
| .map(Self) |
| } |
| |
| /// Return the serialization of this public key. |
| pub fn to_bytes(&self) -> Vec<u8> { |
| unsafe { |
| cbb_to_vec(PUBLIC_KEY_BYTES_1024, |cbb| { |
| let ok = bssl_sys::MLKEM1024_marshal_public_key(cbb, &*self.0); |
| // `MLKEM1024_marshal_public_key` only fails if it cannot |
| // allocate memory, but `cbb_to_vec` handles allocation. |
| assert_eq!(ok, 1); |
| }) |
| } |
| } |
| |
| /// Generate a secret key and encrypt it to this public key, returning the |
| /// ciphertext and the shared secret key. |
| pub fn encapsulate(&self) -> (Vec<u8>, [u8; SHARED_SECRET_BYTES]) { |
| let mut ciphertext = Box::new_uninit_slice(CIPHERTEXT_BYTES_1024); |
| let mut shared_secret = MaybeUninit::<[u8; SHARED_SECRET_BYTES]>::uninit(); |
| |
| unsafe { |
| // Safety: the two buffer arguments are sized correctly and |
| // always fully written. |
| bssl_sys::MLKEM1024_encap( |
| ciphertext.as_mut_ptr() as *mut u8, |
| shared_secret.as_mut_ptr() as *mut u8, |
| &*self.0, |
| ); |
| (ciphertext.assume_init().into(), shared_secret.assume_init()) |
| } |
| } |
| } |
| |
| impl PrivateKey1024 { |
| /// Generates a random public/private key pair returning a serialized public |
| /// key, a private key, and a private seed value that can be used to |
| /// regenerate the same private key in the future. |
| pub fn generate() -> (Vec<u8>, PrivateKey1024, [u8; SEED_BYTES]) { |
| let mut public_key = Box::new_uninit_slice(PUBLIC_KEY_BYTES_1024); |
| let mut private_key = Box::new(MaybeUninit::uninit()); |
| let mut seed = MaybeUninit::<[u8; SEED_BYTES]>::uninit(); |
| |
| unsafe { |
| // Safety: the two buffer arguments are sized correctly and |
| // always fully written. `private_key` is sized correctly via |
| // the type system. |
| bssl_sys::MLKEM1024_generate_key( |
| public_key.as_mut_ptr() as *mut u8, |
| seed.as_mut_ptr() as *mut u8, |
| private_key.as_mut_ptr(), |
| ); |
| |
| ( |
| public_key.assume_init().into(), |
| Self(private_key.assume_init()), |
| seed.assume_init(), |
| ) |
| } |
| } |
| |
| /// Regenerate a private key from a seed value. |
| pub fn from_seed(seed: &[u8; SEED_BYTES]) -> Self { |
| Self(unsafe { |
| initialized_boxed_struct(|priv_key| { |
| // Safety: `priv_key` is correctly sized by the type system and |
| // is always fully written. |
| let ok = bssl_sys::MLKEM1024_private_key_from_seed( |
| priv_key, |
| seed.as_ffi_ptr(), |
| seed.len(), |
| ); |
| // Since the seed value has the correct length, this function can |
| // never fail. |
| assert_eq!(ok, 1); |
| }) |
| }) |
| } |
| |
| /// Derives the public key corresponding to this private key. |
| pub fn to_public_key(&self) -> PublicKey1024 { |
| PublicKey1024(unsafe { |
| initialized_boxed_struct(|pub_key| { |
| bssl_sys::MLKEM1024_public_from_private(pub_key, &*self.0); |
| }) |
| }) |
| } |
| |
| /// Decapsulates a shared secret from a ciphertext. This function only |
| /// returns `None` if ciphertext is the wrong length. For invalid |
| /// ciphertexts it returns a key that will always be the same for the |
| /// same `ciphertext` and private key, but which appears to be random |
| /// unless one has access to the private key. These alternatives occur in |
| /// constant time. Any subsequent symmetric encryption using the result |
| /// must use an authenticated encryption scheme in order to discover the |
| /// decapsulation failure. |
| pub fn decapsulate(&self, ciphertext: &[u8]) -> Option<[u8; SHARED_SECRET_BYTES]> { |
| unsafe { |
| with_output_array_fallible(|out, _| { |
| // Safety: `out` is the correct size via the type system and is |
| // always fully written if the return value is one. |
| bssl_sys::MLKEM1024_decap(out, ciphertext.as_ffi_ptr(), ciphertext.len(), &*self.0) |
| == 1 |
| }) |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| |
| #[test] |
| fn basic_768() { |
| let (serialized_public_key, _private_key, private_seed) = PrivateKey768::generate(); |
| let public_key = PublicKey768::parse(&serialized_public_key).unwrap(); |
| let (ciphertext, shared_key) = public_key.encapsulate(); |
| let private_key2 = PrivateKey768::from_seed(&private_seed); |
| let shared_key2 = private_key2.decapsulate(&ciphertext).unwrap(); |
| assert_eq!(shared_key, shared_key2); |
| } |
| |
| #[test] |
| fn basic_1024() { |
| let (serialized_public_key, _private_key, private_seed) = PrivateKey1024::generate(); |
| let public_key = PublicKey1024::parse(&serialized_public_key).unwrap(); |
| let (ciphertext, shared_key) = public_key.encapsulate(); |
| let private_key2 = PrivateKey1024::from_seed(&private_seed); |
| let shared_key2 = private_key2.decapsulate(&ciphertext).unwrap(); |
| assert_eq!(shared_key, shared_key2); |
| } |
| |
| #[test] |
| fn wrong_length_ciphertext() { |
| let (_serialized_public_key, private_key, _private_seed) = PrivateKey768::generate(); |
| assert!(matches!(private_key.decapsulate(&[0u8, 1, 2, 3]), None)); |
| |
| let (_serialized_public_key, private_key, _private_seed) = PrivateKey1024::generate(); |
| assert!(matches!(private_key.decapsulate(&[0u8, 1, 2, 3]), None)); |
| } |
| |
| #[test] |
| fn wrong_length_public_key() { |
| assert!(matches!(PublicKey768::parse(&[0u8, 1, 2, 3]), None)); |
| assert!(matches!(PublicKey1024::parse(&[0u8, 1, 2, 3]), None)); |
| } |
| |
| #[test] |
| fn marshal_public_key_768() { |
| let (serialized_public_key, private_key, _) = PrivateKey768::generate(); |
| let public_key = PublicKey768::parse(&serialized_public_key).unwrap(); |
| assert_eq!(serialized_public_key, public_key.to_bytes()); |
| assert_eq!( |
| serialized_public_key, |
| private_key.to_public_key().to_bytes() |
| ); |
| } |
| |
| #[test] |
| fn marshal_public_key_1024() { |
| let (serialized_public_key, private_key, _) = PrivateKey1024::generate(); |
| let public_key = PublicKey1024::parse(&serialized_public_key).unwrap(); |
| assert_eq!(serialized_public_key, public_key.to_bytes()); |
| assert_eq!( |
| serialized_public_key, |
| private_key.to_public_key().to_bytes() |
| ); |
| } |
| } |