diff --git a/libcrux-ml-kem/benches/ml-kem.rs b/libcrux-ml-kem/benches/ml-kem.rs index 37334be2a..d347c9626 100644 --- a/libcrux-ml-kem/benches/ml-kem.rs +++ b/libcrux-ml-kem/benches/ml-kem.rs @@ -48,7 +48,8 @@ pub fn key_generation(c: &mut Criterion) { #[cfg(all( feature = "mlkem768", feature = "pre-verification", - feature = "simd256" + feature = "simd256", + feature = "unpacked" ))] c.bench_function("libcrux avx2 unpacked (external random)", |b| { let mut seed = [0; 64]; @@ -71,7 +72,11 @@ pub fn key_generation(c: &mut Criterion) { }) }); - #[cfg(all(feature = "mlkem768", feature = "pre-verification"))] + #[cfg(all( + feature = "mlkem768", + feature = "pre-verification", + feature = "unpacked" + ))] c.bench_function("libcrux portable unpacked (external random)", |b| { let mut seed = [0; 64]; rng.fill_bytes(&mut seed); @@ -94,10 +99,10 @@ pub fn pk_validation(c: &mut Criterion) { b.iter_batched( || { let keypair = p::generate_key_pair(seed); - keypair.public_key().as_slice().into() + keypair }, - |public_key| { - let _valid = black_box(p::validate_public_key(public_key)); + |keypair| { + let _valid = black_box(p::validate_public_key(keypair.public_key())); }, BatchSize::SmallInput, ) @@ -135,7 +140,11 @@ pub fn encapsulation(c: &mut Criterion) { init!(mlkem768, "Encapsulation", c); init!(mlkem1024, "Encapsulation", c); - #[cfg(all(feature = "mlkem768", feature = "pre-verification"))] + #[cfg(all( + feature = "mlkem768", + feature = "pre-verification", + feature = "unpacked" + ))] c.bench_function("libcrux unpacked portable (external random)", |b| { let mut seed1 = [0; 64]; OsRng.fill_bytes(&mut seed1); @@ -174,7 +183,8 @@ pub fn encapsulation(c: &mut Criterion) { #[cfg(all( feature = "mlkem768", feature = "pre-verification", - feature = "simd256" + feature = "simd256", + feature = "unpacked" ))] c.bench_function("libcrux unpacked avx2 (external random)", |b| { let mut seed1 = [0; 64]; @@ -222,7 +232,11 @@ pub fn decapsulation(c: &mut Criterion) { init!(mlkem768, "Decapsulation", c); init!(mlkem1024, "Decapsulation", c); - #[cfg(all(feature = "mlkem768", feature = "pre-verification"))] + #[cfg(all( + feature = "mlkem768", + feature = "pre-verification", + feature = "unpacked" + ))] c.bench_function("libcrux unpacked portable", |b| { let mut seed1 = [0; 64]; OsRng.fill_bytes(&mut seed1); @@ -270,7 +284,8 @@ pub fn decapsulation(c: &mut Criterion) { #[cfg(all( feature = "mlkem768", feature = "pre-verification", - feature = "simd256" + feature = "simd256", + feature = "unpacked" ))] c.bench_function("libcrux unpacked avx2", |b| { let mut seed1 = [0; 64]; diff --git a/libcrux-ml-kem/src/ind_cca.rs b/libcrux-ml-kem/src/ind_cca.rs index 9b1dc773f..f09b6c7ce 100644 --- a/libcrux-ml-kem/src/ind_cca.rs +++ b/libcrux-ml-kem/src/ind_cca.rs @@ -86,6 +86,11 @@ fn serialize_kem_secret_key, +>( + private_key: &MlKemPrivateKey, + _ciphertext: &MlKemCiphertext, +) -> bool { + // Eurydice can't access values directly on the types. We need to go to the + // `value` directly. + + let t = Hasher::H(&private_key.value[384 * K..768 * K + 32]); + let expected = &private_key.value[768 * K + 32..768 * K + 64]; + t == expected +} + /// Packed API /// /// Generate a key pair. diff --git a/libcrux-ml-kem/src/ind_cca/instantiations.rs b/libcrux-ml-kem/src/ind_cca/instantiations.rs index 582fe79a9..e9d780ab2 100644 --- a/libcrux-ml-kem/src/ind_cca/instantiations.rs +++ b/libcrux-ml-kem/src/ind_cca/instantiations.rs @@ -66,6 +66,7 @@ macro_rules! instantiate { } /// Portable public key validation + #[inline(always)] pub(crate) fn validate_public_key< const K: usize, const RANKED_BYTES_PER_RING_ELEMENT: usize, @@ -81,6 +82,22 @@ macro_rules! instantiate { >(public_key) } + /// Portable private key validation + #[inline(always)] + pub(crate) fn validate_private_key< + const K: usize, + const SECRET_KEY_SIZE: usize, + const CIPHERTEXT_SIZE: usize, + >( + private_key: &MlKemPrivateKey, + ciphertext: &MlKemCiphertext, + ) -> bool { + crate::ind_cca::validate_private_key::( + private_key, + ciphertext, + ) + } + /// Portable encapsulate #[cfg(feature = "kyber")] pub(crate) fn kyber_encapsulate< diff --git a/libcrux-ml-kem/src/ind_cca/multiplexing.rs b/libcrux-ml-kem/src/ind_cca/multiplexing.rs index 77f673a9d..588d551e3 100644 --- a/libcrux-ml-kem/src/ind_cca/multiplexing.rs +++ b/libcrux-ml-kem/src/ind_cca/multiplexing.rs @@ -7,7 +7,7 @@ use super::*; #[cfg(feature = "simd256")] use instantiations::avx2::{ decapsulate as decapsulate_avx2, encapsulate as encapsulate_avx2, - generate_keypair as generate_keypair_avx2, validate_public_key as validate_public_key_avx2, + generate_keypair as generate_keypair_avx2, }; #[cfg(feature = "simd128")] @@ -19,13 +19,13 @@ use instantiations::neon::{ #[cfg(not(feature = "simd256"))] use instantiations::portable::{ decapsulate as decapsulate_avx2, encapsulate as encapsulate_avx2, - generate_keypair as generate_keypair_avx2, validate_public_key as validate_public_key_avx2, + generate_keypair as generate_keypair_avx2, }; #[cfg(not(feature = "simd128"))] use instantiations::portable::{ decapsulate as decapsulate_neon, encapsulate as encapsulate_neon, - generate_keypair as generate_keypair_neon, validate_public_key as validate_public_key_neon, + generate_keypair as generate_keypair_neon, }; #[cfg(all(feature = "simd256", feature = "kyber"))] @@ -52,6 +52,7 @@ use instantiations::portable::{ kyber_generate_keypair as kyber_generate_keypair_neon, }; +#[inline(always)] pub(crate) fn validate_public_key< const K: usize, const RANKED_BYTES_PER_RING_ELEMENT: usize, @@ -59,17 +60,24 @@ pub(crate) fn validate_public_key< >( public_key: &[u8; PUBLIC_KEY_SIZE], ) -> bool { - if libcrux_platform::simd256_support() { - validate_public_key_avx2::(public_key) - } else if libcrux_platform::simd128_support() { - validate_public_key_neon::(public_key) - } else { - instantiations::portable::validate_public_key::< - K, - RANKED_BYTES_PER_RING_ELEMENT, - PUBLIC_KEY_SIZE, - >(public_key) - } + instantiations::portable::validate_public_key::( + public_key, + ) +} + +#[inline(always)] +pub(crate) fn validate_private_key< + const K: usize, + const SECRET_KEY_SIZE: usize, + const CIPHERTEXT_SIZE: usize, +>( + private_key: &MlKemPrivateKey, + ciphertext: &MlKemCiphertext, +) -> bool { + instantiations::portable::validate_private_key::( + private_key, + ciphertext, + ) } #[cfg(feature = "kyber")] diff --git a/libcrux-ml-kem/src/lib.rs b/libcrux-ml-kem/src/lib.rs index 61036042b..a787a5f1d 100644 --- a/libcrux-ml-kem/src/lib.rs +++ b/libcrux-ml-kem/src/lib.rs @@ -146,6 +146,7 @@ cfg_pre_verification! { pub use crate::mlkem512::kyber::decapsulate; pub use crate::mlkem512::kyber::encapsulate; pub use crate::mlkem512::validate_public_key; + pub use crate::mlkem512::validate_private_key; } } @@ -158,6 +159,7 @@ cfg_pre_verification! { pub use crate::mlkem768::kyber::decapsulate; pub use crate::mlkem768::kyber::encapsulate; pub use crate::mlkem768::validate_public_key; + pub use crate::mlkem768::validate_private_key; } } @@ -170,6 +172,7 @@ cfg_pre_verification! { pub use crate::mlkem1024::kyber::decapsulate; pub use crate::mlkem1024::kyber::encapsulate; pub use crate::mlkem1024::validate_public_key; + pub use crate::mlkem1024::validate_private_key; } } } diff --git a/libcrux-ml-kem/src/mlkem1024.rs b/libcrux-ml-kem/src/mlkem1024.rs index 3a7acf194..4d8520881 100644 --- a/libcrux-ml-kem/src/mlkem1024.rs +++ b/libcrux-ml-kem/src/mlkem1024.rs @@ -67,20 +67,27 @@ macro_rules! instantiate { /// Validate a public key. /// - /// Returns `Some(public_key)` if valid, and `None` otherwise. - pub fn validate_public_key( - public_key: MlKem1024PublicKey, - ) -> Option { - if p::validate_public_key::< + /// Returns `true` if valid, and `false` otherwise. + pub fn validate_public_key(public_key: &MlKem1024PublicKey) -> bool { + p::validate_public_key::< RANK_1024, RANKED_BYTES_PER_RING_ELEMENT_1024, CPA_PKE_PUBLIC_KEY_SIZE_1024, >(&public_key.value) - { - Some(public_key) - } else { - None - } + } + + /// Validate a private key. + /// + /// Returns `true` if valid, and `false` otherwise. + pub fn validate_private_key( + private_key: &MlKem1024PrivateKey, + ciphertext: &MlKem1024Ciphertext, + ) -> bool { + p::validate_private_key::< + RANK_1024, + SECRET_KEY_SIZE_1024, + CPA_PKE_CIPHERTEXT_SIZE_1024, + >(private_key, ciphertext) } /// Generate Kyber 1024 Key Pair @@ -331,19 +338,29 @@ instantiate! {neon, ind_cca::instantiations::neon, vector::SIMD128Vector, "Neon /// Validate a public key. /// -/// Returns `Some(public_key)` if valid, and `None` otherwise. +/// Returns `true` if valid, and `false` otherwise. #[cfg(not(eurydice))] -pub fn validate_public_key(public_key: MlKem1024PublicKey) -> Option { - if multiplexing::validate_public_key::< +pub fn validate_public_key(public_key: &MlKem1024PublicKey) -> bool { + multiplexing::validate_public_key::< RANK_1024, RANKED_BYTES_PER_RING_ELEMENT_1024, CPA_PKE_PUBLIC_KEY_SIZE_1024, >(&public_key.value) - { - Some(public_key) - } else { - None - } +} + +/// Validate a private key. +/// +/// Returns `true` if valid, and `false` otherwise. +#[cfg(not(eurydice))] +pub fn validate_private_key( + private_key: &MlKem1024PrivateKey, + ciphertext: &MlKem1024Ciphertext, +) -> bool { + multiplexing::validate_private_key::< + RANK_1024, + SECRET_KEY_SIZE_1024, + CPA_PKE_CIPHERTEXT_SIZE_1024, + >(private_key, ciphertext) } /// Generate ML-KEM 1024 Key Pair diff --git a/libcrux-ml-kem/src/mlkem512.rs b/libcrux-ml-kem/src/mlkem512.rs index 53cec6878..7ad225342 100644 --- a/libcrux-ml-kem/src/mlkem512.rs +++ b/libcrux-ml-kem/src/mlkem512.rs @@ -65,18 +65,27 @@ macro_rules! instantiate { /// Validate a public key. /// - /// Returns `Some(public_key)` if valid, and `None` otherwise. - pub fn validate_public_key(public_key: MlKem512PublicKey) -> Option { - if p::validate_public_key::< + /// Returns `true` if valid, and `false` otherwise. + pub fn validate_public_key(public_key: &MlKem512PublicKey) -> bool { + p::validate_public_key::< RANK_512, RANKED_BYTES_PER_RING_ELEMENT_512, CPA_PKE_PUBLIC_KEY_SIZE_512, >(&public_key.value) - { - Some(public_key) - } else { - None - } + } + + /// Validate a private key. + /// + /// Returns `true` if valid, and `false` otherwise. + pub fn validate_private_key( + private_key: &MlKem512PrivateKey, + ciphertext: &MlKem512Ciphertext, + ) -> bool { + p::validate_private_key::< + RANK_512, + SECRET_KEY_SIZE_512, + CPA_PKE_CIPHERTEXT_SIZE_512, + >(private_key, ciphertext) } /// Generate ML-KEM 512 Key Pair @@ -324,19 +333,28 @@ instantiate! {neon, ind_cca::instantiations::neon, vector::SIMD128Vector, "Neon /// Validate a public key. /// -/// Returns `Some(public_key)` if valid, and `None` otherwise. +/// Returns `true` if valid, and `false` otherwise. #[cfg(not(eurydice))] -pub fn validate_public_key(public_key: MlKem512PublicKey) -> Option { - if multiplexing::validate_public_key::< +pub fn validate_public_key(public_key: &MlKem512PublicKey) -> bool { + multiplexing::validate_public_key::< RANK_512, RANKED_BYTES_PER_RING_ELEMENT_512, CPA_PKE_PUBLIC_KEY_SIZE_512, >(&public_key.value) - { - Some(public_key) - } else { - None - } +} + +/// Validate a private key. +/// +/// Returns `true` if valid, and `false` otherwise. +#[cfg(not(eurydice))] +pub fn validate_private_key( + private_key: &MlKem512PrivateKey, + ciphertext: &MlKem512Ciphertext, +) -> bool { + multiplexing::validate_private_key::( + private_key, + ciphertext, + ) } /// Generate ML-KEM 512 Key Pair diff --git a/libcrux-ml-kem/src/mlkem768.rs b/libcrux-ml-kem/src/mlkem768.rs index 7cc7cff5c..b47a3eb72 100644 --- a/libcrux-ml-kem/src/mlkem768.rs +++ b/libcrux-ml-kem/src/mlkem768.rs @@ -1,5 +1,5 @@ //! ML-KEM 768 -//! + use super::{constants::*, ind_cca::*, types::*, *}; #[cfg(feature = "unpacked")] use super::{ind_cca::unpacked::*, vector::traits::VectorType}; @@ -67,18 +67,27 @@ macro_rules! instantiate { /// Validate a public key. /// - /// Returns `Some(public_key)` if valid, and `None` otherwise. - pub fn validate_public_key(public_key: MlKem768PublicKey) -> Option { - if p::validate_public_key::< + /// Returns `true` if valid, and `false` otherwise. + pub fn validate_public_key(public_key: &MlKem768PublicKey) -> bool { + p::validate_public_key::< RANK_768, RANKED_BYTES_PER_RING_ELEMENT_768, CPA_PKE_PUBLIC_KEY_SIZE_768, >(&public_key.value) - { - Some(public_key) - } else { - None - } + } + + /// Validate a private key. + /// + /// Returns `true` if valid, and `false` otherwise. + pub fn validate_private_key( + private_key: &MlKem768PrivateKey, + ciphertext: &MlKem768Ciphertext, + ) -> bool { + p::validate_private_key::< + RANK_768, + SECRET_KEY_SIZE_768, + CPA_PKE_CIPHERTEXT_SIZE_768, + >(private_key, ciphertext) } /// Generate ML-KEM 768 Key Pair @@ -327,19 +336,28 @@ instantiate! {neon, ind_cca::instantiations::neon, vector::SIMD128Vector, "Neon /// Validate a public key. /// -/// Returns `Some(public_key)` if valid, and `None` otherwise. +/// Returns `true` if valid, and `false` otherwise. #[cfg(not(eurydice))] -pub fn validate_public_key(public_key: MlKem768PublicKey) -> Option { - if multiplexing::validate_public_key::< +pub fn validate_public_key(public_key: &MlKem768PublicKey) -> bool { + multiplexing::validate_public_key::< RANK_768, RANKED_BYTES_PER_RING_ELEMENT_768, CPA_PKE_PUBLIC_KEY_SIZE_768, >(&public_key.value) - { - Some(public_key) - } else { - None - } +} + +/// Validate a private key. +/// +/// Returns `true` if valid, and `false` otherwise. +#[cfg(not(eurydice))] +pub fn validate_private_key( + private_key: &MlKem768PrivateKey, + ciphertext: &MlKem768Ciphertext, +) -> bool { + multiplexing::validate_private_key::( + private_key, + ciphertext, + ) } /// Generate ML-KEM 768 Key Pair @@ -508,6 +526,6 @@ mod tests { OsRng.fill_bytes(&mut randomness); let key_pair = generate_key_pair(randomness); - assert!(validate_public_key(key_pair.pk).is_some()); + assert!(validate_public_key(&key_pair.pk)); } } diff --git a/libcrux-ml-kem/tests/ml-kem.rs b/libcrux-ml-kem/tests/ml-kem.rs index 56d89c4f5..784c2fc36 100644 --- a/libcrux-ml-kem/tests/ml-kem.rs +++ b/libcrux-ml-kem/tests/ml-kem.rs @@ -16,18 +16,17 @@ fn test_invalid_modulus(p: &str) { let pk = pk.as_slice(); match p { #[cfg(feature = "mlkem512")] - "512" => assert!( - libcrux_ml_kem::mlkem512::validate_public_key(pk.try_into().unwrap()).is_none() - ), + "512" => assert!(!libcrux_ml_kem::mlkem512::validate_public_key( + &pk.try_into().unwrap() + )), #[cfg(feature = "mlkem768")] - "768" => assert!( - libcrux_ml_kem::mlkem768::validate_public_key(pk.try_into().unwrap()).is_none() - ), + "768" => assert!(!libcrux_ml_kem::mlkem768::validate_public_key( + &pk.try_into().unwrap() + )), #[cfg(feature = "mlkem1024")] - "1024" => assert!(libcrux_ml_kem::mlkem1024::validate_public_key( - pk.try_into().unwrap() - ) - .is_none()), + "1024" => assert!(!libcrux_ml_kem::mlkem1024::validate_public_key( + &pk.try_into().unwrap() + )), _ => unreachable!(), }; } diff --git a/libcrux-ml-kem/tests/nistkats.rs b/libcrux-ml-kem/tests/nistkats.rs index 7617d3768..51385e923 100644 --- a/libcrux-ml-kem/tests/nistkats.rs +++ b/libcrux-ml-kem/tests/nistkats.rs @@ -26,9 +26,11 @@ struct MlKemNISTKAT { } macro_rules! impl_nist_known_answer_tests { - ($name:ident, $variant:literal, $parameter_set: literal, $key_gen_derand:expr, $encapsulate_derand:expr, $decapsulate_derand: expr) => { + ($name:ident, $variant:literal, $parameter_set:literal, $module:path) => { #[test] fn $name() { + use $module::*; + let katfile_path = Path::new("tests") .join("kats") .join(format!("nistkats_{}_{}.json", $variant, $parameter_set)); @@ -39,7 +41,9 @@ macro_rules! impl_nist_known_answer_tests { serde_json::from_reader(reader).expect("Could not deserialize KAT file."); for kat in nist_kats { - let key_pair = $key_gen_derand(kat.key_generation_seed); + let key_pair = generate_key_pair(kat.key_generation_seed); + + assert!(validate_public_key(key_pair.public_key())); let public_key_hash = sha256(key_pair.pk()); eprintln!("pk hash: {}", hex::encode(public_key_hash)); @@ -49,14 +53,16 @@ macro_rules! impl_nist_known_answer_tests { assert_eq!(secret_key_hash, kat.sha3_256_hash_of_secret_key, "lhs: computed secret key hash, rhs: hash from kat"); let (ciphertext, shared_secret) = - $encapsulate_derand(key_pair.public_key(), kat.encapsulation_seed); + encapsulate(key_pair.public_key(), kat.encapsulation_seed); let ciphertext_hash = sha256(ciphertext.as_ref()); assert_eq!(ciphertext_hash, kat.sha3_256_hash_of_ciphertext, "lhs: computed ciphertext hash, rhs: hash from akt"); assert_eq!(shared_secret.as_ref(), kat.shared_secret, "lhs: computed shared secret from encapsulate, rhs: shared secret from kat"); + assert!(validate_private_key(key_pair.private_key(), &ciphertext)); + let shared_secret_from_decapsulate = - $decapsulate_derand(key_pair.private_key(), &ciphertext); + decapsulate(key_pair.private_key(), &ciphertext); assert_eq!(shared_secret_from_decapsulate, shared_secret.as_ref(), "lhs: shared secret computed via decapsulation, rhs: shared secret computed via encapsulation"); } } @@ -67,9 +73,7 @@ impl_nist_known_answer_tests!( mlkem512_nist_known_answer_tests, "mlkem_ipd", 512, - libcrux_ml_kem::mlkem512::generate_key_pair, - libcrux_ml_kem::mlkem512::encapsulate, - libcrux_ml_kem::mlkem512::decapsulate + libcrux_ml_kem::mlkem512 ); #[cfg(all(not(feature = "pre-verification"), feature = "mlkem768"))] @@ -77,9 +81,7 @@ impl_nist_known_answer_tests!( mlkem768_nist_known_answer_tests, "mlkem_ipd", 768, - libcrux_ml_kem::mlkem768::generate_key_pair, - libcrux_ml_kem::mlkem768::encapsulate, - libcrux_ml_kem::mlkem768::decapsulate + libcrux_ml_kem::mlkem768 ); #[cfg(all(not(feature = "pre-verification"), feature = "mlkem1024"))] @@ -87,9 +89,7 @@ impl_nist_known_answer_tests!( mlkem1024_nist_known_answer_tests, "mlkem_ipd", 1024, - libcrux_ml_kem::mlkem1024::generate_key_pair, - libcrux_ml_kem::mlkem1024::encapsulate, - libcrux_ml_kem::mlkem1024::decapsulate + libcrux_ml_kem::mlkem1024 ); #[cfg(all(feature = "mlkem512", feature = "pre-verification"))] @@ -97,9 +97,7 @@ impl_nist_known_answer_tests!( mlkem512_nist_kats_portable, "mlkem", 512, - libcrux_ml_kem::mlkem512::portable::generate_key_pair, - libcrux_ml_kem::mlkem512::portable::encapsulate, - libcrux_ml_kem::mlkem512::portable::decapsulate + libcrux_ml_kem::mlkem512::portable ); #[cfg(all(feature = "mlkem768", feature = "pre-verification"))] @@ -107,9 +105,7 @@ impl_nist_known_answer_tests!( mlkem768_nist_kats_portable, "mlkem", 768, - libcrux_ml_kem::mlkem768::portable::generate_key_pair, - libcrux_ml_kem::mlkem768::portable::encapsulate, - libcrux_ml_kem::mlkem768::portable::decapsulate + libcrux_ml_kem::mlkem768::portable ); #[cfg(all(feature = "mlkem1024", feature = "pre-verification"))] @@ -117,9 +113,7 @@ impl_nist_known_answer_tests!( mlkem1024_nist_kats_portable, "mlkem", 1024, - libcrux_ml_kem::mlkem1024::portable::generate_key_pair, - libcrux_ml_kem::mlkem1024::portable::encapsulate, - libcrux_ml_kem::mlkem1024::portable::decapsulate + libcrux_ml_kem::mlkem1024::portable ); #[cfg(all(feature = "mlkem512", feature = "kyber", feature = "pre-verification"))] @@ -127,9 +121,7 @@ impl_nist_known_answer_tests!( kyber512_nist_kats_portable, "kyber", 512, - libcrux_ml_kem::kyber512::generate_key_pair, - libcrux_ml_kem::kyber512::encapsulate, - libcrux_ml_kem::kyber512::decapsulate + libcrux_ml_kem::kyber512 ); #[cfg(all(feature = "mlkem768", feature = "kyber", feature = "pre-verification"))] @@ -137,9 +129,7 @@ impl_nist_known_answer_tests!( kyber768_nist_kats_portable, "kyber", 768, - libcrux_ml_kem::kyber768::generate_key_pair, - libcrux_ml_kem::kyber768::encapsulate, - libcrux_ml_kem::kyber768::decapsulate + libcrux_ml_kem::kyber768 ); #[cfg(all(feature = "mlkem1024", feature = "kyber", feature = "pre-verification"))] @@ -147,7 +137,5 @@ impl_nist_known_answer_tests!( kyber1024_nist_kats_portable, "kyber", 1024, - libcrux_ml_kem::kyber1024::generate_key_pair, - libcrux_ml_kem::kyber1024::encapsulate, - libcrux_ml_kem::kyber1024::decapsulate + libcrux_ml_kem::kyber1024 );