// Copyright 2024 The BoringSSL Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! 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); /// An ML-KEM-768 private key. pub struct PrivateKey768(Box); /// 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); /// 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); /// 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 { 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 { 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; 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, 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 { 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 { 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; 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, 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() ); } }