diff --git a/common/nym-kkt/Cargo.toml b/common/nym-kkt/Cargo.toml index 14b3f2f9fc..028bdbc8b7 100644 --- a/common/nym-kkt/Cargo.toml +++ b/common/nym-kkt/Cargo.toml @@ -25,6 +25,9 @@ zeroize = { workspace = true, features = ["zeroize_derive"] } classic-mceliece-rust = { git = "https://github.com/georgio/classic-mceliece-rust", features = ["mceliece460896f", "zeroize"] } +libcrux-psq = { git = "https://github.com/cryspen/libcrux", features = ["classic-mceliece"] } +libcrux-ml-kem = { git = "https://github.com/cryspen/libcrux"} + [dev-dependencies] rand_chacha = "0.9.0" anyhow = { workspace = true } diff --git a/common/nym-kkt/benches/benches.rs b/common/nym-kkt/benches/benches.rs index 4e76fc452f..e6774542a2 100644 --- a/common/nym-kkt/benches/benches.rs +++ b/common/nym-kkt/benches/benches.rs @@ -12,7 +12,10 @@ use nym_kkt::{ ciphersuite::{Ciphersuite, EncapsulationKey, HashFunction, KEM, SignatureScheme}, context::KKTMode, frame::KKTFrame, - key_utils::{generate_keypair_libcrux, generate_keypair_mceliece, hash_encapsulation_key}, + key_utils::{ + generate_keypair_libcrux, generate_keypair_mceliece, generate_keypair_mlkem, + hash_encapsulation_key, + }, session::{ anonymous_initiator_process, initiator_ingest_response, initiator_process, responder_ingest_message, responder_process, @@ -69,8 +72,8 @@ pub fn kkt_benchmark(c: &mut Criterion) { let (responder_kem_public_key, initiator_kem_public_key) = match kem { KEM::MlKem768 => ( - EncapsulationKey::MlKem768(generate_keypair_libcrux(&mut rng, kem).unwrap().1), - EncapsulationKey::MlKem768(generate_keypair_libcrux(&mut rng, kem).unwrap().1), + EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1), + EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1), ), KEM::XWing => ( EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1), diff --git a/common/nym-kkt/src/ciphersuite.rs b/common/nym-kkt/src/ciphersuite.rs index a822afdf7d..b9f431e6cf 100644 --- a/common/nym-kkt/src/ciphersuite.rs +++ b/common/nym-kkt/src/ciphersuite.rs @@ -1,11 +1,10 @@ // Copyright 2025 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::error::KKTError; use libcrux_kem::{Algorithm, MlKem768PublicKey}; use nym_crypto::asymmetric::ed25519; -use std::fmt::Display; - -use crate::error::KKTError; +use std::fmt::{Debug, Display}; pub const DEFAULT_HASH_LEN: usize = 32; const _: () = assert!(DEFAULT_HASH_LEN <= u8::MAX as usize); @@ -34,17 +33,20 @@ impl Display for HashFunction { } pub enum EncapsulationKey<'a> { - MlKem768(libcrux_kem::PublicKey), + MlKem768(libcrux_kem::MlKem768PublicKey), XWing(libcrux_kem::PublicKey), X25519(libcrux_kem::PublicKey), McEliece(classic_mceliece_rust::PublicKey<'a>), } - -pub enum DecapsulationKey<'a> { - MlKem768(libcrux_kem::PrivateKey), - XWing(libcrux_kem::PrivateKey), - X25519(libcrux_kem::PrivateKey), - McEliece(classic_mceliece_rust::SecretKey<'a>), +impl<'a> Debug for EncapsulationKey<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MlKem768(_) => f.debug_tuple("MlKem768").finish(), + Self::XWing(_) => f.debug_tuple("XWing").finish(), + Self::X25519(_) => f.debug_tuple("X25519").finish(), + Self::McEliece(_) => f.debug_tuple("McEliece").finish(), + } + } } impl<'a> EncapsulationKey<'a> { pub(crate) fn decode(kem: KEM, bytes: &[u8]) -> Result { @@ -68,10 +70,13 @@ impl<'a> EncapsulationKey<'a> { map_kem_to_libcrux_kem(kem)?, bytes, )?)), - KEM::MlKem768 => Ok(EncapsulationKey::MlKem768(libcrux_kem::PublicKey::decode( - map_kem_to_libcrux_kem(kem)?, - bytes, - )?)), + KEM::MlKem768 => Ok(EncapsulationKey::MlKem768( + libcrux_kem::MlKem768PublicKey::try_from(bytes).map_err(|_| { + KKTError::DecodingError { + info: "MlKem Encapsulation Key Decoding Failure", + } + })?, + )), KEM::XWing => Ok(EncapsulationKey::XWing(libcrux_kem::PublicKey::decode( map_kem_to_libcrux_kem(kem)?, bytes, @@ -81,10 +86,27 @@ impl<'a> EncapsulationKey<'a> { pub fn encode(&self) -> Vec { match self { - EncapsulationKey::XWing(public_key) - | EncapsulationKey::MlKem768(public_key) - | EncapsulationKey::X25519(public_key) => public_key.encode(), + EncapsulationKey::XWing(public_key) | EncapsulationKey::X25519(public_key) => { + public_key.encode() + } EncapsulationKey::McEliece(public_key) => Vec::from(public_key.as_array()), + EncapsulationKey::MlKem768(public_key) => Vec::from(public_key.as_slice()), + } + } +} +pub enum DecapsulationKey<'a> { + MlKem768(libcrux_kem::MlKem768PrivateKey), + XWing(libcrux_kem::PrivateKey), + X25519(libcrux_kem::PrivateKey), + McEliece(classic_mceliece_rust::SecretKey<'a>), +} +impl<'a> Debug for DecapsulationKey<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MlKem768(_) => f.debug_tuple("MlKem768").finish(), + Self::XWing(_) => f.debug_tuple("XWing").finish(), + Self::X25519(_) => f.debug_tuple("X25519").finish(), + Self::McEliece(_) => f.debug_tuple("McEliece").finish(), } } } diff --git a/common/nym-kkt/src/error.rs b/common/nym-kkt/src/error.rs index 61744154a1..60abdb8f7a 100644 --- a/common/nym-kkt/src/error.rs +++ b/common/nym-kkt/src/error.rs @@ -48,6 +48,9 @@ pub enum KKTError { #[error("{}", info)] AEADError { info: &'static str }, + #[error("{}", info)] + DecodingError { info: &'static str }, + #[error("Generic libcrux error")] LibcruxError, } diff --git a/common/nym-kkt/src/key_utils.rs b/common/nym-kkt/src/key_utils.rs index 2845e7a907..8b30d55074 100644 --- a/common/nym-kkt/src/key_utils.rs +++ b/common/nym-kkt/src/key_utils.rs @@ -2,6 +2,7 @@ use crate::ciphersuite::HashFunction; use classic_mceliece_rust::keypair_boxed; +use libcrux_kem::{MlKem768PrivateKey, MlKem768PublicKey}; use libcrux_sha3; use rand::{CryptoRng, RngCore}; @@ -37,9 +38,6 @@ where R: RngCore + CryptoRng, { match kem { - crate::ciphersuite::KEM::MlKem768 => { - Ok(libcrux_kem::key_gen(libcrux_kem::Algorithm::MlKem768, rng)?) - } crate::ciphersuite::KEM::XWing => Ok(libcrux_kem::key_gen( libcrux_kem::Algorithm::XWingKemDraft06, rng, @@ -52,6 +50,14 @@ where }), } } + +pub fn generate_keypair_mlkem(rng: &mut R) -> (MlKem768PrivateKey, MlKem768PublicKey) +where + R: RngCore + CryptoRng, +{ + libcrux_ml_kem::mlkem768::rand::generate_key_pair(rng).into_parts() +} + // (decapsulation_key, encapsulation_key) pub fn generate_keypair_mceliece<'a, R>( rng: &mut R, diff --git a/common/nym-kkt/src/lib.rs b/common/nym-kkt/src/lib.rs index 4fbb51b197..c8a033fc69 100644 --- a/common/nym-kkt/src/lib.rs +++ b/common/nym-kkt/src/lib.rs @@ -28,7 +28,7 @@ mod test { frame::KKTFrame, key_utils::{ generate_keypair_ed25519, generate_keypair_libcrux, generate_keypair_mceliece, - generate_keypair_x25519, hash_encapsulation_key, + generate_keypair_mlkem, generate_keypair_x25519, hash_encapsulation_key, }, session::{ anonymous_initiator_process, initiator_ingest_response, initiator_process, @@ -63,12 +63,8 @@ mod test { let (responder_kem_public_key, initiator_kem_public_key) = match kem { KEM::MlKem768 => ( - EncapsulationKey::MlKem768( - generate_keypair_libcrux(&mut rng, kem).unwrap().1, - ), - EncapsulationKey::MlKem768( - generate_keypair_libcrux(&mut rng, kem).unwrap().1, - ), + EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1), + EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1), ), KEM::XWing => ( EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1), @@ -269,12 +265,8 @@ mod test { let (responder_kem_public_key, initiator_kem_public_key) = match kem { KEM::MlKem768 => ( - EncapsulationKey::MlKem768( - generate_keypair_libcrux(&mut rng, kem).unwrap().1, - ), - EncapsulationKey::MlKem768( - generate_keypair_libcrux(&mut rng, kem).unwrap().1, - ), + EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1), + EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1), ), KEM::XWing => ( EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1), diff --git a/common/nym-lp/src/peer.rs b/common/nym-lp/src/peer.rs index 02a6372ea6..c3b1b402fc 100644 --- a/common/nym-lp/src/peer.rs +++ b/common/nym-lp/src/peer.rs @@ -1,12 +1,14 @@ // Copyright 2026 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use libcrux_kem::{MlKem768PrivateKey, MlKem768PublicKey}; use nym_crypto::asymmetric::{ed25519, x25519}; +use std::fmt::Debug; use std::sync::Arc; /// Representation of a local Lewes Protocol peer /// encapsulating all the known information and keys. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct LpLocalPeer { /// Local Ed25519 keys for PSQ authentication pub(crate) ed25519: Arc, @@ -14,8 +16,11 @@ pub struct LpLocalPeer { /// Local x25519 keys (Noise static key) pub(crate) x25519: Arc, - /// Local KEM key used for PSQ + /// Local KEM key used for PSQ (x25519: to deprecate) pub(crate) kem_psq: Option>, + + /// Local MlKem keypair used for PSQ + pub(crate) mlkem: Option<(Arc, Arc)>, } impl LpLocalPeer { @@ -24,15 +29,26 @@ impl LpLocalPeer { ed25519, x25519, kem_psq: None, + mlkem: None, + // mceliece: None, } } - #[must_use] + // #[must_use] pub fn with_kem_psq_key(mut self, key: Arc) -> Self { self.kem_psq = Some(key); self } + pub fn with_mlkem_keypair( + mut self, + decapsulation_key: Arc, + encapsulation_key: Arc, + ) -> Self { + self.mlkem = Some((decapsulation_key, encapsulation_key)); + self + } + pub fn ed25519(&self) -> &Arc { &self.ed25519 } @@ -44,12 +60,12 @@ impl LpLocalPeer { /// Convert this `LpLocalPeer` into a valid `LpRemotePeer` that can be used within tests #[doc(hidden)] pub fn as_remote(&self) -> LpRemotePeer { - let expected_kem_key_digest = match &self.kem_psq { + let expected_kem_key_digest = match &self.mlkem { None => Vec::new(), Some(kem_keys) => nym_kkt::key_utils::hash_key_bytes( &nym_kkt::ciphersuite::HashFunction::Blake3, nym_kkt::ciphersuite::DEFAULT_HASH_LEN, - kem_keys.public_key().as_bytes(), + kem_keys.1.as_slice(), ), }; LpRemotePeer { @@ -70,6 +86,36 @@ impl LpLocalPeer { } } +impl Debug for LpLocalPeer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LpLocalPeer") + .field("ed25519", &self.ed25519) + .field("x25519", &self.x25519) + .field("kem_psq", &self.kem_psq) + .field( + "mlkem", + &format!( + "mlkem_public_key: {}", + match &self.mlkem { + Some(keypair) => format!("{:?}", keypair.1.as_slice()), + None => format!("None"), + } + ), + ) + // .field( + // "mceliece", + // &format!( + // "mceliece_public_key: {}", + // match &self.mceliece { + // Some(keypair) => format!("{:?}", keypair.1.as_slice()), + // None => format!("None"), + // } + // ), + // ) + .finish() + } +} + /// Representation of a remote Lewes Protocol peer /// encapsulating all the known information and keys. #[derive(Debug, Clone)] @@ -117,15 +163,21 @@ pub fn mock_peer() -> LpLocalPeer { } #[cfg(test)] -pub fn random_peer(rng: &mut R) -> LpLocalPeer { +pub fn random_peer<'a, R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> LpLocalPeer { + use nym_kkt::key_utils::generate_keypair_mlkem; + let ed25519 = Arc::new(ed25519::KeyPair::new(rng)); let x25519 = Arc::new(ed25519.to_x25519()); let kem_psq = Some(x25519.clone()); + let mlkem_keypair = generate_keypair_mlkem(&mut rand09::rng()); + LpLocalPeer { ed25519, x25519, kem_psq, + mlkem: Some((Arc::new(mlkem_keypair.0), Arc::new(mlkem_keypair.1))), + // mceliece: None, } }