Introduce newer mlkem key types

This commit is contained in:
Georgio Nicolas
2026-01-23 13:48:55 +01:00
parent 43d1c61b70
commit 60f8fe09a7
7 changed files with 123 additions and 42 deletions
+3
View File
@@ -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 }
+6 -3
View File
@@ -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),
+39 -17
View File
@@ -1,11 +1,10 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// 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<Self, KKTError> {
@@ -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<u8> {
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(),
}
}
}
+3
View File
@@ -48,6 +48,9 @@ pub enum KKTError {
#[error("{}", info)]
AEADError { info: &'static str },
#[error("{}", info)]
DecodingError { info: &'static str },
#[error("Generic libcrux error")]
LibcruxError,
}
+9 -3
View File
@@ -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<R>(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,
+5 -13
View File
@@ -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),
+58 -6
View File
@@ -1,12 +1,14 @@
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
// 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<ed25519::KeyPair>,
@@ -14,8 +16,11 @@ pub struct LpLocalPeer {
/// Local x25519 keys (Noise static key)
pub(crate) x25519: Arc<x25519::KeyPair>,
/// Local KEM key used for PSQ
/// Local KEM key used for PSQ (x25519: to deprecate)
pub(crate) kem_psq: Option<Arc<x25519::KeyPair>>,
/// Local MlKem keypair used for PSQ
pub(crate) mlkem: Option<(Arc<MlKem768PrivateKey>, Arc<MlKem768PublicKey>)>,
}
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<x25519::KeyPair>) -> Self {
self.kem_psq = Some(key);
self
}
pub fn with_mlkem_keypair(
mut self,
decapsulation_key: Arc<MlKem768PrivateKey>,
encapsulation_key: Arc<MlKem768PublicKey>,
) -> Self {
self.mlkem = Some((decapsulation_key, encapsulation_key));
self
}
pub fn ed25519(&self) -> &Arc<ed25519::KeyPair> {
&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<R: rand::CryptoRng + rand::RngCore>(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,
}
}