From c83e19996345dd1184c33154817c1dc31d9da7e7 Mon Sep 17 00:00:00 2001 From: Kornel Date: Mon, 2 Feb 2026 15:27:41 +0000 Subject: [PATCH 1/4] Use associated constants --- boring/src/mlkem.rs | 143 ++++++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 78 deletions(-) diff --git a/boring/src/mlkem.rs b/boring/src/mlkem.rs index 5bc9dcb99..5297b2632 100644 --- a/boring/src/mlkem.rs +++ b/boring/src/mlkem.rs @@ -33,9 +33,16 @@ fn cbs_init(data: &[u8]) -> ffi::CBS { } /// Private key seed size (64 bytes). -pub const PRIVATE_KEY_SEED_BYTES: usize = 64; +pub const PRIVATE_KEY_SEED_BYTES: usize = ffi::MLKEM_SEED_BYTES as usize; + /// Shared secret size (32 bytes). -pub const SHARED_SECRET_BYTES: usize = 32; +pub const SHARED_SECRET_BYTES: usize = ffi::MLKEM_SHARED_SECRET_BYTES as usize; + +/// Raw bytes of the private key seed ([`PRIVATE_KEY_SEED_BYTES`] long) +pub type MlKemPrivateKeySeed = [u8; PRIVATE_KEY_SEED_BYTES]; + +/// Raw bytes of the shared secret ([`SHARED_SECRET_BYTES`] long) +pub type MlKemSharedSecret = [u8; SHARED_SECRET_BYTES]; /// ML-KEM variant selection. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -51,8 +58,8 @@ impl MlKemParams { #[must_use] pub const fn public_key_bytes(&self) -> usize { match self { - MlKemParams::MlKem768 => mlkem768::PUBLIC_KEY_BYTES, - MlKemParams::MlKem1024 => mlkem1024::PUBLIC_KEY_BYTES, + MlKemParams::MlKem768 => MlKem768PublicKey::PUBLIC_KEY_BYTES, + MlKemParams::MlKem1024 => MlKem1024PublicKey::PUBLIC_KEY_BYTES, } } @@ -60,8 +67,8 @@ impl MlKemParams { #[must_use] pub const fn ciphertext_bytes(&self) -> usize { match self { - MlKemParams::MlKem768 => mlkem768::CIPHERTEXT_BYTES, - MlKemParams::MlKem1024 => mlkem1024::CIPHERTEXT_BYTES, + MlKemParams::MlKem768 => MlKem768PrivateKey::CIPHERTEXT_BYTES, + MlKemParams::MlKem1024 => MlKem1024PrivateKey::CIPHERTEXT_BYTES, } } } @@ -108,7 +115,7 @@ impl MlKem { /// Generates a new key pair, returning `(public_key, private_key)`. /// /// The private key is a 64-byte seed. Keep it secret. - pub fn generate_key(&self) -> Result<(Vec, [u8; PRIVATE_KEY_SEED_BYTES]), ErrorStack> { + pub fn generate_key(&self) -> Result<(Vec, MlKemPrivateKeySeed), ErrorStack> { match self.params { MlKemParams::MlKem768 => { let (sk, pk) = MlKem768PrivateKey::generate(); @@ -126,7 +133,7 @@ impl MlKem { pub fn encapsulate( &self, public_key: &[u8], - ) -> Result<(Vec, [u8; SHARED_SECRET_BYTES]), ErrorStack> { + ) -> Result<(Vec, MlKemSharedSecret), ErrorStack> { match self.params { MlKemParams::MlKem768 => { let pk = MlKem768PublicKey::from_slice(public_key)?; @@ -146,22 +153,22 @@ impl MlKem { &self, private_key: &[u8], ciphertext: &[u8], - ) -> Result<[u8; SHARED_SECRET_BYTES], ErrorStack> { + ) -> Result { if private_key.len() != PRIVATE_KEY_SEED_BYTES { return Err(ErrorStack::internal_error_str("invalid private key length")); } - let seed_arr: [u8; PRIVATE_KEY_SEED_BYTES] = private_key.try_into().unwrap(); + let seed_arr: MlKemPrivateKeySeed = private_key.try_into().unwrap(); match self.params { MlKemParams::MlKem768 => { - let ct: &[u8; mlkem768::CIPHERTEXT_BYTES] = ciphertext + let ct: &[u8; MlKem768PrivateKey::CIPHERTEXT_BYTES] = ciphertext .try_into() .map_err(|_| ErrorStack::internal_error_str("invalid ciphertext length"))?; let sk = MlKem768PrivateKey::from_seed(seed_arr)?; Ok(sk.decapsulate(ct)) } MlKemParams::MlKem1024 => { - let ct: &[u8; mlkem1024::CIPHERTEXT_BYTES] = ciphertext + let ct: &[u8; MlKem1024PrivateKey::CIPHERTEXT_BYTES] = ciphertext .try_into() .map_err(|_| ErrorStack::internal_error_str("invalid ciphertext length"))?; let sk = MlKem1024PrivateKey::from_seed(seed_arr)?; @@ -171,22 +178,11 @@ impl MlKem { } } -// ML-KEM-768 - -/// Size constants for ML-KEM-768. -pub mod mlkem768 { - use super::ffi; - pub const PUBLIC_KEY_BYTES: usize = ffi::MLKEM768_PUBLIC_KEY_BYTES as usize; - pub const SEED_BYTES: usize = ffi::MLKEM_SEED_BYTES as usize; - pub const CIPHERTEXT_BYTES: usize = ffi::MLKEM768_CIPHERTEXT_BYTES as usize; - pub const SHARED_SECRET_BYTES: usize = ffi::MLKEM_SHARED_SECRET_BYTES as usize; -} - /// ML-KEM-768 private key. /// /// Caches the expanded key for fast decapsulation. struct MlKem768PrivateKey { - seed: [u8; mlkem768::SEED_BYTES], + seed: MlKemPrivateKeySeed, expanded: ffi::MLKEM768_private_key, } @@ -198,15 +194,17 @@ impl Clone for MlKem768PrivateKey { } impl MlKem768PrivateKey { + pub const CIPHERTEXT_BYTES: usize = ffi::MLKEM768_CIPHERTEXT_BYTES as usize; + /// Generate a new key pair. #[must_use] fn generate() -> (MlKem768PrivateKey, MlKem768PublicKey) { // SAFETY: all buffers are out parameters, correctly sized unsafe { ffi::init(); - let mut public_key_bytes: MaybeUninit<[u8; mlkem768::PUBLIC_KEY_BYTES]> = + let mut public_key_bytes: MaybeUninit<[u8; MlKem768PublicKey::PUBLIC_KEY_BYTES]> = MaybeUninit::uninit(); - let mut seed: MaybeUninit<[u8; mlkem768::SEED_BYTES]> = MaybeUninit::uninit(); + let mut seed: MaybeUninit = MaybeUninit::uninit(); let mut expanded: MaybeUninit = MaybeUninit::uninit(); ffi::MLKEM768_generate_key( @@ -236,7 +234,7 @@ impl MlKem768PrivateKey { } /// Restore private key from seed. - fn from_seed(seed: [u8; mlkem768::SEED_BYTES]) -> Result { + fn from_seed(seed: MlKemPrivateKeySeed) -> Result { // SAFETY: seed is 64 bytes, out parameter correctly sized unsafe { ffi::init(); @@ -262,7 +260,7 @@ impl MlKem768PrivateKey { let mut parsed: MaybeUninit = MaybeUninit::uninit(); ffi::MLKEM768_public_from_private(parsed.as_mut_ptr(), &self.expanded); - let mut bytes = [0u8; mlkem768::PUBLIC_KEY_BYTES]; + let mut bytes = [0u8; MlKem768PublicKey::PUBLIC_KEY_BYTES]; let mut cbb: MaybeUninit = MaybeUninit::uninit(); cvt(ffi::CBB_init_fixed( cbb.as_mut_ptr(), @@ -282,14 +280,11 @@ impl MlKem768PrivateKey { } /// Decapsulate to get the shared secret. - fn decapsulate( - &self, - ciphertext: &[u8; mlkem768::CIPHERTEXT_BYTES], - ) -> [u8; mlkem768::SHARED_SECRET_BYTES] { + fn decapsulate(&self, ciphertext: &[u8; Self::CIPHERTEXT_BYTES]) -> MlKemSharedSecret { // SAFETY: expanded key is valid, ciphertext is correctly sized unsafe { ffi::init(); - let mut shared_secret = [0u8; mlkem768::SHARED_SECRET_BYTES]; + let mut shared_secret = [0u8; SHARED_SECRET_BYTES]; ffi::MLKEM768_decap( shared_secret.as_mut_ptr(), @@ -324,8 +319,8 @@ impl Drop for MlKem768PrivateKey { } } -impl AsRef<[u8; mlkem768::SEED_BYTES]> for MlKem768PrivateKey { - fn as_ref(&self) -> &[u8; mlkem768::SEED_BYTES] { +impl AsRef for MlKem768PrivateKey { + fn as_ref(&self) -> &MlKemPrivateKeySeed { &self.seed } } @@ -333,14 +328,16 @@ impl AsRef<[u8; mlkem768::SEED_BYTES]> for MlKem768PrivateKey { /// ML-KEM-768 public key. #[derive(Clone)] struct MlKem768PublicKey { - bytes: [u8; mlkem768::PUBLIC_KEY_BYTES], + bytes: [u8; Self::PUBLIC_KEY_BYTES], parsed: ffi::MLKEM768_public_key, } impl MlKem768PublicKey { + pub const PUBLIC_KEY_BYTES: usize = ffi::MLKEM768_PUBLIC_KEY_BYTES as usize; + /// Parse and validate a public key. fn from_slice(slice: &[u8]) -> Result { - if slice.len() != mlkem768::PUBLIC_KEY_BYTES { + if slice.len() != Self::PUBLIC_KEY_BYTES { return Err(ErrorStack::internal_error_str("invalid public key length")); } @@ -360,7 +357,7 @@ impl MlKem768PublicKey { )); } - let mut bytes = [0u8; mlkem768::PUBLIC_KEY_BYTES]; + let mut bytes = [0u8; Self::PUBLIC_KEY_BYTES]; bytes.copy_from_slice(slice); Ok(Self { bytes, @@ -371,7 +368,7 @@ impl MlKem768PublicKey { /// Raw public key bytes. #[cfg(test)] - fn as_bytes(&self) -> &[u8; mlkem768::PUBLIC_KEY_BYTES] { + fn as_bytes(&self) -> &[u8; Self::PUBLIC_KEY_BYTES] { &self.bytes } @@ -379,14 +376,14 @@ impl MlKem768PublicKey { fn encapsulate( &self, ) -> ( - [u8; mlkem768::CIPHERTEXT_BYTES], - [u8; mlkem768::SHARED_SECRET_BYTES], + [u8; MlKem768PrivateKey::CIPHERTEXT_BYTES], + MlKemSharedSecret, ) { // SAFETY: buffers correctly sized, parsed key is valid unsafe { ffi::init(); - let mut ciphertext = [0u8; mlkem768::CIPHERTEXT_BYTES]; - let mut shared_secret = [0u8; mlkem768::SHARED_SECRET_BYTES]; + let mut ciphertext = [0u8; MlKem768PrivateKey::CIPHERTEXT_BYTES]; + let mut shared_secret = [0u8; SHARED_SECRET_BYTES]; ffi::MLKEM768_encap( ciphertext.as_mut_ptr(), @@ -407,29 +404,18 @@ impl fmt::Debug for MlKem768PublicKey { } } -impl AsRef<[u8; mlkem768::PUBLIC_KEY_BYTES]> for MlKem768PublicKey { - fn as_ref(&self) -> &[u8; mlkem768::PUBLIC_KEY_BYTES] { +impl AsRef<[u8; Self::PUBLIC_KEY_BYTES]> for MlKem768PublicKey { + fn as_ref(&self) -> &[u8; Self::PUBLIC_KEY_BYTES] { &self.bytes } } -// ML-KEM-1024 - -/// Size constants for ML-KEM-1024. -pub mod mlkem1024 { - use super::ffi; - pub const PUBLIC_KEY_BYTES: usize = ffi::MLKEM1024_PUBLIC_KEY_BYTES as usize; - pub const SEED_BYTES: usize = ffi::MLKEM_SEED_BYTES as usize; - pub const CIPHERTEXT_BYTES: usize = ffi::MLKEM1024_CIPHERTEXT_BYTES as usize; - pub const SHARED_SECRET_BYTES: usize = ffi::MLKEM_SHARED_SECRET_BYTES as usize; -} - /// ML-KEM-1024 private key. /// /// Prefer ML-KEM-768 unless you need AES-256 equivalent security. /// Caches the expanded key for fast decapsulation. struct MlKem1024PrivateKey { - seed: [u8; mlkem1024::SEED_BYTES], + seed: MlKemPrivateKeySeed, expanded: ffi::MLKEM1024_private_key, } @@ -441,15 +427,17 @@ impl Clone for MlKem1024PrivateKey { } impl MlKem1024PrivateKey { + pub const CIPHERTEXT_BYTES: usize = ffi::MLKEM1024_CIPHERTEXT_BYTES as usize; + /// Generate a new key pair. #[must_use] fn generate() -> (MlKem1024PrivateKey, MlKem1024PublicKey) { // SAFETY: all buffers are out parameters, correctly sized unsafe { ffi::init(); - let mut public_key_bytes: MaybeUninit<[u8; mlkem1024::PUBLIC_KEY_BYTES]> = + let mut public_key_bytes: MaybeUninit<[u8; MlKem1024PublicKey::PUBLIC_KEY_BYTES]> = MaybeUninit::uninit(); - let mut seed: MaybeUninit<[u8; mlkem1024::SEED_BYTES]> = MaybeUninit::uninit(); + let mut seed: MaybeUninit = MaybeUninit::uninit(); let mut expanded: MaybeUninit = MaybeUninit::uninit(); ffi::MLKEM1024_generate_key( @@ -479,7 +467,7 @@ impl MlKem1024PrivateKey { } /// Restore private key from seed. - fn from_seed(seed: [u8; mlkem1024::SEED_BYTES]) -> Result { + fn from_seed(seed: MlKemPrivateKeySeed) -> Result { // SAFETY: seed is 64 bytes, out parameter correctly sized unsafe { ffi::init(); @@ -505,7 +493,7 @@ impl MlKem1024PrivateKey { let mut parsed: MaybeUninit = MaybeUninit::uninit(); ffi::MLKEM1024_public_from_private(parsed.as_mut_ptr(), &self.expanded); - let mut bytes = [0u8; mlkem1024::PUBLIC_KEY_BYTES]; + let mut bytes = [0u8; MlKem1024PublicKey::PUBLIC_KEY_BYTES]; let mut cbb: MaybeUninit = MaybeUninit::uninit(); cvt(ffi::CBB_init_fixed( cbb.as_mut_ptr(), @@ -525,14 +513,11 @@ impl MlKem1024PrivateKey { } /// Decapsulate to get the shared secret. - fn decapsulate( - &self, - ciphertext: &[u8; mlkem1024::CIPHERTEXT_BYTES], - ) -> [u8; mlkem1024::SHARED_SECRET_BYTES] { + fn decapsulate(&self, ciphertext: &[u8; Self::CIPHERTEXT_BYTES]) -> MlKemSharedSecret { // SAFETY: expanded key is valid, ciphertext is correctly sized unsafe { ffi::init(); - let mut shared_secret = [0u8; mlkem1024::SHARED_SECRET_BYTES]; + let mut shared_secret = [0u8; SHARED_SECRET_BYTES]; ffi::MLKEM1024_decap( shared_secret.as_mut_ptr(), @@ -567,8 +552,8 @@ impl Drop for MlKem1024PrivateKey { } } -impl AsRef<[u8; mlkem1024::SEED_BYTES]> for MlKem1024PrivateKey { - fn as_ref(&self) -> &[u8; mlkem1024::SEED_BYTES] { +impl AsRef for MlKem1024PrivateKey { + fn as_ref(&self) -> &MlKemPrivateKeySeed { &self.seed } } @@ -578,14 +563,16 @@ impl AsRef<[u8; mlkem1024::SEED_BYTES]> for MlKem1024PrivateKey { /// Prefer ML-KEM-768 unless you need AES-256 equivalent security. #[derive(Clone)] struct MlKem1024PublicKey { - bytes: [u8; mlkem1024::PUBLIC_KEY_BYTES], + bytes: [u8; Self::PUBLIC_KEY_BYTES], parsed: ffi::MLKEM1024_public_key, } impl MlKem1024PublicKey { + pub const PUBLIC_KEY_BYTES: usize = ffi::MLKEM1024_PUBLIC_KEY_BYTES as usize; + /// Parse and validate a public key. fn from_slice(slice: &[u8]) -> Result { - if slice.len() != mlkem1024::PUBLIC_KEY_BYTES { + if slice.len() != Self::PUBLIC_KEY_BYTES { return Err(ErrorStack::internal_error_str("invalid public key length")); } @@ -605,7 +592,7 @@ impl MlKem1024PublicKey { )); } - let mut bytes = [0u8; mlkem1024::PUBLIC_KEY_BYTES]; + let mut bytes = [0u8; Self::PUBLIC_KEY_BYTES]; bytes.copy_from_slice(slice); Ok(Self { bytes, @@ -616,7 +603,7 @@ impl MlKem1024PublicKey { /// Raw public key bytes. #[cfg(test)] - fn as_bytes(&self) -> &[u8; mlkem1024::PUBLIC_KEY_BYTES] { + fn as_bytes(&self) -> &[u8; Self::PUBLIC_KEY_BYTES] { &self.bytes } @@ -624,14 +611,14 @@ impl MlKem1024PublicKey { fn encapsulate( &self, ) -> ( - [u8; mlkem1024::CIPHERTEXT_BYTES], - [u8; mlkem1024::SHARED_SECRET_BYTES], + [u8; MlKem1024PrivateKey::CIPHERTEXT_BYTES], + [u8; SHARED_SECRET_BYTES], ) { // SAFETY: buffers correctly sized, parsed key is valid unsafe { ffi::init(); - let mut ciphertext = [0u8; mlkem1024::CIPHERTEXT_BYTES]; - let mut shared_secret = [0u8; mlkem1024::SHARED_SECRET_BYTES]; + let mut ciphertext = [0u8; MlKem1024PrivateKey::CIPHERTEXT_BYTES]; + let mut shared_secret = [0u8; SHARED_SECRET_BYTES]; ffi::MLKEM1024_encap( ciphertext.as_mut_ptr(), @@ -652,8 +639,8 @@ impl fmt::Debug for MlKem1024PublicKey { } } -impl AsRef<[u8; mlkem1024::PUBLIC_KEY_BYTES]> for MlKem1024PublicKey { - fn as_ref(&self) -> &[u8; mlkem1024::PUBLIC_KEY_BYTES] { +impl AsRef<[u8; Self::PUBLIC_KEY_BYTES]> for MlKem1024PublicKey { + fn as_ref(&self) -> &[u8; Self::PUBLIC_KEY_BYTES] { &self.bytes } } From 674d7564c9ace13b593c06b0a4e4d8a9bcb0108e Mon Sep 17 00:00:00 2001 From: Kornel Date: Mon, 2 Feb 2026 22:40:30 +0000 Subject: [PATCH 2/4] Tests don't need AsRef and other accessors --- boring/src/mlkem.rs | 50 ++++++++------------------------------------- 1 file changed, 9 insertions(+), 41 deletions(-) diff --git a/boring/src/mlkem.rs b/boring/src/mlkem.rs index 5297b2632..b10a388ce 100644 --- a/boring/src/mlkem.rs +++ b/boring/src/mlkem.rs @@ -319,12 +319,6 @@ impl Drop for MlKem768PrivateKey { } } -impl AsRef for MlKem768PrivateKey { - fn as_ref(&self) -> &MlKemPrivateKeySeed { - &self.seed - } -} - /// ML-KEM-768 public key. #[derive(Clone)] struct MlKem768PublicKey { @@ -336,6 +330,8 @@ impl MlKem768PublicKey { pub const PUBLIC_KEY_BYTES: usize = ffi::MLKEM768_PUBLIC_KEY_BYTES as usize; /// Parse and validate a public key. + /// + /// The slice must be [`Self::PUBLIC_KEY_BYTES`] long. fn from_slice(slice: &[u8]) -> Result { if slice.len() != Self::PUBLIC_KEY_BYTES { return Err(ErrorStack::internal_error_str("invalid public key length")); @@ -366,12 +362,6 @@ impl MlKem768PublicKey { } } - /// Raw public key bytes. - #[cfg(test)] - fn as_bytes(&self) -> &[u8; Self::PUBLIC_KEY_BYTES] { - &self.bytes - } - /// Encapsulate: returns (ciphertext, shared_secret). fn encapsulate( &self, @@ -404,12 +394,6 @@ impl fmt::Debug for MlKem768PublicKey { } } -impl AsRef<[u8; Self::PUBLIC_KEY_BYTES]> for MlKem768PublicKey { - fn as_ref(&self) -> &[u8; Self::PUBLIC_KEY_BYTES] { - &self.bytes - } -} - /// ML-KEM-1024 private key. /// /// Prefer ML-KEM-768 unless you need AES-256 equivalent security. @@ -552,12 +536,6 @@ impl Drop for MlKem1024PrivateKey { } } -impl AsRef for MlKem1024PrivateKey { - fn as_ref(&self) -> &MlKemPrivateKeySeed { - &self.seed - } -} - /// ML-KEM-1024 public key. /// /// Prefer ML-KEM-768 unless you need AES-256 equivalent security. @@ -570,7 +548,9 @@ struct MlKem1024PublicKey { impl MlKem1024PublicKey { pub const PUBLIC_KEY_BYTES: usize = ffi::MLKEM1024_PUBLIC_KEY_BYTES as usize; - /// Parse and validate a public key. + /// Parse and validate a serialized public key. + /// + /// The slice must be [`Self::PUBLIC_KEY_BYTES`] long. fn from_slice(slice: &[u8]) -> Result { if slice.len() != Self::PUBLIC_KEY_BYTES { return Err(ErrorStack::internal_error_str("invalid public key length")); @@ -601,12 +581,6 @@ impl MlKem1024PublicKey { } } - /// Raw public key bytes. - #[cfg(test)] - fn as_bytes(&self) -> &[u8; Self::PUBLIC_KEY_BYTES] { - &self.bytes - } - /// Encapsulate: returns (ciphertext, shared_secret). fn encapsulate( &self, @@ -639,12 +613,6 @@ impl fmt::Debug for MlKem1024PublicKey { } } -impl AsRef<[u8; Self::PUBLIC_KEY_BYTES]> for MlKem1024PublicKey { - fn as_ref(&self) -> &[u8; Self::PUBLIC_KEY_BYTES] { - &self.bytes - } -} - #[cfg(test)] mod tests { use super::*; @@ -665,7 +633,7 @@ mod tests { #[test] fn seed_roundtrip() { let (sk, pk) = <$priv>::generate(); - let sk2 = <$priv>::from_seed(*sk.as_ref()).unwrap(); + let sk2 = <$priv>::from_seed(sk.seed).unwrap(); let (ct, ss1) = pk.encapsulate(); let ss2 = sk2.decapsulate(&ct); assert_eq!(ss1, ss2); @@ -674,7 +642,7 @@ mod tests { #[test] fn derive_pubkey() { let (sk, pk) = <$priv>::generate(); - assert_eq!(pk.as_bytes(), sk.public_key().unwrap().as_bytes()); + assert_eq!(pk.bytes, sk.public_key().unwrap().bytes); } #[test] @@ -686,8 +654,8 @@ mod tests { #[test] fn from_slice_roundtrip() { let (_, pk) = <$priv>::generate(); - let pk2 = <$pub>::from_slice(pk.as_bytes()).unwrap(); - assert_eq!(pk.as_bytes(), pk2.as_bytes()); + let pk2 = <$pub>::from_slice(&pk.bytes).unwrap(); + assert_eq!(pk.bytes, pk2.bytes); } #[test] From 8428499fe3803946a08ae10e844c647e8e05e066 Mon Sep 17 00:00:00 2001 From: Kornel Date: Mon, 2 Feb 2026 23:40:31 +0000 Subject: [PATCH 3/4] Skip MlKemParams --- boring/src/mlkem.rs | 124 +++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 81 deletions(-) diff --git a/boring/src/mlkem.rs b/boring/src/mlkem.rs index b10a388ce..18569535f 100644 --- a/boring/src/mlkem.rs +++ b/boring/src/mlkem.rs @@ -7,12 +7,11 @@ //! Provides ML-KEM-768 (recommended) and ML-KEM-1024 variants via [`MlKem`]. //! //! ``` -//! use boring::mlkem::{MlKem, MlKemParams}; +//! use boring::mlkem::MlKem; //! -//! let kem = MlKem::new(MlKemParams::MlKem768); -//! let (public_key, private_key) = kem.generate_key().unwrap(); -//! let (ciphertext, shared_secret) = kem.encapsulate(&public_key).unwrap(); -//! let decrypted = kem.decapsulate(&private_key, &ciphertext).unwrap(); +//! let (public_key, private_key) = MlKem::MlKem768.generate_key().unwrap(); +//! let (ciphertext, shared_secret) = MlKem::MlKem768.encapsulate(&public_key).unwrap(); +//! let decrypted = MlKem::MlKem768.decapsulate(&private_key, &ciphertext).unwrap(); //! assert_eq!(shared_secret, decrypted); //! ``` @@ -44,22 +43,31 @@ pub type MlKemPrivateKeySeed = [u8; PRIVATE_KEY_SEED_BYTES]; /// Raw bytes of the shared secret ([`SHARED_SECRET_BYTES`] long) pub type MlKemSharedSecret = [u8; SHARED_SECRET_BYTES]; -/// ML-KEM variant selection. +/// ML-KEM with runtime algorithm selection. Works with byte slices. +/// +/// ``` +/// use boring::mlkem::MlKem; +/// +/// let (public_key, private_key) = MlKem::MlKem768.generate_key().unwrap(); +/// let (ciphertext, shared_secret) = MlKem::MlKem768.encapsulate(&public_key).unwrap(); +/// let decrypted = kem.decapsulate(&private_key, &ciphertext).unwrap(); +/// assert_eq!(shared_secret, decrypted); +/// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum MlKemParams { +pub enum MlKem { /// Recommended. AES-192 equivalent security. MlKem768, /// AES-256 equivalent security. MlKem1024, } -impl MlKemParams { +impl MlKem { /// Returns 1184 for ML-KEM-768, 1568 for ML-KEM-1024. #[must_use] pub const fn public_key_bytes(&self) -> usize { match self { - MlKemParams::MlKem768 => MlKem768PublicKey::PUBLIC_KEY_BYTES, - MlKemParams::MlKem1024 => MlKem1024PublicKey::PUBLIC_KEY_BYTES, + Self::MlKem768 => MlKem768PublicKey::PUBLIC_KEY_BYTES, + Self::MlKem1024 => MlKem1024PublicKey::PUBLIC_KEY_BYTES, } } @@ -67,61 +75,21 @@ impl MlKemParams { #[must_use] pub const fn ciphertext_bytes(&self) -> usize { match self { - MlKemParams::MlKem768 => MlKem768PrivateKey::CIPHERTEXT_BYTES, - MlKemParams::MlKem1024 => MlKem1024PrivateKey::CIPHERTEXT_BYTES, + Self::MlKem768 => MlKem768PrivateKey::CIPHERTEXT_BYTES, + Self::MlKem1024 => MlKem1024PrivateKey::CIPHERTEXT_BYTES, } } -} - -/// ML-KEM with runtime algorithm selection. Works with byte slices. -/// -/// ``` -/// use boring::mlkem::{MlKem, MlKemParams}; -/// -/// let kem = MlKem::new(MlKemParams::MlKem768); -/// let (public_key, private_key) = kem.generate_key().unwrap(); -/// let (ciphertext, shared_secret) = kem.encapsulate(&public_key).unwrap(); -/// let decrypted = kem.decapsulate(&private_key, &ciphertext).unwrap(); -/// assert_eq!(shared_secret, decrypted); -/// ``` -#[derive(Debug, Clone, Copy)] -pub struct MlKem { - params: MlKemParams, -} - -impl MlKem { - /// Creates a new context for the given parameter set. - #[must_use] - pub fn new(params: MlKemParams) -> Self { - ffi::init(); - Self { params } - } - - #[must_use] - pub fn params(&self) -> MlKemParams { - self.params - } - - #[must_use] - pub fn public_key_bytes(&self) -> usize { - self.params.public_key_bytes() - } - - #[must_use] - pub fn ciphertext_bytes(&self) -> usize { - self.params.ciphertext_bytes() - } /// Generates a new key pair, returning `(public_key, private_key)`. /// /// The private key is a 64-byte seed. Keep it secret. pub fn generate_key(&self) -> Result<(Vec, MlKemPrivateKeySeed), ErrorStack> { - match self.params { - MlKemParams::MlKem768 => { + match self { + Self::MlKem768 => { let (sk, pk) = MlKem768PrivateKey::generate(); Ok((pk.bytes.to_vec(), sk.seed)) } - MlKemParams::MlKem1024 => { + Self::MlKem1024 => { let (sk, pk) = MlKem1024PrivateKey::generate(); Ok((pk.bytes.to_vec(), sk.seed)) } @@ -134,13 +102,13 @@ impl MlKem { &self, public_key: &[u8], ) -> Result<(Vec, MlKemSharedSecret), ErrorStack> { - match self.params { - MlKemParams::MlKem768 => { + match self { + Self::MlKem768 => { let pk = MlKem768PublicKey::from_slice(public_key)?; let (ct, ss) = pk.encapsulate(); Ok((ct.to_vec(), ss)) } - MlKemParams::MlKem1024 => { + Self::MlKem1024 => { let pk = MlKem1024PublicKey::from_slice(public_key)?; let (ct, ss) = pk.encapsulate(); Ok((ct.to_vec(), ss)) @@ -159,15 +127,15 @@ impl MlKem { } let seed_arr: MlKemPrivateKeySeed = private_key.try_into().unwrap(); - match self.params { - MlKemParams::MlKem768 => { + match self { + Self::MlKem768 => { let ct: &[u8; MlKem768PrivateKey::CIPHERTEXT_BYTES] = ciphertext .try_into() .map_err(|_| ErrorStack::internal_error_str("invalid ciphertext length"))?; let sk = MlKem768PrivateKey::from_seed(seed_arr)?; Ok(sk.decapsulate(ct)) } - MlKemParams::MlKem1024 => { + Self::MlKem1024 => { let ct: &[u8; MlKem1024PrivateKey::CIPHERTEXT_BYTES] = ciphertext .try_into() .map_err(|_| ErrorStack::internal_error_str("invalid ciphertext length"))?; @@ -686,13 +654,13 @@ mod tests { use super::*; macro_rules! unified_tests { - ($name:ident, $params:expr, $pk_len:expr, $ct_len:expr) => { + ($name:ident, $algorithm:expr, $pk_len:expr, $ct_len:expr) => { mod $name { use super::*; #[test] fn roundtrip() { - let kem = MlKem::new($params); + let kem = $algorithm; let (pk, seed) = kem.generate_key().unwrap(); let (ct, ss1) = kem.encapsulate(&pk).unwrap(); let ss2 = kem.decapsulate(&seed, &ct).unwrap(); @@ -701,7 +669,7 @@ mod tests { #[test] fn key_sizes() { - let kem = MlKem::new($params); + let kem = $algorithm; assert_eq!(kem.public_key_bytes(), $pk_len); assert_eq!(kem.ciphertext_bytes(), $ct_len); @@ -716,14 +684,14 @@ mod tests { #[test] fn invalid_public_key_length() { - let kem = MlKem::new($params); + let kem = $algorithm; let result = kem.encapsulate(&[0u8; 100]); assert!(result.is_err()); } #[test] fn invalid_private_key_length() { - let kem = MlKem::new($params); + let kem = $algorithm; let (pk, _) = kem.generate_key().unwrap(); let (ct, _) = kem.encapsulate(&pk).unwrap(); let result = kem.decapsulate(&[0u8; 32], &ct); @@ -732,37 +700,31 @@ mod tests { #[test] fn invalid_ciphertext_length() { - let kem = MlKem::new($params); + let kem = $algorithm; let (_, private_key) = kem.generate_key().unwrap(); let result = kem.decapsulate(&private_key, &[0u8; 100]); assert!(result.is_err()); } - - #[test] - fn params_accessor() { - let kem = MlKem::new($params); - assert_eq!(kem.params(), $params); - } } }; } - unified_tests!(mlkem768, MlKemParams::MlKem768, 1184, 1088); - unified_tests!(mlkem1024, MlKemParams::MlKem1024, 1568, 1568); + unified_tests!(mlkem768, MlKem::MlKem768, 1184, 1088); + unified_tests!(mlkem1024, MlKem::MlKem1024, 1568, 1568); #[test] fn params_constants() { - assert_eq!(MlKemParams::MlKem768.public_key_bytes(), 1184); - assert_eq!(MlKemParams::MlKem768.ciphertext_bytes(), 1088); - assert_eq!(MlKemParams::MlKem1024.public_key_bytes(), 1568); - assert_eq!(MlKemParams::MlKem1024.ciphertext_bytes(), 1568); + assert_eq!(MlKem::MlKem768.public_key_bytes(), 1184); + assert_eq!(MlKem::MlKem768.ciphertext_bytes(), 1088); + assert_eq!(MlKem::MlKem1024.public_key_bytes(), 1568); + assert_eq!(MlKem::MlKem1024.ciphertext_bytes(), 1568); } #[test] fn cross_kem_incompatibility() { // Keys from one KEM variant should not work with another - let kem768 = MlKem::new(MlKemParams::MlKem768); - let kem1024 = MlKem::new(MlKemParams::MlKem1024); + let kem768 = MlKem::MlKem768; + let kem1024 = MlKem::MlKem1024; let (pk768, _) = kem768.generate_key().unwrap(); let (pk1024, _) = kem1024.generate_key().unwrap(); From 995812e75d0561d2cfdbac9cc2a0516f78364693 Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 3 Feb 2026 00:10:34 +0000 Subject: [PATCH 4/4] Rename MlKem to Algorithm --- boring/src/mlkem.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/boring/src/mlkem.rs b/boring/src/mlkem.rs index 18569535f..2c8bfbcc8 100644 --- a/boring/src/mlkem.rs +++ b/boring/src/mlkem.rs @@ -4,14 +4,14 @@ //! using higher-level constructions like HPKE is preferred. //! Note that it's also enabled in TLS by default, in the X25519MLKEM768 exchange. //! -//! Provides ML-KEM-768 (recommended) and ML-KEM-1024 variants via [`MlKem`]. +//! Provides ML-KEM-768 (recommended) and ML-KEM-1024 variants via [`Algorithm`]. //! //! ``` -//! use boring::mlkem::MlKem; +//! use boring::mlkem::Algorithm; //! -//! let (public_key, private_key) = MlKem::MlKem768.generate_key().unwrap(); -//! let (ciphertext, shared_secret) = MlKem::MlKem768.encapsulate(&public_key).unwrap(); -//! let decrypted = MlKem::MlKem768.decapsulate(&private_key, &ciphertext).unwrap(); +//! let (public_key, private_key) = Algorithm::MlKem768.generate_key().unwrap(); +//! let (ciphertext, shared_secret) = Algorithm::MlKem768.encapsulate(&public_key).unwrap(); +//! let decrypted = Algorithm::MlKem768.decapsulate(&private_key, &ciphertext).unwrap(); //! assert_eq!(shared_secret, decrypted); //! ``` @@ -46,22 +46,22 @@ pub type MlKemSharedSecret = [u8; SHARED_SECRET_BYTES]; /// ML-KEM with runtime algorithm selection. Works with byte slices. /// /// ``` -/// use boring::mlkem::MlKem; +/// use boring::mlkem::Algorithm; /// -/// let (public_key, private_key) = MlKem::MlKem768.generate_key().unwrap(); -/// let (ciphertext, shared_secret) = MlKem::MlKem768.encapsulate(&public_key).unwrap(); -/// let decrypted = kem.decapsulate(&private_key, &ciphertext).unwrap(); +/// let (public_key, private_key) = Algorithm::MlKem768.generate_key().unwrap(); +/// let (ciphertext, shared_secret) = Algorithm::MlKem768.encapsulate(&public_key).unwrap(); +/// let decrypted = Algorithm::MlKem768.decapsulate(&private_key, &ciphertext).unwrap(); /// assert_eq!(shared_secret, decrypted); /// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum MlKem { +pub enum Algorithm { /// Recommended. AES-192 equivalent security. MlKem768, /// AES-256 equivalent security. MlKem1024, } -impl MlKem { +impl Algorithm { /// Returns 1184 for ML-KEM-768, 1568 for ML-KEM-1024. #[must_use] pub const fn public_key_bytes(&self) -> usize { @@ -709,22 +709,22 @@ mod tests { }; } - unified_tests!(mlkem768, MlKem::MlKem768, 1184, 1088); - unified_tests!(mlkem1024, MlKem::MlKem1024, 1568, 1568); + unified_tests!(mlkem768, Algorithm::MlKem768, 1184, 1088); + unified_tests!(mlkem1024, Algorithm::MlKem1024, 1568, 1568); #[test] fn params_constants() { - assert_eq!(MlKem::MlKem768.public_key_bytes(), 1184); - assert_eq!(MlKem::MlKem768.ciphertext_bytes(), 1088); - assert_eq!(MlKem::MlKem1024.public_key_bytes(), 1568); - assert_eq!(MlKem::MlKem1024.ciphertext_bytes(), 1568); + assert_eq!(Algorithm::MlKem768.public_key_bytes(), 1184); + assert_eq!(Algorithm::MlKem768.ciphertext_bytes(), 1088); + assert_eq!(Algorithm::MlKem1024.public_key_bytes(), 1568); + assert_eq!(Algorithm::MlKem1024.ciphertext_bytes(), 1568); } #[test] fn cross_kem_incompatibility() { // Keys from one KEM variant should not work with another - let kem768 = MlKem::MlKem768; - let kem1024 = MlKem::MlKem1024; + let kem768 = Algorithm::MlKem768; + let kem1024 = Algorithm::MlKem1024; let (pk768, _) = kem768.generate_key().unwrap(); let (pk1024, _) = kem1024.generate_key().unwrap();