From 3b77f5dc7584283fe5bb7668ccc7a29a03993f84 Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 10 Feb 2026 21:54:24 +0000 Subject: [PATCH 1/4] Add missing error handling in ML-KEM generate --- boring/src/mlkem.rs | 46 +++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/boring/src/mlkem.rs b/boring/src/mlkem.rs index f5b84147e..9b9516026 100644 --- a/boring/src/mlkem.rs +++ b/boring/src/mlkem.rs @@ -91,14 +91,14 @@ impl MlKemPrivateKey { pub fn generate(algorithm: Algorithm) -> Result<(MlKemPublicKey, MlKemPrivateKey), ErrorStack> { match algorithm { Algorithm::MlKem768 => { - let (pk, sk) = MlKem768PrivateKey::generate(); + let (pk, sk) = MlKem768PrivateKey::generate()?; Ok(( MlKemPublicKey(Either::MlKem768(pk)), MlKemPrivateKey(Either::MlKem768(sk)), )) } Algorithm::MlKem1024 => { - let (pk, sk) = MlKem1024PrivateKey::generate(); + let (pk, sk) = MlKem1024PrivateKey::generate()?; Ok(( MlKemPublicKey(Either::MlKem1024(pk)), MlKemPrivateKey(Either::MlKem1024(sk)), @@ -223,7 +223,7 @@ impl MlKem768PrivateKey { /// Generate a new key pair. #[must_use] - fn generate() -> (Box, Box) { + fn generate() -> Result<(Box, Box), ErrorStack> { // SAFETY: all buffers are out parameters, correctly sized unsafe { ffi::init(); @@ -243,9 +243,12 @@ impl MlKem768PrivateKey { // Parse the public key bytes to get the parsed struct let mut cbs = cbs_init(&bytes); let mut parsed: MaybeUninit = MaybeUninit::uninit(); - ffi::MLKEM768_parse_public_key(parsed.as_mut_ptr(), &mut cbs); + cvt(ffi::MLKEM768_parse_public_key( + parsed.as_mut_ptr(), + &mut cbs, + ))?; - ( + Ok(( Box::new(MlKem768PublicKey { bytes, parsed: parsed.assume_init(), @@ -254,7 +257,7 @@ impl MlKem768PrivateKey { seed: seed.assume_init(), expanded: expanded.assume_init(), }), - ) + )) } } @@ -440,7 +443,7 @@ impl MlKem1024PrivateKey { /// Generate a new key pair. #[must_use] - fn generate() -> (Box, Box) { + fn generate() -> Result<(Box, Box), ErrorStack> { // SAFETY: all buffers are out parameters, correctly sized unsafe { ffi::init(); @@ -460,9 +463,12 @@ impl MlKem1024PrivateKey { // Parse the public key bytes to get the parsed struct let mut cbs = cbs_init(&bytes); let mut parsed: MaybeUninit = MaybeUninit::uninit(); - ffi::MLKEM1024_parse_public_key(parsed.as_mut_ptr(), &mut cbs); + cvt(ffi::MLKEM1024_parse_public_key( + parsed.as_mut_ptr(), + &mut cbs, + ))?; - ( + Ok(( Box::new(MlKem1024PublicKey { bytes, parsed: parsed.assume_init(), @@ -471,7 +477,7 @@ impl MlKem1024PrivateKey { seed: seed.assume_init(), expanded: expanded.assume_init(), }), - ) + )) } } @@ -649,24 +655,28 @@ mod tests { #[test] fn roundtrip() { - let (pk, sk) = <$priv>::generate(); - let (ct, ss1) = pk.encapsulate(); + let (pk, sk) = <$priv>::generate().unwrap(); + let mut ct = [0; _]; + let mut ss1 = [0; _]; + pk.encapsulate_into(&mut ct, &mut ss1); let ss2 = sk.decapsulate(&ct); assert_eq!(ss1, ss2); } #[test] fn seed_roundtrip() { - let (pk, sk) = <$priv>::generate(); + let (pk, sk) = <$priv>::generate().unwrap(); let sk2 = <$priv>::from_seed(&sk.seed).unwrap(); - let (ct, ss1) = pk.encapsulate(); + let mut ct = [0; _]; + let mut ss1 = [0; _]; + pk.encapsulate_into(&mut ct, &mut ss1); let ss2 = sk2.decapsulate(&ct); assert_eq!(ss1, ss2); } #[test] fn derive_pubkey() { - let (pk, sk) = <$priv>::generate(); + let (pk, sk) = <$priv>::generate().unwrap(); assert_eq!(pk.bytes, sk.public_key().unwrap().bytes); } @@ -678,14 +688,14 @@ mod tests { #[test] fn from_slice_roundtrip() { - let (pk, _) = <$priv>::generate(); + let (pk, _) = <$priv>::generate().unwrap(); let pk2 = <$pub>::from_slice(&pk.bytes).unwrap(); assert_eq!(pk.bytes, pk2.bytes); } #[test] fn implicit_rejection() { - let (_, sk) = <$priv>::generate(); + let (_, sk) = <$priv>::generate().unwrap(); let bad_ct = [0x42u8; $ct_len]; // bad ciphertext still "works", just returns deterministic garbage let ss1 = sk.decapsulate(&bad_ct); @@ -695,7 +705,7 @@ mod tests { #[test] fn debug_redacts_seed() { - let (_, sk) = <$priv>::generate(); + let (_, sk) = <$priv>::generate().unwrap(); let dbg = format!("{:?}", sk); assert!(dbg.contains("redacted")); } From 7a093bc01a2b355108e1e0429204fac027e233d4 Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 10 Feb 2026 22:57:53 +0000 Subject: [PATCH 2/4] Avoid large-ish stack copies in encapsulate() --- boring/src/mlkem.rs | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/boring/src/mlkem.rs b/boring/src/mlkem.rs index 9b9516026..09972b496 100644 --- a/boring/src/mlkem.rs +++ b/boring/src/mlkem.rs @@ -131,16 +131,20 @@ impl MlKemPublicKey { /// Encapsulates a shared secret to the given public key, returning /// `(ciphertext, shared_secret)`. pub fn encapsulate(&self) -> Result<(Vec, MlKemSharedSecret), ErrorStack> { - match &self.0 { + let mut ss = [0; SHARED_SECRET_BYTES]; + let ct = match &self.0 { Either::MlKem768(pk) => { - let (ct, ss) = pk.encapsulate(); - Ok((ct.to_vec(), ss)) + let mut ct = vec![0; MlKem768PrivateKey::CIPHERTEXT_BYTES]; + pk.encapsulate_into(ct.as_mut_slice().try_into().unwrap(), &mut ss); + ct } Either::MlKem1024(pk) => { - let (ct, ss) = pk.encapsulate(); - Ok((ct.to_vec(), ss)) + let mut ct = vec![0; MlKem1024PrivateKey::CIPHERTEXT_BYTES]; + pk.encapsulate_into(ct.as_mut_slice().try_into().unwrap(), &mut ss); + ct } - } + }; + Ok((ct, ss)) } /// Query public key and ciphertext length @@ -391,25 +395,19 @@ impl MlKem768PublicKey { } /// Encapsulate: returns (ciphertext, shared_secret). - fn encapsulate( + fn encapsulate_into( &self, - ) -> ( - [u8; MlKem768PrivateKey::CIPHERTEXT_BYTES], - MlKemSharedSecret, + ciphertext: &mut [u8; MlKem768PrivateKey::CIPHERTEXT_BYTES], + shared_secret: &mut MlKemSharedSecret, ) { // SAFETY: buffers correctly sized, parsed key is valid unsafe { ffi::init(); - let mut ciphertext = [0u8; MlKem768PrivateKey::CIPHERTEXT_BYTES]; - let mut shared_secret = [0u8; SHARED_SECRET_BYTES]; - ffi::MLKEM768_encap( ciphertext.as_mut_ptr(), shared_secret.as_mut_ptr(), &self.parsed, ); - - (ciphertext, shared_secret) } } } @@ -613,25 +611,19 @@ impl MlKem1024PublicKey { } /// Encapsulate: returns (ciphertext, shared_secret). - fn encapsulate( + fn encapsulate_into( &self, - ) -> ( - [u8; MlKem1024PrivateKey::CIPHERTEXT_BYTES], - [u8; SHARED_SECRET_BYTES], + ciphertext: &mut [u8; MlKem1024PrivateKey::CIPHERTEXT_BYTES], + shared_secret: &mut [u8; SHARED_SECRET_BYTES], ) { // SAFETY: buffers correctly sized, parsed key is valid unsafe { ffi::init(); - let mut ciphertext = [0u8; MlKem1024PrivateKey::CIPHERTEXT_BYTES]; - let mut shared_secret = [0u8; SHARED_SECRET_BYTES]; - ffi::MLKEM1024_encap( ciphertext.as_mut_ptr(), shared_secret.as_mut_ptr(), &self.parsed, ); - - (ciphertext, shared_secret) } } } From 60c5ea6d4eaa1333ddc4d020b7131e889d69570f Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 10 Feb 2026 22:54:37 +0000 Subject: [PATCH 3/4] Ensure we don't leave unit memory if generate_key fails --- boring/src/mlkem.rs | 54 ++++++++++----------------------------------- 1 file changed, 12 insertions(+), 42 deletions(-) diff --git a/boring/src/mlkem.rs b/boring/src/mlkem.rs index 09972b496..d402ae64d 100644 --- a/boring/src/mlkem.rs +++ b/boring/src/mlkem.rs @@ -226,39 +226,24 @@ impl MlKem768PrivateKey { pub const CIPHERTEXT_BYTES: usize = ffi::MLKEM768_CIPHERTEXT_BYTES as usize; /// Generate a new key pair. - #[must_use] fn generate() -> Result<(Box, Box), ErrorStack> { // SAFETY: all buffers are out parameters, correctly sized unsafe { ffi::init(); - let mut public_key_bytes: MaybeUninit<[u8; MlKem768PublicKey::PUBLIC_KEY_BYTES]> = - MaybeUninit::uninit(); - let mut seed: MaybeUninit = MaybeUninit::uninit(); + let mut bytes = [0; MlKem768PublicKey::PUBLIC_KEY_BYTES]; + let mut seed = [0; PRIVATE_KEY_SEED_BYTES]; let mut expanded: MaybeUninit = MaybeUninit::uninit(); ffi::MLKEM768_generate_key( - public_key_bytes.as_mut_ptr().cast(), - seed.as_mut_ptr().cast(), + bytes.as_mut_ptr().cast(), + seed.as_mut_ptr(), expanded.as_mut_ptr(), ); - let bytes = public_key_bytes.assume_init(); - - // Parse the public key bytes to get the parsed struct - let mut cbs = cbs_init(&bytes); - let mut parsed: MaybeUninit = MaybeUninit::uninit(); - cvt(ffi::MLKEM768_parse_public_key( - parsed.as_mut_ptr(), - &mut cbs, - ))?; - Ok(( - Box::new(MlKem768PublicKey { - bytes, - parsed: parsed.assume_init(), - }), + Box::new(MlKem768PublicKey::from_slice(&bytes)?), Box::new(MlKem768PrivateKey { - seed: seed.assume_init(), + seed, expanded: expanded.assume_init(), }), )) @@ -440,39 +425,24 @@ impl MlKem1024PrivateKey { pub const CIPHERTEXT_BYTES: usize = ffi::MLKEM1024_CIPHERTEXT_BYTES as usize; /// Generate a new key pair. - #[must_use] fn generate() -> Result<(Box, Box), ErrorStack> { // SAFETY: all buffers are out parameters, correctly sized unsafe { ffi::init(); - let mut public_key_bytes: MaybeUninit<[u8; MlKem1024PublicKey::PUBLIC_KEY_BYTES]> = - MaybeUninit::uninit(); - let mut seed: MaybeUninit = MaybeUninit::uninit(); + let mut bytes = [0; MlKem1024PublicKey::PUBLIC_KEY_BYTES]; + let mut seed = [0; PRIVATE_KEY_SEED_BYTES]; let mut expanded: MaybeUninit = MaybeUninit::uninit(); ffi::MLKEM1024_generate_key( - public_key_bytes.as_mut_ptr().cast(), - seed.as_mut_ptr().cast(), + bytes.as_mut_ptr().cast(), + seed.as_mut_ptr(), expanded.as_mut_ptr(), ); - let bytes = public_key_bytes.assume_init(); - - // Parse the public key bytes to get the parsed struct - let mut cbs = cbs_init(&bytes); - let mut parsed: MaybeUninit = MaybeUninit::uninit(); - cvt(ffi::MLKEM1024_parse_public_key( - parsed.as_mut_ptr(), - &mut cbs, - ))?; - Ok(( - Box::new(MlKem1024PublicKey { - bytes, - parsed: parsed.assume_init(), - }), + Box::new(MlKem1024PublicKey::from_slice(&bytes)?), Box::new(MlKem1024PrivateKey { - seed: seed.assume_init(), + seed, expanded: expanded.assume_init(), }), )) From fb69e238a7b59170d0f5e6be2a391c69f82051e0 Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 10 Feb 2026 22:27:11 +0000 Subject: [PATCH 4/4] Generate dummy placeholder for missing mlkem.h --- boring-sys/build/main.rs | 22 ++-- boring-sys/src/lib.rs | 3 + boring-sys/src/mlkem_dummy.rs | 184 ++++++++++++++++++++++++++++++++++ boring/src/mlkem.rs | 21 ++++ 4 files changed, 222 insertions(+), 8 deletions(-) create mode 100644 boring-sys/src/mlkem_dummy.rs diff --git a/boring-sys/build/main.rs b/boring-sys/build/main.rs index abb733bed..af128372f 100644 --- a/boring-sys/build/main.rs +++ b/boring-sys/build/main.rs @@ -755,20 +755,26 @@ fn generate_bindings(config: &Config) { bindings .write(Box::new(&mut source_code)) .expect("Couldn't serialize bindings!"); + let mut source_code = String::from_utf8_lossy(&source_code).into_owned(); + + if !source_code.contains("MLKEM768_encap") { + // Fail at runtime to allow boring v5 to compile with mlkem disabled + source_code.push_str("\n#[cfg(not(feature = \"mlkem\"))] #[allow(deprecated)] pub use crate::mlkem_dummy::*;\n"); + } + ensure_err_lib_enum_is_named(&mut source_code); - fs::write(config.out_dir.join("bindings.rs"), source_code).expect("Couldn't write bindings!"); + fs::write(config.out_dir.join("bindings.rs"), source_code.as_bytes()) + .expect("Couldn't write bindings!"); } /// err.h has anonymous `enum { ERR_LIB_NONE = 1 }`, which makes a dodgy `_bindgen_ty_1` name -fn ensure_err_lib_enum_is_named(source_code: &mut Vec) { - let src = String::from_utf8_lossy(source_code); - let enum_type = src +fn ensure_err_lib_enum_is_named(source_code: &mut String) { + let enum_type = source_code .split_once("ERR_LIB_SSL:") .and_then(|(_, def)| Some(def.split_once('=')?.0)) .unwrap_or("_bindgen_ty_1"); - source_code.extend_from_slice( - format!("\n/// Newtype for [`ERR_LIB_SSL`] constants\npub use {enum_type} as ErrLib;\n") - .as_bytes(), - ); + let ty = + format!("\n/// Newtype for [`ERR_LIB_SSL`] constants\npub use {enum_type} as ErrLib;\n"); + source_code.push_str(&ty); } diff --git a/boring-sys/src/lib.rs b/boring-sys/src/lib.rs index 57ceffc76..a969b1051 100644 --- a/boring-sys/src/lib.rs +++ b/boring-sys/src/lib.rs @@ -15,6 +15,9 @@ use std::convert::TryInto; use std::ffi::c_void; use std::os::raw::{c_char, c_int, c_uint, c_ulong}; +#[cfg(not(feature = "mlkem"))] +mod mlkem_dummy; + #[allow( clippy::useless_transmute, clippy::derive_partial_eq_without_eq, diff --git a/boring-sys/src/mlkem_dummy.rs b/boring-sys/src/mlkem_dummy.rs new file mode 100644 index 000000000..668f8eb16 --- /dev/null +++ b/boring-sys/src/mlkem_dummy.rs @@ -0,0 +1,184 @@ +#![allow(unused, deprecated)] + +use crate::ERR_put_error; +use crate::CBB; +use crate::CBS; +use crate::ERR_R_FATAL; + +#[derive(Copy, Clone, Default)] +pub struct UnimplementedPlaceholder { + pub bytes: [u8; 0], +} + +#[derive(Copy, Clone, Default)] +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub struct MLKEM768_private_key { + pub opaque: UnimplementedPlaceholder, +} + +#[derive(Copy, Clone, Default)] +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub struct MLKEM768_public_key; +#[derive(Copy, Clone, Default)] +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub struct MLKEM1024_private_key { + pub opaque: UnimplementedPlaceholder, +} +#[derive(Copy, Clone, Default)] +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub struct MLKEM1024_public_key; + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub const MLKEM_SEED_BYTES: u32 = 0; +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub const MLKEM_SHARED_SECRET_BYTES: u32 = 0; +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub const MLKEM768_PUBLIC_KEY_BYTES: u32 = 0; +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub const MLKEM1024_PUBLIC_KEY_BYTES: u32 = 0; +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub const MLKEM768_CIPHERTEXT_BYTES: u32 = 0; +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub const MLKEM1024_CIPHERTEXT_BYTES: u32 = 0; + +fn put_error() { + unsafe { + ERR_put_error( + 34, + 0, + ERR_R_FATAL, + c"boring-sys missing mlkem.h".as_ptr(), + 1, + ) + } +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C-unwind" fn MLKEM1024_generate_key( + _out_encoded_public_key: *mut u8, + _optional_out_seed: *mut u8, + _out_private_key: *mut MLKEM1024_private_key, +) { + unimplemented!(); +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C" fn MLKEM1024_private_key_from_seed( + _out_private_key: *mut MLKEM1024_private_key, + _seed: *const u8, + _seed_len: usize, +) -> ::std::os::raw::c_int { + put_error(); + 0 +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C-unwind" fn MLKEM1024_public_from_private( + _out_public_key: *mut MLKEM1024_public_key, + _private_key: *const MLKEM1024_private_key, +) { + unimplemented!(); +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C-unwind" fn MLKEM1024_encap( + _out_ciphertext: *mut u8, + _out_shared_secret: *mut u8, + _public_key: *const MLKEM1024_public_key, +) { + unimplemented!(); +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C" fn MLKEM1024_decap( + _out_shared_secret: *mut u8, + _ciphertext: *const u8, + _ciphertext_len: usize, + _private_key: *const MLKEM1024_private_key, +) -> ::std::os::raw::c_int { + put_error(); + 0 +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C-unwind" fn MLKEM768_generate_key( + _out_encoded_public_key: *mut u8, + _optional_out_seed: *mut u8, + _out_private_key: *mut MLKEM768_private_key, +) { + unimplemented!(); +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C" fn MLKEM768_private_key_from_seed( + _out_private_key: *mut MLKEM768_private_key, + _seed: *const u8, + _seed_len: usize, +) -> ::std::os::raw::c_int { + put_error(); + 0 +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C-unwind" fn MLKEM768_public_from_private( + _out_public_key: *mut MLKEM768_public_key, + _private_key: *const MLKEM768_private_key, +) { + unimplemented!(); +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C-unwind" fn MLKEM768_encap( + _out_ciphertext: *mut u8, + _out_shared_secret: *mut u8, + _public_key: *const MLKEM768_public_key, +) { + unimplemented!(); +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C" fn MLKEM768_decap( + _out_shared_secret: *mut u8, + _ciphertext: *const u8, + _ciphertext_len: usize, + _private_key: *const MLKEM768_private_key, +) -> ::std::os::raw::c_int { + put_error(); + 0 +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C" fn MLKEM768_marshal_public_key( + out: *mut CBB, + public_key: *const MLKEM768_public_key, +) -> ::std::os::raw::c_int { + put_error(); + 0 +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C" fn MLKEM768_parse_public_key( + out_public_key: *mut MLKEM768_public_key, + in_: *mut CBS, +) -> ::std::os::raw::c_int { + put_error(); + 0 +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C" fn MLKEM1024_marshal_public_key( + out: *mut CBB, + public_key: *const MLKEM1024_public_key, +) -> ::std::os::raw::c_int { + put_error(); + 0 +} + +#[deprecated(note = "mlkem.h was missing; this API won't work")] +pub unsafe extern "C" fn MLKEM1024_parse_public_key( + out_public_key: *mut MLKEM1024_public_key, + in_: *mut CBS, +) -> ::std::os::raw::c_int { + put_error(); + 0 +} diff --git a/boring/src/mlkem.rs b/boring/src/mlkem.rs index d402ae64d..8059e4b8c 100644 --- a/boring/src/mlkem.rs +++ b/boring/src/mlkem.rs @@ -89,6 +89,9 @@ impl MlKemPrivateKey { /// /// The private key is a 64-byte seed. Keep it secret. pub fn generate(algorithm: Algorithm) -> Result<(MlKemPublicKey, MlKemPrivateKey), ErrorStack> { + if PRIVATE_KEY_SEED_BYTES != 64 { + unimplemented!() + } match algorithm { Algorithm::MlKem768 => { let (pk, sk) = MlKem768PrivateKey::generate()?; @@ -110,6 +113,9 @@ impl MlKemPrivateKey { impl MlKemPublicKey { pub fn from_slice(algorithm: Algorithm, public_key: &[u8]) -> Result { + if SHARED_SECRET_BYTES != 32 { + unimplemented!() + } match algorithm { Algorithm::MlKem768 => Ok(Self(Either::MlKem768(Box::new( MlKem768PublicKey::from_slice(public_key)?, @@ -131,6 +137,9 @@ impl MlKemPublicKey { /// Encapsulates a shared secret to the given public key, returning /// `(ciphertext, shared_secret)`. pub fn encapsulate(&self) -> Result<(Vec, MlKemSharedSecret), ErrorStack> { + if SHARED_SECRET_BYTES != 32 { + unimplemented!() + } let mut ss = [0; SHARED_SECRET_BYTES]; let ct = match &self.0 { Either::MlKem768(pk) => { @@ -162,6 +171,9 @@ impl MlKemPrivateKey { algorithm: Algorithm, private_seed: &MlKemPrivateKeySeed, ) -> Result { + if PRIVATE_KEY_SEED_BYTES != 64 { + unimplemented!() + } match algorithm { Algorithm::MlKem768 => Ok(Self(Either::MlKem768(Box::new( MlKem768PrivateKey::from_seed(private_seed)?, @@ -182,6 +194,9 @@ impl MlKemPrivateKey { /// Decapsulates a shared secret from a ciphertext using the private key. pub fn decapsulate(&self, ciphertext: &[u8]) -> Result { + if SHARED_SECRET_BYTES != 32 { + unimplemented!() + } match &self.0 { Either::MlKem768(sk) => { let ct: &[u8; MlKem768PrivateKey::CIPHERTEXT_BYTES] = ciphertext @@ -227,6 +242,9 @@ impl MlKem768PrivateKey { /// Generate a new key pair. fn generate() -> Result<(Box, Box), ErrorStack> { + if PRIVATE_KEY_SEED_BYTES != 64 { + unimplemented!() + } // SAFETY: all buffers are out parameters, correctly sized unsafe { ffi::init(); @@ -426,6 +444,9 @@ impl MlKem1024PrivateKey { /// Generate a new key pair. fn generate() -> Result<(Box, Box), ErrorStack> { + if PRIVATE_KEY_SEED_BYTES != 64 { + unimplemented!() + } // SAFETY: all buffers are out parameters, correctly sized unsafe { ffi::init();