Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8958126e8 | |||
| fe45b856b7 | |||
| 5c50b760a8 | |||
| aafa99ea47 | |||
| 4767a719f7 | |||
| 89d167de08 | |||
| 05d5f4ae83 | |||
| 2109beeef6 | |||
| d1a5342625 | |||
| f0645bad57 | |||
| 60f8fe09a7 |
Generated
+470
-365
File diff suppressed because it is too large
Load Diff
@@ -23,9 +23,15 @@ libcrux-chacha20poly1305 = { git = "https://github.com/cryspen/libcrux" }
|
|||||||
|
|
||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||||
classic-mceliece-rust = { git = "https://github.com/georgio/classic-mceliece-rust", features = ["mceliece460896f", "zeroize"] }
|
# classic-mceliece-rust = { git = "https://github.com/georgio/classic-mceliece-rust", features = ["mceliece460896f", "zeroize"] }
|
||||||
|
classic-mceliece-rust = { version = "3.1.0", 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]
|
[dev-dependencies]
|
||||||
rand_chacha = "0.9.0"
|
rand_chacha = "0.9.0"
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ use nym_kkt::{
|
|||||||
ciphersuite::{Ciphersuite, EncapsulationKey, HashFunction, KEM, SignatureScheme},
|
ciphersuite::{Ciphersuite, EncapsulationKey, HashFunction, KEM, SignatureScheme},
|
||||||
context::KKTMode,
|
context::KKTMode,
|
||||||
frame::KKTFrame,
|
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::{
|
session::{
|
||||||
anonymous_initiator_process, initiator_ingest_response, initiator_process,
|
anonymous_initiator_process, initiator_ingest_response, initiator_process,
|
||||||
responder_ingest_message, responder_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 {
|
let (responder_kem_public_key, initiator_kem_public_key) = match kem {
|
||||||
KEM::MlKem768 => (
|
KEM::MlKem768 => (
|
||||||
EncapsulationKey::MlKem768(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1),
|
||||||
EncapsulationKey::MlKem768(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1),
|
||||||
),
|
),
|
||||||
KEM::XWing => (
|
KEM::XWing => (
|
||||||
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||||
|
|||||||
@@ -3,23 +3,27 @@
|
|||||||
|
|
||||||
use crate::error::KKTError;
|
use crate::error::KKTError;
|
||||||
use libcrux_kem::Algorithm;
|
use libcrux_kem::Algorithm;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub use nym_kkt_ciphersuite::*;
|
pub use nym_kkt_ciphersuite::*;
|
||||||
|
|
||||||
pub enum EncapsulationKey<'a> {
|
pub enum EncapsulationKey {
|
||||||
MlKem768(libcrux_kem::PublicKey),
|
MlKem768(libcrux_kem::PublicKey),
|
||||||
XWing(libcrux_kem::PublicKey),
|
XWing(libcrux_kem::PublicKey),
|
||||||
X25519(libcrux_kem::PublicKey),
|
X25519(libcrux_kem::PublicKey),
|
||||||
McEliece(classic_mceliece_rust::PublicKey<'a>),
|
McEliece(libcrux_psq::classic_mceliece::PublicKey),
|
||||||
}
|
}
|
||||||
|
impl Debug for EncapsulationKey {
|
||||||
pub enum DecapsulationKey<'a> {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
MlKem768(libcrux_kem::PrivateKey),
|
match self {
|
||||||
XWing(libcrux_kem::PrivateKey),
|
Self::MlKem768(_) => f.debug_tuple("MlKem768").finish(),
|
||||||
X25519(libcrux_kem::PrivateKey),
|
Self::XWing(_) => f.debug_tuple("XWing").finish(),
|
||||||
McEliece(classic_mceliece_rust::SecretKey<'a>),
|
Self::X25519(_) => f.debug_tuple("X25519").finish(),
|
||||||
|
Self::McEliece(_) => f.debug_tuple("McEliece").finish(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl<'a> EncapsulationKey<'a> {
|
impl EncapsulationKey {
|
||||||
pub(crate) fn decode(kem: KEM, bytes: &[u8]) -> Result<Self, KKTError> {
|
pub(crate) fn decode(kem: KEM, bytes: &[u8]) -> Result<Self, KKTError> {
|
||||||
match kem {
|
match kem {
|
||||||
KEM::McEliece => {
|
KEM::McEliece => {
|
||||||
@@ -33,7 +37,7 @@ impl<'a> EncapsulationKey<'a> {
|
|||||||
// Size must be correct due to KKTFrame::from_bytes(message_bytes)?
|
// Size must be correct due to KKTFrame::from_bytes(message_bytes)?
|
||||||
public_key_bytes.clone_from_slice(bytes);
|
public_key_bytes.clone_from_slice(bytes);
|
||||||
Ok(EncapsulationKey::McEliece(
|
Ok(EncapsulationKey::McEliece(
|
||||||
classic_mceliece_rust::PublicKey::from(public_key_bytes),
|
libcrux_psq::classic_mceliece::PublicKey::from(public_key_bytes),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,10 +58,30 @@ impl<'a> EncapsulationKey<'a> {
|
|||||||
|
|
||||||
pub fn encode(&self) -> Vec<u8> {
|
pub fn encode(&self) -> Vec<u8> {
|
||||||
match self {
|
match self {
|
||||||
EncapsulationKey::XWing(public_key)
|
EncapsulationKey::XWing(public_key) | EncapsulationKey::X25519(public_key) => {
|
||||||
| EncapsulationKey::MlKem768(public_key)
|
public_key.encode()
|
||||||
| EncapsulationKey::X25519(public_key) => public_key.encode(),
|
}
|
||||||
EncapsulationKey::McEliece(public_key) => Vec::from(public_key.as_array()),
|
EncapsulationKey::McEliece(public_key) => {
|
||||||
|
let bytes_ref: &[u8] = public_key.as_ref();
|
||||||
|
Vec::from(bytes_ref)
|
||||||
|
}
|
||||||
|
EncapsulationKey::MlKem768(public_key) => Vec::from(public_key.encode()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub enum DecapsulationKey {
|
||||||
|
MlKem768(libcrux_kem::MlKem768PrivateKey),
|
||||||
|
XWing(libcrux_kem::PrivateKey),
|
||||||
|
X25519(libcrux_kem::PrivateKey),
|
||||||
|
McEliece(libcrux_psq::classic_mceliece::SecretKey),
|
||||||
|
}
|
||||||
|
impl Debug for DecapsulationKey {
|
||||||
|
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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ pub enum KKTError {
|
|||||||
#[error("{}", info)]
|
#[error("{}", info)]
|
||||||
AEADError { info: &'static str },
|
AEADError { info: &'static str },
|
||||||
|
|
||||||
|
#[error("{}", info)]
|
||||||
|
DecodingError { info: &'static str },
|
||||||
|
|
||||||
#[error("Generic libcrux error")]
|
#[error("Generic libcrux error")]
|
||||||
LibcruxError,
|
LibcruxError,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use crate::ciphersuite::HashFunction;
|
use crate::ciphersuite::HashFunction;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use classic_mceliece_rust::keypair_boxed;
|
use libcrux_kem::{MlKem768PrivateKey, MlKem768PublicKey};
|
||||||
|
|
||||||
use nym_kkt_ciphersuite::{DEFAULT_HASH_LEN, KEMKeyDigests};
|
use nym_kkt_ciphersuite::{DEFAULT_HASH_LEN, KEMKeyDigests};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
@@ -38,9 +37,6 @@ where
|
|||||||
R: RngCore + CryptoRng,
|
R: RngCore + CryptoRng,
|
||||||
{
|
{
|
||||||
match kem {
|
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(
|
crate::ciphersuite::KEM::XWing => Ok(libcrux_kem::key_gen(
|
||||||
libcrux_kem::Algorithm::XWingKemDraft06,
|
libcrux_kem::Algorithm::XWingKemDraft06,
|
||||||
rng,
|
rng,
|
||||||
@@ -53,19 +49,28 @@ 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)
|
// (decapsulation_key, encapsulation_key)
|
||||||
pub fn generate_keypair_mceliece<'a, R>(
|
pub fn generate_keypair_mceliece<R>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) -> (
|
) -> (
|
||||||
classic_mceliece_rust::SecretKey<'a>,
|
libcrux_psq::classic_mceliece::SecretKey,
|
||||||
classic_mceliece_rust::PublicKey<'a>,
|
libcrux_psq::classic_mceliece::PublicKey,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
// this is annoying because mceliece lib uses rand 0.8.5...
|
// this is annoying because mceliece lib uses rand 0.8.5...
|
||||||
R: RngCore + CryptoRng,
|
R: RngCore + CryptoRng,
|
||||||
{
|
{
|
||||||
let (encapsulation_key, decapsulation_key) = keypair_boxed(rng);
|
let kp = libcrux_psq::classic_mceliece::KeyPair::generate_key_pair(rng);
|
||||||
(decapsulation_key, encapsulation_key)
|
|
||||||
|
(kp.sk, kp.pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_key_bytes(
|
pub fn hash_key_bytes(
|
||||||
|
|||||||
@@ -98,13 +98,13 @@ pub fn request_kem_key<R: CryptoRng + RngCore>(
|
|||||||
/// )?;
|
/// )?;
|
||||||
/// // Use gateway_kem_key for PSQ
|
/// // Use gateway_kem_key for PSQ
|
||||||
/// ```
|
/// ```
|
||||||
pub fn validate_kem_response<'a>(
|
pub fn validate_kem_response(
|
||||||
context: &mut KKTContext,
|
context: &mut KKTContext,
|
||||||
session_secret: &KKTSessionSecret,
|
session_secret: &KKTSessionSecret,
|
||||||
responder_vk: &ed25519::PublicKey,
|
responder_vk: &ed25519::PublicKey,
|
||||||
expected_key_hash: &[u8],
|
expected_key_hash: &[u8],
|
||||||
encrypted_response_bytes: &[u8],
|
encrypted_response_bytes: &[u8],
|
||||||
) -> Result<EncapsulationKey<'a>, KKTError> {
|
) -> Result<EncapsulationKey, KKTError> {
|
||||||
let (responder_frame, responder_context) =
|
let (responder_frame, responder_context) =
|
||||||
decrypt_kkt_response_frame(session_secret, encrypted_response_bytes)?;
|
decrypt_kkt_response_frame(session_secret, encrypted_response_bytes)?;
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ pub fn handle_kem_request<'a, R>(
|
|||||||
initiator_vk: Option<&ed25519::PublicKey>,
|
initiator_vk: Option<&ed25519::PublicKey>,
|
||||||
responder_signing_key: &ed25519::PrivateKey,
|
responder_signing_key: &ed25519::PrivateKey,
|
||||||
responder_dh_private_key: &x25519::PrivateKey,
|
responder_dh_private_key: &x25519::PrivateKey,
|
||||||
responder_kem_key: &EncapsulationKey<'a>,
|
responder_kem_key: &EncapsulationKey,
|
||||||
) -> Result<Vec<u8>, KKTError>
|
) -> Result<Vec<u8>, KKTError>
|
||||||
where
|
where
|
||||||
R: RngCore + CryptoRng,
|
R: RngCore + CryptoRng,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ mod test {
|
|||||||
frame::KKTFrame,
|
frame::KKTFrame,
|
||||||
key_utils::{
|
key_utils::{
|
||||||
generate_keypair_ed25519, generate_keypair_libcrux, generate_keypair_mceliece,
|
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::{
|
session::{
|
||||||
anonymous_initiator_process, initiator_ingest_response, initiator_process,
|
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 {
|
let (responder_kem_public_key, initiator_kem_public_key) = match kem {
|
||||||
KEM::MlKem768 => (
|
KEM::MlKem768 => (
|
||||||
EncapsulationKey::MlKem768(
|
EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1),
|
||||||
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1),
|
||||||
),
|
|
||||||
EncapsulationKey::MlKem768(
|
|
||||||
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
KEM::XWing => (
|
KEM::XWing => (
|
||||||
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
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 {
|
let (responder_kem_public_key, initiator_kem_public_key) = match kem {
|
||||||
KEM::MlKem768 => (
|
KEM::MlKem768 => (
|
||||||
EncapsulationKey::MlKem768(
|
EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1),
|
||||||
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1),
|
||||||
),
|
|
||||||
EncapsulationKey::MlKem768(
|
|
||||||
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
KEM::XWing => (
|
KEM::XWing => (
|
||||||
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ use crate::{
|
|||||||
key_utils::validate_encapsulation_key,
|
key_utils::validate_encapsulation_key,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn initiator_process<'a, R>(
|
pub fn initiator_process<R>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
mode: KKTMode,
|
mode: KKTMode,
|
||||||
ciphersuite: Ciphersuite,
|
ciphersuite: Ciphersuite,
|
||||||
signing_key: &ed25519::PrivateKey,
|
signing_key: &ed25519::PrivateKey,
|
||||||
own_encapsulation_key: Option<&EncapsulationKey<'a>>,
|
own_encapsulation_key: Option<&EncapsulationKey>,
|
||||||
) -> Result<(KKTContext, KKTFrame), KKTError>
|
) -> Result<(KKTContext, KKTFrame), KKTError>
|
||||||
where
|
where
|
||||||
R: CryptoRng + RngCore,
|
R: CryptoRng + RngCore,
|
||||||
@@ -72,13 +72,13 @@ where
|
|||||||
Ok((context, KKTFrame::new(context_bytes, &[], session_id, &[])))
|
Ok((context, KKTFrame::new(context_bytes, &[], session_id, &[])))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initiator_ingest_response<'a>(
|
pub fn initiator_ingest_response(
|
||||||
own_context: &mut KKTContext,
|
own_context: &mut KKTContext,
|
||||||
remote_frame: &KKTFrame,
|
remote_frame: &KKTFrame,
|
||||||
remote_context: &KKTContext,
|
remote_context: &KKTContext,
|
||||||
remote_verification_key: &ed25519::PublicKey,
|
remote_verification_key: &ed25519::PublicKey,
|
||||||
expected_hash: &[u8],
|
expected_hash: &[u8],
|
||||||
) -> Result<EncapsulationKey<'a>, KKTError> {
|
) -> Result<EncapsulationKey, KKTError> {
|
||||||
check_compatibility(own_context, remote_context)?;
|
check_compatibility(own_context, remote_context)?;
|
||||||
match remote_context.status() {
|
match remote_context.status() {
|
||||||
KKTStatus::Ok => {
|
KKTStatus::Ok => {
|
||||||
@@ -124,12 +124,12 @@ pub fn initiator_ingest_response<'a>(
|
|||||||
|
|
||||||
// todo: figure out how to handle errors using status codes
|
// todo: figure out how to handle errors using status codes
|
||||||
|
|
||||||
pub fn responder_ingest_message<'a>(
|
pub fn responder_ingest_message(
|
||||||
remote_context: &KKTContext,
|
remote_context: &KKTContext,
|
||||||
remote_verification_key: Option<&ed25519::PublicKey>,
|
remote_verification_key: Option<&ed25519::PublicKey>,
|
||||||
expected_hash: Option<&[u8]>,
|
expected_hash: Option<&[u8]>,
|
||||||
remote_frame: &KKTFrame,
|
remote_frame: &KKTFrame,
|
||||||
) -> Result<(KKTContext, Option<EncapsulationKey<'a>>), KKTError> {
|
) -> Result<(KKTContext, Option<EncapsulationKey>), KKTError> {
|
||||||
let own_context = remote_context.derive_responder_header()?;
|
let own_context = remote_context.derive_responder_header()?;
|
||||||
|
|
||||||
match remote_context.role() {
|
match remote_context.role() {
|
||||||
@@ -200,11 +200,11 @@ pub fn responder_ingest_message<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn responder_process<'a>(
|
pub fn responder_process(
|
||||||
own_context: &mut KKTContext,
|
own_context: &mut KKTContext,
|
||||||
session_id: KKTSessionId,
|
session_id: KKTSessionId,
|
||||||
signing_key: &ed25519::PrivateKey,
|
signing_key: &ed25519::PrivateKey,
|
||||||
encapsulation_key: &EncapsulationKey<'a>,
|
encapsulation_key: &EncapsulationKey,
|
||||||
) -> Result<KKTFrame, KKTError> {
|
) -> Result<KKTFrame, KKTError> {
|
||||||
let body = encapsulation_key.encode();
|
let body = encapsulation_key.encode();
|
||||||
|
|
||||||
|
|||||||
+92
-16
@@ -1,14 +1,16 @@
|
|||||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use libcrux_kem::{MlKem768PrivateKey, MlKem768PublicKey};
|
||||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use nym_kkt::ciphersuite::{HashFunction, KEM};
|
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey, HashFunction, KEM};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Representation of a local Lewes Protocol peer
|
/// Representation of a local Lewes Protocol peer
|
||||||
/// encapsulating all the known information and keys.
|
/// encapsulating all the known information and keys.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LpLocalPeer {
|
pub struct LpLocalPeer {
|
||||||
/// Local Ed25519 keys for PSQ authentication
|
/// Local Ed25519 keys for PSQ authentication
|
||||||
pub(crate) ed25519: Arc<ed25519::KeyPair>,
|
pub(crate) ed25519: Arc<ed25519::KeyPair>,
|
||||||
@@ -16,8 +18,14 @@ pub struct LpLocalPeer {
|
|||||||
/// Local x25519 keys (Noise static key)
|
/// Local x25519 keys (Noise static key)
|
||||||
pub(crate) x25519: Arc<x25519::KeyPair>,
|
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>>,
|
pub(crate) kem_psq: Option<Arc<x25519::KeyPair>>,
|
||||||
|
|
||||||
|
/// Local MlKem keypair used for PSQ
|
||||||
|
pub(crate) mlkem: Option<(Arc<DecapsulationKey>, Arc<EncapsulationKey>)>,
|
||||||
|
|
||||||
|
/// Local McEliece keypair used for PSQ
|
||||||
|
pub(crate) mceliece: Option<(Arc<DecapsulationKey>, Arc<EncapsulationKey>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LpLocalPeer {
|
impl LpLocalPeer {
|
||||||
@@ -26,15 +34,43 @@ impl LpLocalPeer {
|
|||||||
ed25519,
|
ed25519,
|
||||||
x25519,
|
x25519,
|
||||||
kem_psq: None,
|
kem_psq: None,
|
||||||
|
mlkem: None,
|
||||||
|
mceliece: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
// #[must_use]
|
||||||
pub fn with_kem_psq_key(mut self, key: Arc<x25519::KeyPair>) -> Self {
|
pub fn with_kem_psq_key(mut self, key: Arc<x25519::KeyPair>) -> Self {
|
||||||
self.kem_psq = Some(key);
|
self.kem_psq = Some(key);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_mlkem_keypair(
|
||||||
|
mut self,
|
||||||
|
decapsulation_key: &MlKem768PrivateKey,
|
||||||
|
encapsulation_key: &MlKem768PublicKey,
|
||||||
|
) -> Self {
|
||||||
|
self.mlkem = Some((
|
||||||
|
Arc::new(DecapsulationKey::MlKem768(decapsulation_key.clone())),
|
||||||
|
Arc::new(EncapsulationKey::MlKem768(
|
||||||
|
libcrux_kem::PublicKey::MlKem768(encapsulation_key.clone()),
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_mceliece_keypair(
|
||||||
|
mut self,
|
||||||
|
decapsulation_key: libcrux_psq::classic_mceliece::SecretKey,
|
||||||
|
encapsulation_key: libcrux_psq::classic_mceliece::PublicKey,
|
||||||
|
) -> Self {
|
||||||
|
self.mceliece = Some((
|
||||||
|
Arc::new(DecapsulationKey::McEliece(decapsulation_key)),
|
||||||
|
Arc::new(EncapsulationKey::McEliece(encapsulation_key)),
|
||||||
|
));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ed25519(&self) -> &Arc<ed25519::KeyPair> {
|
pub fn ed25519(&self) -> &Arc<ed25519::KeyPair> {
|
||||||
&self.ed25519
|
&self.ed25519
|
||||||
}
|
}
|
||||||
@@ -46,17 +82,28 @@ impl LpLocalPeer {
|
|||||||
/// Convert this `LpLocalPeer` into a valid `LpRemotePeer` that can be used within tests
|
/// Convert this `LpLocalPeer` into a valid `LpRemotePeer` that can be used within tests
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn as_remote(&self) -> LpRemotePeer {
|
pub fn as_remote(&self) -> LpRemotePeer {
|
||||||
let expected_kem_key_digests = match &self.kem_psq {
|
let mut expected_kem_key_digests = HashMap::new();
|
||||||
None => HashMap::new(),
|
|
||||||
Some(kem_keys) => {
|
if let Some(x25519_key) = &self.kem_psq {
|
||||||
let hashes =
|
expected_kem_key_digests.insert(
|
||||||
nym_kkt::key_utils::produce_key_digests(kem_keys.public_key().as_bytes());
|
KEM::X25519,
|
||||||
|
nym_kkt::key_utils::produce_key_digests(x25519_key.public_key().as_bytes()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(mlkem_key) = &self.mlkem {
|
||||||
|
expected_kem_key_digests.insert(
|
||||||
|
KEM::MlKem768,
|
||||||
|
nym_kkt::key_utils::produce_key_digests(&mlkem_key.1.encode()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mceliece_key) = &self.mceliece {
|
||||||
|
expected_kem_key_digests.insert(
|
||||||
|
KEM::McEliece,
|
||||||
|
nym_kkt::key_utils::produce_key_digests(&mceliece_key.1.encode()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mut digests = HashMap::new();
|
|
||||||
digests.insert(KEM::X25519, hashes);
|
|
||||||
digests
|
|
||||||
}
|
|
||||||
};
|
|
||||||
LpRemotePeer {
|
LpRemotePeer {
|
||||||
ed25519_public: *self.ed25519.public_key(),
|
ed25519_public: *self.ed25519.public_key(),
|
||||||
x25519_public: *self.x25519.public_key(),
|
x25519_public: *self.x25519.public_key(),
|
||||||
@@ -66,7 +113,7 @@ impl LpLocalPeer {
|
|||||||
|
|
||||||
// this is only exposed in tests as ideally we should be storing the proper types to begin with
|
// this is only exposed in tests as ideally we should be storing the proper types to begin with
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn encapsulate_kem_key(&self) -> Option<nym_kkt::ciphersuite::EncapsulationKey<'_>> {
|
pub fn encapsulate_kem_key(&self) -> Option<nym_kkt::ciphersuite::EncapsulationKey> {
|
||||||
let pk_bytes = self.kem_psq.as_ref()?.public_key().to_bytes();
|
let pk_bytes = self.kem_psq.as_ref()?.public_key().to_bytes();
|
||||||
let libcrux_pk =
|
let libcrux_pk =
|
||||||
libcrux_kem::PublicKey::decode(libcrux_kem::Algorithm::X25519, &pk_bytes).ok()?;
|
libcrux_kem::PublicKey::decode(libcrux_kem::Algorithm::X25519, &pk_bytes).ok()?;
|
||||||
@@ -75,6 +122,26 @@ 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_ref()),
|
||||||
|
None => "None".to_string(),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Representation of a remote Lewes Protocol peer
|
/// Representation of a remote Lewes Protocol peer
|
||||||
/// encapsulating all the known information and keys.
|
/// encapsulating all the known information and keys.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -124,16 +191,25 @@ pub fn mock_peer() -> LpLocalPeer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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_mceliece, generate_keypair_mlkem};
|
||||||
|
|
||||||
let ed25519 = Arc::new(ed25519::KeyPair::new(rng));
|
let ed25519 = Arc::new(ed25519::KeyPair::new(rng));
|
||||||
let x25519 = Arc::new(ed25519.to_x25519());
|
let x25519 = Arc::new(ed25519.to_x25519());
|
||||||
let kem_psq = Some(x25519.clone());
|
let kem_psq = Some(x25519.clone());
|
||||||
|
|
||||||
|
let mlkem_keypair = generate_keypair_mlkem(&mut rand09::rng());
|
||||||
|
let mceliece_keypair = generate_keypair_mceliece(&mut rand09::rng());
|
||||||
|
|
||||||
LpLocalPeer {
|
LpLocalPeer {
|
||||||
ed25519,
|
ed25519,
|
||||||
x25519,
|
x25519,
|
||||||
kem_psq,
|
kem_psq,
|
||||||
|
mlkem: None,
|
||||||
|
mceliece: None,
|
||||||
}
|
}
|
||||||
|
.with_mlkem_keypair(&mlkem_keypair.0, &mlkem_keypair.1)
|
||||||
|
.with_mceliece_keypair(mceliece_keypair.0, mceliece_keypair.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
+57
-36
@@ -48,7 +48,9 @@
|
|||||||
|
|
||||||
use crate::LpError;
|
use crate::LpError;
|
||||||
use libcrux_psq::v1::cred::{Authenticator, Ed25519};
|
use libcrux_psq::v1::cred::{Authenticator, Ed25519};
|
||||||
|
use libcrux_psq::v1::impls::MlKem768 as PsqMlKem768;
|
||||||
use libcrux_psq::v1::impls::X25519 as PsqX25519;
|
use libcrux_psq::v1::impls::X25519 as PsqX25519;
|
||||||
|
use libcrux_psq::v1::impls::XWingKemDraft06 as PsqXwing;
|
||||||
use libcrux_psq::v1::psk_registration::{Initiator, InitiatorMsg, Responder};
|
use libcrux_psq::v1::psk_registration::{Initiator, InitiatorMsg, Responder};
|
||||||
use libcrux_psq::v1::traits::{Ciphertext as PsqCiphertext, PSQ};
|
use libcrux_psq::v1::traits::{Ciphertext as PsqCiphertext, PSQ};
|
||||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
@@ -290,64 +292,83 @@ pub fn psq_initiator_create_message(
|
|||||||
// Step 1: Classical ECDH for baseline security
|
// Step 1: Classical ECDH for baseline security
|
||||||
let ecdh_secret = local_x25519_private.diffie_hellman(remote_x25519_public);
|
let ecdh_secret = local_x25519_private.diffie_hellman(remote_x25519_public);
|
||||||
|
|
||||||
// Step 2: PSQ v1 with Ed25519 authentication
|
|
||||||
// Extract X25519 KEM key from EncapsulationKey
|
|
||||||
let kem_pk = match remote_kem_public {
|
|
||||||
EncapsulationKey::X25519(pk) => pk,
|
|
||||||
_ => {
|
|
||||||
return Err(LpError::KKTError(
|
|
||||||
"Only X25519 KEM is currently supported for PSQ".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert nym Ed25519 keys to libcrux format
|
// Convert nym Ed25519 keys to libcrux format
|
||||||
type Ed25519VerificationKey = <Ed25519 as Authenticator>::VerificationKey;
|
type Ed25519VerificationKey = <Ed25519 as Authenticator>::VerificationKey;
|
||||||
let ed25519_sk_bytes = client_ed25519_sk.to_bytes();
|
let ed25519_sk_bytes = client_ed25519_sk.to_bytes();
|
||||||
let ed25519_pk_bytes = client_ed25519_pk.to_bytes();
|
let ed25519_pk_bytes = client_ed25519_pk.to_bytes();
|
||||||
let ed25519_verification_key = Ed25519VerificationKey::from_bytes(ed25519_pk_bytes);
|
let ed25519_verification_key = Ed25519VerificationKey::from_bytes(ed25519_pk_bytes);
|
||||||
|
|
||||||
// Use PSQ v1 API with Ed25519 authentication
|
let init_msg_err = |e: libcrux_psq::v1::Error| {
|
||||||
let mut rng = rand09::rng();
|
|
||||||
let (state, initiator_msg) = Initiator::send_initial_message::<Ed25519, PsqX25519>(
|
|
||||||
session_context,
|
|
||||||
Duration::from_secs(3600), // 1 hour expiry
|
|
||||||
kem_pk,
|
|
||||||
&ed25519_sk_bytes,
|
|
||||||
&ed25519_verification_key,
|
|
||||||
&mut rng,
|
|
||||||
)
|
|
||||||
.map_err(|e| {
|
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
"PSQ initiator failed - KEM encapsulation or signing error: {:?}",
|
"PSQ initiator failed - KEM encapsulation or signing error: {:?}",
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
LpError::Internal(format!("PSQ v1 send_initial_message failed: {:?}", e))
|
LpError::Internal(format!("PSQ v1 send_initial_message failed: {:?}", e))
|
||||||
})?;
|
};
|
||||||
|
|
||||||
// Extract PSQ shared secret (unregistered PSK) - this is K_pq
|
let serialisation_err =
|
||||||
let psq_psk = state.unregistered_psk();
|
|e| LpError::Internal(format!("InitiatorMsg serialization failed: {:?}", e));
|
||||||
|
|
||||||
// pq_shared_secret is the raw K_pq from KEM encapsulation.
|
let ctx = session_context;
|
||||||
// Store it for subsession derivation before it's combined with ECDH.
|
let sk = &ed25519_sk_bytes;
|
||||||
let pq_shared_secret: [u8; 32] = *psq_psk;
|
let vk = &ed25519_verification_key;
|
||||||
|
let exp = Duration::from_secs(3600); // 1 hour expiry
|
||||||
|
|
||||||
|
// Step 2: PSQ v1 with Ed25519 authentication
|
||||||
|
// Use PSQ v1 API with Ed25519 authentication
|
||||||
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
|
// psq_psk - this is K_pq
|
||||||
|
let (pq_shared_secret, payload) = match remote_kem_public {
|
||||||
|
EncapsulationKey::X25519(kem_pk) => {
|
||||||
|
let (state, initiator_msg) = Initiator::send_initial_message::<Ed25519, PsqX25519>(
|
||||||
|
ctx, exp, kem_pk, sk, vk, &mut rng,
|
||||||
|
)
|
||||||
|
.map_err(init_msg_err)?;
|
||||||
|
|
||||||
|
let msg_bytes = initiator_msg
|
||||||
|
.tls_serialize_detached()
|
||||||
|
.map_err(serialisation_err)?;
|
||||||
|
(*state.unregistered_psk(), msg_bytes)
|
||||||
|
}
|
||||||
|
EncapsulationKey::MlKem768(kem_pk) => {
|
||||||
|
let (state, initiator_msg) = Initiator::send_initial_message::<Ed25519, PsqMlKem768>(
|
||||||
|
ctx, exp, kem_pk, sk, vk, &mut rng,
|
||||||
|
)
|
||||||
|
.map_err(init_msg_err)?;
|
||||||
|
|
||||||
|
let msg_bytes = initiator_msg
|
||||||
|
.tls_serialize_detached()
|
||||||
|
.map_err(serialisation_err)?;
|
||||||
|
(*state.unregistered_psk(), msg_bytes)
|
||||||
|
}
|
||||||
|
EncapsulationKey::XWing(kem_pk) => {
|
||||||
|
let (state, initiator_msg) = Initiator::send_initial_message::<Ed25519, PsqXwing>(
|
||||||
|
ctx, exp, kem_pk, sk, vk, &mut rng,
|
||||||
|
)
|
||||||
|
.map_err(init_msg_err)?;
|
||||||
|
|
||||||
|
let msg_bytes = initiator_msg
|
||||||
|
.tls_serialize_detached()
|
||||||
|
.map_err(serialisation_err)?;
|
||||||
|
(*state.unregistered_psk(), msg_bytes)
|
||||||
|
}
|
||||||
|
EncapsulationKey::McEliece(pk) => {
|
||||||
|
todo!("unsupported mceliece for PSQ")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Step 3: Combine ECDH + PSQ via Blake3 KDF
|
// Step 3: Combine ECDH + PSQ via Blake3 KDF
|
||||||
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
let mut combined = Vec::with_capacity(64 + pq_shared_secret.len());
|
||||||
combined.extend_from_slice(&ecdh_secret);
|
combined.extend_from_slice(&ecdh_secret);
|
||||||
combined.extend_from_slice(psq_psk); // psq_psk is already a &[u8; 32]
|
combined.extend_from_slice(&pq_shared_secret); // psq_psk is already a &[u8; 32]
|
||||||
combined.extend_from_slice(salt);
|
combined.extend_from_slice(salt);
|
||||||
|
|
||||||
let final_psk = nym_crypto::kdf::derive_key_blake3(PSK_PSQ_CONTEXT, &combined, &[]);
|
let final_psk = nym_crypto::kdf::derive_key_blake3(PSK_PSQ_CONTEXT, &combined, &[]);
|
||||||
|
|
||||||
// Serialize InitiatorMsg with TLS encoding for transport
|
|
||||||
let msg_bytes = initiator_msg
|
|
||||||
.tls_serialize_detached()
|
|
||||||
.map_err(|e| LpError::Internal(format!("InitiatorMsg serialization failed: {:?}", e)))?;
|
|
||||||
|
|
||||||
Ok(PsqInitiatorResult {
|
Ok(PsqInitiatorResult {
|
||||||
psk: final_psk,
|
psk: final_psk,
|
||||||
payload: msg_bytes,
|
payload,
|
||||||
pq_shared_secret,
|
pq_shared_secret,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use crate::replay::ReceivingKeyCounterValidator;
|
|||||||
use crate::{LpError, LpMessage, LpPacket};
|
use crate::{LpError, LpMessage, LpPacket};
|
||||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use nym_kkt::KKT_RESPONSE_AAD;
|
use nym_kkt::KKT_RESPONSE_AAD;
|
||||||
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey};
|
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey, KEM};
|
||||||
use nym_kkt::context::KKTContext;
|
use nym_kkt::context::KKTContext;
|
||||||
use nym_kkt::encryption::{
|
use nym_kkt::encryption::{
|
||||||
KKTSessionSecret, decrypt_initial_kkt_frame, decrypt_kkt_frame, encrypt_initial_kkt_frame,
|
KKTSessionSecret, decrypt_initial_kkt_frame, decrypt_kkt_frame, encrypt_initial_kkt_frame,
|
||||||
@@ -32,6 +32,7 @@ use nym_kkt::session::{
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use snow::Builder;
|
use snow::Builder;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
@@ -90,7 +91,7 @@ pub enum KKTState {
|
|||||||
/// KKT exchange completed (initiator received and validated KEM key).
|
/// KKT exchange completed (initiator received and validated KEM key).
|
||||||
Completed {
|
Completed {
|
||||||
/// Responder's KEM public key for PSQ encapsulation
|
/// Responder's KEM public key for PSQ encapsulation
|
||||||
kem_pk: Box<EncapsulationKey<'static>>,
|
kem_pk: Box<EncapsulationKey>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Responder processed a KKT request and sent response.
|
/// Responder processed a KKT request and sent response.
|
||||||
@@ -303,6 +304,14 @@ impl LpSession {
|
|||||||
self.remote_peer.ed25519_public
|
self.remote_peer.ed25519_public
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn local_kem_encapsulation_key(&self, kem: &KEM) -> Option<Arc<EncapsulationKey>> {
|
||||||
|
match kem {
|
||||||
|
KEM::MlKem768 => self.local_peer.mlkem.as_ref().map(|x| x.1.clone()),
|
||||||
|
KEM::McEliece => self.local_peer.mceliece.as_ref().map(|x| x.1.clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the remote X25519 public key.
|
/// Returns the remote X25519 public key.
|
||||||
///
|
///
|
||||||
/// Used for tie-breaking in simultaneous subsession initiation.
|
/// Used for tie-breaking in simultaneous subsession initiation.
|
||||||
@@ -538,9 +547,7 @@ impl LpSession {
|
|||||||
|
|
||||||
// TODO: missing Zeroize
|
// TODO: missing Zeroize
|
||||||
/// Convert local KEM PSQ keys to typed EncapsulationKey / EncapsulationKey
|
/// Convert local KEM PSQ keys to typed EncapsulationKey / EncapsulationKey
|
||||||
pub fn encapsulated_kem_keys(
|
pub fn encapsulated_kem_keys(&self) -> Result<(DecapsulationKey, EncapsulationKey), LpError> {
|
||||||
&self,
|
|
||||||
) -> Result<(DecapsulationKey<'_>, EncapsulationKey<'_>), LpError> {
|
|
||||||
let kem_keys = self
|
let kem_keys = self
|
||||||
.local_peer
|
.local_peer
|
||||||
.kem_psq
|
.kem_psq
|
||||||
@@ -599,7 +606,7 @@ impl LpSession {
|
|||||||
|
|
||||||
// georgio this needs to be moved one level up (maybe chosen in LPSession)
|
// georgio this needs to be moved one level up (maybe chosen in LPSession)
|
||||||
let ciphersuite = match Ciphersuite::resolve_ciphersuite(
|
let ciphersuite = match Ciphersuite::resolve_ciphersuite(
|
||||||
KEM::X25519,
|
KEM::MlKem768,
|
||||||
HashFunction::Blake3,
|
HashFunction::Blake3,
|
||||||
SignatureScheme::Ed25519,
|
SignatureScheme::Ed25519,
|
||||||
None,
|
None,
|
||||||
@@ -743,11 +750,7 @@ impl LpSession {
|
|||||||
///
|
///
|
||||||
/// * `Ok(LpMessage::KKTResponse)` - Signed KKT response ready to send
|
/// * `Ok(LpMessage::KKTResponse)` - Signed KKT response ready to send
|
||||||
/// * `Err(LpError)` - Signature verification failed or invalid request
|
/// * `Err(LpError)` - Signature verification failed or invalid request
|
||||||
pub fn process_kkt_request(
|
pub fn process_kkt_request(&self, request_bytes: &[u8]) -> Result<LpMessage, LpError> {
|
||||||
&self,
|
|
||||||
request_bytes: &[u8],
|
|
||||||
responder_kem_pk: &EncapsulationKey,
|
|
||||||
) -> Result<LpMessage, LpError> {
|
|
||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
let mut kkt_state = self.kkt_state.lock();
|
let mut kkt_state = self.kkt_state.lock();
|
||||||
@@ -758,33 +761,46 @@ impl LpSession {
|
|||||||
) {
|
) {
|
||||||
Ok((session_secret, request_frame, remote_context)) => {
|
Ok((session_secret, request_frame, remote_context)) => {
|
||||||
match responder_ingest_message(&remote_context, None, None, &request_frame) {
|
match responder_ingest_message(&remote_context, None, None, &request_frame) {
|
||||||
Ok((mut context, _)) => match responder_process(
|
Ok((mut context, _)) => {
|
||||||
&mut context,
|
let local_kem_key: Arc<EncapsulationKey> =
|
||||||
request_frame.session_id(),
|
match self.local_kem_encapsulation_key(&context.ciphersuite().kem()) {
|
||||||
self.local_peer.ed25519().private_key(),
|
Some(key) => key.clone(),
|
||||||
responder_kem_pk,
|
None => {
|
||||||
) {
|
return Err(LpError::Internal(format!(
|
||||||
Ok(response_frame) => match encrypt_kkt_frame(
|
"KEM key for algorithm ({:?}) is not available.",
|
||||||
&mut rng,
|
&context.ciphersuite().kem()
|
||||||
&session_secret,
|
)));
|
||||||
&response_frame,
|
}
|
||||||
KKT_RESPONSE_AAD,
|
};
|
||||||
|
|
||||||
|
match responder_process(
|
||||||
|
&mut context,
|
||||||
|
request_frame.session_id(),
|
||||||
|
self.local_peer.ed25519().private_key(),
|
||||||
|
&local_kem_key,
|
||||||
) {
|
) {
|
||||||
Ok(response_bytes) => response_bytes,
|
Ok(response_frame) => match encrypt_kkt_frame(
|
||||||
|
&mut rng,
|
||||||
|
&session_secret,
|
||||||
|
&response_frame,
|
||||||
|
KKT_RESPONSE_AAD,
|
||||||
|
) {
|
||||||
|
Ok(response_bytes) => response_bytes,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(LpError::Internal(format!(
|
||||||
|
"KKT response encryption failure : {:?}",
|
||||||
|
e
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(LpError::Internal(format!(
|
return Err(LpError::Internal(format!(
|
||||||
"KKT response encryption failure : {:?}",
|
"KKT response generation failure : {:?}",
|
||||||
e
|
e
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
return Err(LpError::Internal(format!(
|
|
||||||
"KKT response generation failure : {:?}",
|
|
||||||
e
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(LpError::Internal(format!(
|
return Err(LpError::Internal(format!(
|
||||||
"KKT request handling failure: {:?}",
|
"KKT request handling failure: {:?}",
|
||||||
|
|||||||
@@ -360,22 +360,7 @@ impl LpStateMachine {
|
|||||||
// Packet message is already parsed, match on it directly
|
// Packet message is already parsed, match on it directly
|
||||||
match &packet.message {
|
match &packet.message {
|
||||||
LpMessage::KKTRequest(kkt_request) if !session.is_initiator() => {
|
LpMessage::KKTRequest(kkt_request) if !session.is_initiator() => {
|
||||||
// Responder processes KKT request
|
match session.process_kkt_request(&kkt_request.0) {
|
||||||
// Convert X25519 public key to KEM format for KKT response
|
|
||||||
use nym_kkt::ciphersuite::EncapsulationKey;
|
|
||||||
|
|
||||||
// Get local X25519 public key by deriving from private key
|
|
||||||
let local_x25519_public = session.local_x25519_public();
|
|
||||||
|
|
||||||
// Convert to libcrux KEM public key
|
|
||||||
match libcrux_kem::PublicKey::decode(
|
|
||||||
libcrux_kem::Algorithm::X25519,
|
|
||||||
local_x25519_public.as_bytes(),
|
|
||||||
) {
|
|
||||||
Ok(libcrux_public_key) => {
|
|
||||||
let responder_kem_pk = EncapsulationKey::X25519(libcrux_public_key);
|
|
||||||
|
|
||||||
match session.process_kkt_request(&kkt_request.0, &responder_kem_pk) {
|
|
||||||
Ok(kkt_response_message) => {
|
Ok(kkt_response_message) => {
|
||||||
match session.next_packet(kkt_response_message) {
|
match session.next_packet(kkt_response_message) {
|
||||||
Ok(response_packet) => {
|
Ok(response_packet) => {
|
||||||
@@ -396,14 +381,8 @@ impl LpStateMachine {
|
|||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let reason = format!("Failed to convert X25519 to KEM: {:?}", e);
|
|
||||||
let err = LpError::Internal(reason.clone());
|
|
||||||
result_action = Some(Err(err));
|
|
||||||
LpState::Closed { reason }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LpMessage::KKTResponse(kkt_response) if session.is_initiator() => {
|
LpMessage::KKTResponse(kkt_response) if session.is_initiator() => {
|
||||||
match session.process_kkt_response(&kkt_response.0) {
|
match session.process_kkt_response(&kkt_response.0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user