LP: x25519/ed22519 cleanup round (#6335)
* removed dependency on nymsphinx::* key types and removed needless copies of ed25519 keys * use more strongly types in ClientHelloData * explicitly use provided client's x25519 from ClientHelloData this requires adjusting LpSession constructor to take an additional key argument * allow large LpInput enum * clippy within tests * removed redundant type aliases for x25519 keys
This commit is contained in:
committed by
GitHub
parent
b6f234259c
commit
7c0babf35a
Generated
-4
@@ -6700,7 +6700,6 @@ dependencies = [
|
||||
"libcrux-sha3",
|
||||
"num_enum",
|
||||
"nym-crypto",
|
||||
"nym-sphinx",
|
||||
"rand 0.9.2",
|
||||
"thiserror 2.0.17",
|
||||
"rand_chacha 0.9.0",
|
||||
@@ -6722,7 +6721,6 @@ dependencies = [
|
||||
name = "nym-lp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bs58",
|
||||
"bytes",
|
||||
"chacha20poly1305",
|
||||
@@ -6735,7 +6733,6 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-kkt",
|
||||
"nym-lp-common",
|
||||
"nym-sphinx",
|
||||
"parking_lot",
|
||||
"rand 0.8.5",
|
||||
"rand 0.9.2",
|
||||
@@ -6746,7 +6743,6 @@ dependencies = [
|
||||
"thiserror 2.0.17",
|
||||
"tls_codec",
|
||||
"tracing",
|
||||
"utoipa",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ pub use serde_helpers::*;
|
||||
#[cfg(feature = "sphinx")]
|
||||
use nym_sphinx_types::{DESTINATION_ADDRESS_LENGTH, DestinationAddressBytes};
|
||||
|
||||
use crate::asymmetric::x25519;
|
||||
#[cfg(feature = "rand")]
|
||||
use rand::{CryptoRng, Rng, RngCore};
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -110,6 +111,18 @@ impl KeyPair {
|
||||
index: fake_index(pub_bytes),
|
||||
})
|
||||
}
|
||||
|
||||
/// Converts this Ed25519 keypair to an X25519 keypair for ECDH.
|
||||
///
|
||||
/// Uses the standard ed25519→x25519 conversion via SHA-512 hash and clamping.
|
||||
/// This is the same approach as libsodium's `crypto_sign_ed25519_sk_to_curve25519`.
|
||||
///
|
||||
/// # Returns
|
||||
/// The converted X25519 keypair
|
||||
pub fn to_x25519(&self) -> x25519::KeyPair {
|
||||
let private_key = self.private_key.to_x25519();
|
||||
x25519::KeyPair::from(private_key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reduces a byte slice into a u32 value by XOR-ing all its bytes into a 4-byte accumulator.
|
||||
@@ -136,6 +149,16 @@ impl From<PrivateKey> for KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(PrivateKey, PublicKey)> for KeyPair {
|
||||
fn from((private_key, public_key): (PrivateKey, PublicKey)) -> Self {
|
||||
KeyPair {
|
||||
private_key,
|
||||
public_key,
|
||||
index: fake_index(public_key.to_bytes().as_ref()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PemStorableKeyPair for KeyPair {
|
||||
type PrivatePemKey = PrivateKey;
|
||||
type PublicPemKey = PublicKey;
|
||||
@@ -185,14 +208,25 @@ impl PublicKey {
|
||||
}
|
||||
|
||||
/// Convert this public key to a byte array.
|
||||
#[inline]
|
||||
pub fn to_bytes(self) -> [u8; PUBLIC_KEY_LENGTH] {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
|
||||
/// View this public key as a byte array.
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_bytes(b: &[u8]) -> Result<Self, Ed25519RecoveryError> {
|
||||
Ok(PublicKey(ed25519_dalek::VerifyingKey::from_bytes(
|
||||
b.try_into()?,
|
||||
)?))
|
||||
Self::from_byte_array(b.try_into()?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_byte_array(b: &[u8; PUBLIC_KEY_LENGTH]) -> Result<Self, Ed25519RecoveryError> {
|
||||
Ok(PublicKey(ed25519_dalek::VerifyingKey::from_bytes(b)?))
|
||||
}
|
||||
|
||||
pub fn to_base58_string(self) -> String {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use base64::Engine;
|
||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
@@ -56,6 +57,15 @@ pub struct KeyPair {
|
||||
pub(crate) public_key: PublicKey,
|
||||
}
|
||||
|
||||
impl Debug for KeyPair {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("KeyPair")
|
||||
.field("private_key", &"<redacted>")
|
||||
.field("public_key", &self.public_key.to_base58_string())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyPair {
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
@@ -93,6 +103,15 @@ impl From<PrivateKey> for KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(PrivateKey, PublicKey)> for KeyPair {
|
||||
fn from((private_key, public_key): (PrivateKey, PublicKey)) -> Self {
|
||||
KeyPair {
|
||||
private_key,
|
||||
public_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PemStorableKeyPair for KeyPair {
|
||||
type PrivatePemKey = PrivateKey;
|
||||
type PublicPemKey = PublicKey;
|
||||
@@ -116,6 +135,13 @@ impl PemStorableKeyPair for KeyPair {
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
|
||||
pub struct PublicKey(x25519_dalek::PublicKey);
|
||||
|
||||
impl Deref for PublicKey {
|
||||
type Target = x25519_dalek::PublicKey;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PublicKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Display::fmt(&self.to_base58_string(), f)
|
||||
@@ -129,14 +155,17 @@ impl Debug for PublicKey {
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
#[inline]
|
||||
pub fn to_bytes(self) -> [u8; PUBLIC_KEY_SIZE] {
|
||||
*self.0.as_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_SIZE] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_bytes(b: &[u8]) -> Result<Self, KeyRecoveryError> {
|
||||
if b.len() != PUBLIC_KEY_SIZE {
|
||||
return Err(KeyRecoveryError::InvalidSizePublicKey {
|
||||
@@ -146,7 +175,12 @@ impl PublicKey {
|
||||
}
|
||||
let mut bytes = [0; PUBLIC_KEY_SIZE];
|
||||
bytes.copy_from_slice(&b[..PUBLIC_KEY_SIZE]);
|
||||
Ok(Self(x25519_dalek::PublicKey::from(bytes)))
|
||||
Ok(Self::from_byte_array(&bytes))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_byte_array(b: &[u8; PUBLIC_KEY_SIZE]) -> Self {
|
||||
Self(x25519_dalek::PublicKey::from(*b))
|
||||
}
|
||||
|
||||
pub fn to_base58_string(self) -> String {
|
||||
@@ -174,6 +208,12 @@ impl PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; PUBLIC_KEY_SIZE]> for PublicKey {
|
||||
fn from(bytes: [u8; PUBLIC_KEY_SIZE]) -> Self {
|
||||
PublicKey(x25519_dalek::PublicKey::from(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PublicKey {
|
||||
type Err = KeyRecoveryError;
|
||||
|
||||
@@ -296,6 +336,10 @@ impl PrivateKey {
|
||||
Ok(Self(x25519_dalek::StaticSecret::from(bytes)))
|
||||
}
|
||||
|
||||
pub fn from_secret(secret: [u8; PRIVATE_KEY_SIZE]) -> Self {
|
||||
Self(x25519_dalek::StaticSecret::from(secret))
|
||||
}
|
||||
|
||||
pub fn to_base58_string(&self) -> String {
|
||||
bs58::encode(&self.to_bytes()).into_string()
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ num_enum = { workspace = true }
|
||||
|
||||
# internal
|
||||
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"] }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
|
||||
libcrux-kem = { git = "https://github.com/cryspen/libcrux" }
|
||||
libcrux-sha3 = { git = "https://github.com/cryspen/libcrux" }
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
use blake3::Hasher;
|
||||
|
||||
use libcrux_chacha20poly1305::{NONCE_LEN, TAG_LEN};
|
||||
|
||||
use nym_sphinx::{PrivateKey, PublicKey};
|
||||
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use zeroize::Zeroize;
|
||||
// Copyright 2025-2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::kkt::KKT_INITIAL_FRAME_AAD;
|
||||
use crate::{
|
||||
ciphersuite::CURVE25519_KEY_LEN, context::KKTContext, error::KKTError, frame::KKTFrame,
|
||||
};
|
||||
use blake3::Hasher;
|
||||
use libcrux_chacha20poly1305::{NONCE_LEN, TAG_LEN};
|
||||
use nym_crypto::asymmetric::x25519;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[derive(Clone, Copy, Zeroize)]
|
||||
pub struct KKTSessionSecret([u8; 32]);
|
||||
|
||||
impl KKTSessionSecret {
|
||||
pub fn new(remote_public_key: &PublicKey) -> (Self, PublicKey) {
|
||||
// this doesn't use the newer rand crate
|
||||
let ephemeral_private_key = PrivateKey::random();
|
||||
let ephemeral_public_key = PublicKey::from(&ephemeral_private_key);
|
||||
pub fn new<R>(rng: &mut R, remote_public_key: &x25519::PublicKey) -> (Self, x25519::PublicKey)
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
let mut private_key_bytes = [0u8; x25519::PRIVATE_KEY_SIZE];
|
||||
rng.fill_bytes(&mut private_key_bytes);
|
||||
|
||||
let ephemeral_private_key = x25519::PrivateKey::from_secret(private_key_bytes);
|
||||
let ephemeral_public_key = x25519::PublicKey::from(&ephemeral_private_key);
|
||||
|
||||
(
|
||||
Self::derive(&ephemeral_private_key, remote_public_key),
|
||||
@@ -29,21 +33,22 @@ impl KKTSessionSecret {
|
||||
pub fn from_bytes(secret: [u8; 32]) -> Self {
|
||||
Self(secret)
|
||||
}
|
||||
pub fn try_derive(private_key: &PrivateKey, public_key: &[u8]) -> Result<Self, KKTError> {
|
||||
|
||||
fn try_derive(private_key: &x25519::PrivateKey, public_key: &[u8]) -> Result<Self, KKTError> {
|
||||
let mut pub_key: [u8; 32] = [0u8; 32];
|
||||
pub_key.copy_from_slice(&public_key[0..CURVE25519_KEY_LEN]);
|
||||
|
||||
// Todo: check validity of pk...
|
||||
let pk = PublicKey::from(pub_key);
|
||||
let pk = x25519::PublicKey::from(pub_key);
|
||||
Ok(Self::derive(private_key, &pk))
|
||||
}
|
||||
|
||||
pub fn derive(private_key: &PrivateKey, public_key: &PublicKey) -> Self {
|
||||
pub fn derive(private_key: &x25519::PrivateKey, public_key: &x25519::PublicKey) -> Self {
|
||||
let mut shared_secret = private_key.diffie_hellman(public_key);
|
||||
|
||||
let mut hasher = Hasher::new();
|
||||
|
||||
hasher.update(shared_secret.as_bytes());
|
||||
hasher.update(&shared_secret);
|
||||
shared_secret.zeroize();
|
||||
|
||||
Self(hasher.finalize().as_bytes().to_owned())
|
||||
@@ -55,13 +60,13 @@ impl KKTSessionSecret {
|
||||
|
||||
pub fn encrypt_initial_kkt_frame<R>(
|
||||
rng: &mut R,
|
||||
remote_public_key: &PublicKey,
|
||||
remote_public_key: &x25519::PublicKey,
|
||||
kkt_frame: &KKTFrame,
|
||||
) -> Result<(KKTSessionSecret, Vec<u8>), KKTError>
|
||||
where
|
||||
R: CryptoRng + RngCore,
|
||||
{
|
||||
let (session_secret_key, ephemeral_public_key) = KKTSessionSecret::new(remote_public_key);
|
||||
let (session_secret_key, ephemeral_public_key) = KKTSessionSecret::new(rng, remote_public_key);
|
||||
|
||||
let mut encrypted_frame =
|
||||
encrypt_kkt_frame(rng, &session_secret_key, kkt_frame, KKT_INITIAL_FRAME_AAD)?;
|
||||
@@ -76,7 +81,7 @@ where
|
||||
}
|
||||
|
||||
pub fn decrypt_initial_kkt_frame(
|
||||
responder_private_key: &PrivateKey,
|
||||
responder_private_key: &x25519::PrivateKey,
|
||||
encrypted_frame_bytes: &[u8],
|
||||
) -> Result<(KKTSessionSecret, KKTFrame, KKTContext), KKTError> {
|
||||
if encrypted_frame_bytes.len() < CURVE25519_KEY_LEN + TAG_LEN + NONCE_LEN {
|
||||
@@ -185,13 +190,14 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_keygen() {
|
||||
let responder_x25519_keypair = generate_keypair_x25519();
|
||||
let mut rng = rng();
|
||||
let responder_x25519_keypair = generate_keypair_x25519(&mut rng);
|
||||
|
||||
let (session_secret_key, ephemeral_public_key) =
|
||||
KKTSessionSecret::new(&responder_x25519_keypair.1);
|
||||
KKTSessionSecret::new(&mut rng, responder_x25519_keypair.public_key());
|
||||
|
||||
let shared_secret = KKTSessionSecret::try_derive(
|
||||
&responder_x25519_keypair.0,
|
||||
responder_x25519_keypair.private_key(),
|
||||
ephemeral_public_key.as_bytes().as_slice(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -1,42 +1,53 @@
|
||||
use crate::{
|
||||
ciphersuite::{HashFunction, KEM},
|
||||
error::KKTError,
|
||||
};
|
||||
use crate::ciphersuite::HashFunction;
|
||||
|
||||
use classic_mceliece_rust::keypair_boxed;
|
||||
use libcrux_kem::{Algorithm, key_gen};
|
||||
|
||||
use libcrux_sha3;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
pub fn generate_keypair_ed25519<R>(rng: &mut R, index: Option<u32>) -> ed25519::KeyPair
|
||||
|
||||
pub fn generate_keypair_ed25519<R>(
|
||||
rng: &mut R,
|
||||
index: Option<u32>,
|
||||
) -> nym_crypto::asymmetric::ed25519::KeyPair
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
let mut secret_initiator: [u8; 32] = [0u8; 32];
|
||||
rng.fill_bytes(&mut secret_initiator);
|
||||
ed25519::KeyPair::from_secret(secret_initiator, index.unwrap_or(0))
|
||||
nym_crypto::asymmetric::ed25519::KeyPair::from_secret(secret_initiator, index.unwrap_or(0))
|
||||
}
|
||||
|
||||
pub fn generate_keypair_x25519() -> (nym_sphinx::PrivateKey, nym_sphinx::PublicKey) {
|
||||
let private_key = nym_sphinx::PrivateKey::random();
|
||||
let public_key = nym_sphinx::PublicKey::from(&private_key);
|
||||
(private_key, public_key)
|
||||
pub fn generate_keypair_x25519<R>(rng: &mut R) -> nym_crypto::asymmetric::x25519::KeyPair
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
let mut secret_initiator: [u8; 32] = [0u8; 32];
|
||||
rng.fill_bytes(&mut secret_initiator);
|
||||
|
||||
let private_key = nym_crypto::asymmetric::x25519::PrivateKey::from_secret(secret_initiator);
|
||||
private_key.into()
|
||||
}
|
||||
|
||||
// (decapsulation_key, encapsulation_key)
|
||||
pub fn generate_keypair_libcrux<R>(
|
||||
rng: &mut R,
|
||||
kem: KEM,
|
||||
) -> Result<(libcrux_kem::PrivateKey, libcrux_kem::PublicKey), KKTError>
|
||||
kem: crate::ciphersuite::KEM,
|
||||
) -> Result<(libcrux_kem::PrivateKey, libcrux_kem::PublicKey), crate::error::KKTError>
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
match kem {
|
||||
KEM::MlKem768 => Ok(key_gen(Algorithm::MlKem768, rng)?),
|
||||
KEM::XWing => Ok(key_gen(Algorithm::XWingKemDraft06, rng)?),
|
||||
KEM::X25519 => Ok(key_gen(Algorithm::X25519, rng)?),
|
||||
_ => Err(KKTError::KEMError {
|
||||
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,
|
||||
)?),
|
||||
crate::ciphersuite::KEM::X25519 => {
|
||||
Ok(libcrux_kem::key_gen(libcrux_kem::Algorithm::X25519, rng)?)
|
||||
}
|
||||
_ => Err(crate::error::KKTError::KEMError {
|
||||
info: "Key Generation Error: Unsupported Libcrux Algorithm",
|
||||
}),
|
||||
}
|
||||
|
||||
+18
-11
@@ -8,7 +8,7 @@
|
||||
//!
|
||||
//! The underlying KKT protocol is implemented in the `session` module.
|
||||
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use crate::{
|
||||
@@ -60,7 +60,7 @@ pub fn request_kem_key<R: CryptoRng + RngCore>(
|
||||
rng: &mut R,
|
||||
ciphersuite: Ciphersuite,
|
||||
signing_key: &ed25519::PrivateKey,
|
||||
responder_dh_public_key: &nym_sphinx::PublicKey,
|
||||
responder_dh_public_key: &x25519::PublicKey,
|
||||
) -> Result<(KKTSessionSecret, KKTContext, Vec<u8>), KKTError> {
|
||||
// OneWay mode: client only wants responder's KEM key
|
||||
// None: client doesn't send their own KEM key
|
||||
@@ -161,7 +161,7 @@ pub fn handle_kem_request<'a, R>(
|
||||
encrypted_request_bytes: &[u8],
|
||||
initiator_vk: Option<&ed25519::PublicKey>,
|
||||
responder_signing_key: &ed25519::PrivateKey,
|
||||
responder_dh_private_key: &nym_sphinx::PrivateKey,
|
||||
responder_dh_private_key: &x25519::PrivateKey,
|
||||
responder_kem_key: &EncapsulationKey<'a>,
|
||||
) -> Result<Vec<u8>, KKTError>
|
||||
where
|
||||
@@ -200,6 +200,13 @@ mod tests {
|
||||
key_utils::{generate_keypair_libcrux, hash_encapsulation_key},
|
||||
};
|
||||
|
||||
fn random_x25519_key() -> x25519::PrivateKey {
|
||||
let mut bytes = [0u8; 32];
|
||||
let mut rng = rand::rng();
|
||||
rng.fill_bytes(&mut bytes);
|
||||
x25519::PrivateKey::from_secret(bytes)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kkt_wrappers_oneway_authenticated() {
|
||||
let mut rng = rand::rng();
|
||||
@@ -213,8 +220,8 @@ mod tests {
|
||||
rng.fill_bytes(&mut responder_secret);
|
||||
let ed25519_resp = ed25519::KeyPair::from_secret(responder_secret, 1);
|
||||
|
||||
let x25519_resp_priv = nym_sphinx::PrivateKey::random();
|
||||
let x25519_resp_pub = nym_sphinx::PublicKey::from(&x25519_resp_priv);
|
||||
let x25519_resp_priv = random_x25519_key();
|
||||
let x25519_resp_pub = x25519::PublicKey::from(&x25519_resp_priv);
|
||||
|
||||
// Generate responder's KEM keypair (X25519 for testing)
|
||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
@@ -282,8 +289,8 @@ mod tests {
|
||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
||||
|
||||
let x25519_resp_priv = nym_sphinx::PrivateKey::random();
|
||||
let x25519_resp_pub = nym_sphinx::PublicKey::from(&x25519_resp_priv);
|
||||
let x25519_resp_priv = random_x25519_key();
|
||||
let x25519_resp_pub = x25519::PublicKey::from(&x25519_resp_priv);
|
||||
|
||||
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||
KEM::X25519,
|
||||
@@ -343,8 +350,8 @@ mod tests {
|
||||
rng.fill_bytes(&mut responder_secret);
|
||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
||||
|
||||
let x25519_resp_priv = nym_sphinx::PrivateKey::random();
|
||||
let x25519_resp_pub = nym_sphinx::PublicKey::from(&x25519_resp_priv);
|
||||
let x25519_resp_priv = random_x25519_key();
|
||||
let x25519_resp_pub = x25519::PublicKey::from(&x25519_resp_priv);
|
||||
|
||||
// Different keypair for wrong signature
|
||||
let mut wrong_secret = [0u8; 32];
|
||||
@@ -396,8 +403,8 @@ mod tests {
|
||||
rng.fill_bytes(&mut responder_secret);
|
||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
||||
|
||||
let x25519_resp_priv = nym_sphinx::PrivateKey::random();
|
||||
let x25519_resp_pub = nym_sphinx::PublicKey::from(&x25519_resp_priv);
|
||||
let x25519_resp_priv = random_x25519_key();
|
||||
let x25519_resp_pub = x25519::PublicKey::from(&x25519_resp_priv);
|
||||
|
||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
||||
|
||||
+25
-13
@@ -246,7 +246,7 @@ mod test {
|
||||
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||
|
||||
// generate responder x25519 keys
|
||||
let responder_x25519_keypair = generate_keypair_x25519();
|
||||
let responder_x25519_keypair = generate_keypair_x25519(&mut rng);
|
||||
|
||||
for kem in [KEM::MlKem768, KEM::XWing, KEM::X25519, KEM::McEliece] {
|
||||
for hash_function in [
|
||||
@@ -315,14 +315,18 @@ mod test {
|
||||
|
||||
// encryption - initiator frame
|
||||
|
||||
let (i_session_secret, i_bytes) =
|
||||
encrypt_initial_kkt_frame(&mut rng, &responder_x25519_keypair.1, &i_frame)
|
||||
.unwrap();
|
||||
let (i_session_secret, i_bytes) = encrypt_initial_kkt_frame(
|
||||
&mut rng,
|
||||
responder_x25519_keypair.public_key(),
|
||||
&i_frame,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// decryption - initiator frame
|
||||
|
||||
let (r_session_secret, i_frame_r, i_context_r) =
|
||||
decrypt_initial_kkt_frame(&responder_x25519_keypair.0, &i_bytes).unwrap();
|
||||
decrypt_initial_kkt_frame(responder_x25519_keypair.private_key(), &i_bytes)
|
||||
.unwrap();
|
||||
|
||||
let (mut r_context, _) =
|
||||
responder_ingest_message(&i_context_r, None, None, &i_frame_r).unwrap();
|
||||
@@ -369,14 +373,18 @@ mod test {
|
||||
|
||||
// encryption - initiator frame
|
||||
|
||||
let (i_session_secret, i_bytes) =
|
||||
encrypt_initial_kkt_frame(&mut rng, &responder_x25519_keypair.1, &i_frame)
|
||||
.unwrap();
|
||||
let (i_session_secret, i_bytes) = encrypt_initial_kkt_frame(
|
||||
&mut rng,
|
||||
responder_x25519_keypair.public_key(),
|
||||
&i_frame,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// decryption - initiator frame
|
||||
|
||||
let (r_session_secret, i_frame_r, r_context) =
|
||||
decrypt_initial_kkt_frame(&responder_x25519_keypair.0, &i_bytes).unwrap();
|
||||
decrypt_initial_kkt_frame(responder_x25519_keypair.private_key(), &i_bytes)
|
||||
.unwrap();
|
||||
|
||||
let (mut r_context, r_obtained_key) = responder_ingest_message(
|
||||
&r_context,
|
||||
@@ -431,14 +439,18 @@ mod test {
|
||||
|
||||
// encryption - initiator frame
|
||||
|
||||
let (i_session_secret, i_bytes) =
|
||||
encrypt_initial_kkt_frame(&mut rng, &responder_x25519_keypair.1, &i_frame)
|
||||
.unwrap();
|
||||
let (i_session_secret, i_bytes) = encrypt_initial_kkt_frame(
|
||||
&mut rng,
|
||||
responder_x25519_keypair.public_key(),
|
||||
&i_frame,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// decryption - initiator frame
|
||||
|
||||
let (r_session_secret, i_frame_r, i_context_r) =
|
||||
decrypt_initial_kkt_frame(&responder_x25519_keypair.0, &i_bytes).unwrap();
|
||||
decrypt_initial_kkt_frame(responder_x25519_keypair.private_key(), &i_bytes)
|
||||
.unwrap();
|
||||
|
||||
let (mut r_context, r_obtained_key) = responder_ingest_message(
|
||||
&i_context_r,
|
||||
|
||||
@@ -13,9 +13,7 @@ serde = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
dashmap = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
ansi_term = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
utoipa = { workspace = true, features = ["macros", "non_strict_integers"] }
|
||||
rand = { workspace = true }
|
||||
# rand 0.9 for KKT integration (nym-kkt uses rand 0.9)
|
||||
rand09 = { package = "rand", version = "0.9.2" }
|
||||
@@ -23,7 +21,6 @@ rand09 = { package = "rand", version = "0.9.2" }
|
||||
nym-crypto = { path = "../crypto", features = ["hashing", "asymmetric"] }
|
||||
nym-kkt = { path = "../nym-kkt" }
|
||||
nym-lp-common = { path = "../nym-lp-common" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
|
||||
# libcrux dependencies for PSQ (Post-Quantum PSK derivation)
|
||||
libcrux-psq = { git = "https://github.com/cryspen/libcrux", features = [
|
||||
@@ -39,6 +36,7 @@ zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
rand_chacha = "0.3"
|
||||
nym-crypto = { path = "../crypto", features = ["rand"] }
|
||||
|
||||
|
||||
[[bench]]
|
||||
|
||||
+11
-41
@@ -321,6 +321,8 @@ mod tests {
|
||||
use crate::message::{EncryptedDataPayload, HandshakeData, LpMessage, MessageType};
|
||||
use crate::packet::{LpHeader, LpPacket, TRAILER_LEN};
|
||||
use bytes::BytesMut;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use rand::thread_rng;
|
||||
|
||||
// With unified format, outer header (receiver_idx + counter) is always first
|
||||
// and is the only cleartext portion for encrypted packets
|
||||
@@ -603,11 +605,13 @@ mod tests {
|
||||
fn test_serialize_parse_client_hello() {
|
||||
use crate::message::ClientHelloData;
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let valid_ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||
let mut dst = BytesMut::new();
|
||||
|
||||
// Create ClientHelloData
|
||||
let client_key = [42u8; 32];
|
||||
let client_ed25519_key = [43u8; 32];
|
||||
let client_key = x25519::PublicKey::from_byte_array(&[42u8; 32]);
|
||||
let client_ed25519_key = *valid_ed25519.public_key();
|
||||
let salt = [99u8; 32];
|
||||
let hello_data = ClientHelloData {
|
||||
receiver_index: 12345,
|
||||
@@ -661,8 +665,11 @@ mod tests {
|
||||
.as_secs();
|
||||
|
||||
// Create ClientHelloData with fresh salt
|
||||
let client_key = [7u8; 32];
|
||||
let client_ed25519_key = [8u8; 32];
|
||||
let mut rng = thread_rng();
|
||||
let valid_ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||
|
||||
let client_key = x25519::PublicKey::from_byte_array(&[7u8; 32]);
|
||||
let client_ed25519_key = *valid_ed25519.public_key();
|
||||
let hello_data =
|
||||
ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
||||
|
||||
@@ -749,43 +756,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_client_hello_different_protocol_versions() {
|
||||
use crate::message::ClientHelloData;
|
||||
|
||||
for version in [0u8, 1, 2, 255] {
|
||||
let mut dst = BytesMut::new();
|
||||
|
||||
let hello_data = ClientHelloData {
|
||||
receiver_index: version as u32,
|
||||
client_lp_public_key: [version; 32],
|
||||
client_ed25519_public_key: [version.wrapping_add(2); 32],
|
||||
salt: [version.wrapping_add(1); 32],
|
||||
};
|
||||
|
||||
let packet = LpPacket {
|
||||
header: LpHeader {
|
||||
protocol_version: 1,
|
||||
reserved: [0u8; 3],
|
||||
receiver_idx: version as u32,
|
||||
counter: version as u64,
|
||||
},
|
||||
message: LpMessage::ClientHello(hello_data.clone()),
|
||||
trailer: [version; TRAILER_LEN],
|
||||
};
|
||||
|
||||
serialize_lp_packet(&packet, &mut dst, None).unwrap();
|
||||
let decoded = parse_lp_packet(&dst, None).unwrap();
|
||||
|
||||
match decoded.message {
|
||||
LpMessage::ClientHello(decoded_data) => {
|
||||
assert_eq!(decoded_data.client_lp_public_key, [version; 32]);
|
||||
}
|
||||
_ => panic!("Expected ClientHello message for version {}", version),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_forward_packet_encode_decode_roundtrip() {
|
||||
let mut dst = BytesMut::new();
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
use nym_sphinx::{PrivateKey as SphinxPrivateKey, PublicKey as SphinxPublicKey};
|
||||
use serde::Serialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::LpError;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PrivateKey(SphinxPrivateKey);
|
||||
|
||||
impl fmt::Debug for PrivateKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("PrivateKey").field(&"[REDACTED]").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for PrivateKey {
|
||||
type Target = SphinxPrivateKey;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PrivateKey {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
pub fn new() -> Self {
|
||||
let private_key = SphinxPrivateKey::random();
|
||||
Self(private_key)
|
||||
}
|
||||
|
||||
pub fn to_base58_string(&self) -> String {
|
||||
bs58::encode(self.0.to_bytes()).into_string()
|
||||
}
|
||||
|
||||
pub fn from_base58_string(s: &str) -> Result<Self, LpError> {
|
||||
let bytes: [u8; 32] = bs58::decode(s).into_vec()?.try_into().unwrap();
|
||||
Ok(PrivateKey(SphinxPrivateKey::from(bytes)))
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8; 32]) -> Self {
|
||||
PrivateKey(SphinxPrivateKey::from(*bytes))
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
let public_key = SphinxPublicKey::from(&self.0);
|
||||
PublicKey(public_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PublicKey(SphinxPublicKey);
|
||||
|
||||
impl fmt::Debug for PublicKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("PublicKey")
|
||||
.field(&self.to_base58_string())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for PublicKey {
|
||||
type Target = SphinxPublicKey;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
pub fn to_base58_string(&self) -> String {
|
||||
bs58::encode(self.0.as_bytes()).into_string()
|
||||
}
|
||||
|
||||
pub fn from_base58_string(s: &str) -> Result<Self, LpError> {
|
||||
let bytes: [u8; 32] = bs58::decode(s).into_vec()?.try_into().unwrap();
|
||||
Ok(PublicKey(SphinxPublicKey::from(bytes)))
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, LpError> {
|
||||
Ok(PublicKey(SphinxPublicKey::from(*bytes)))
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PublicKey {
|
||||
fn default() -> Self {
|
||||
let private_key = PrivateKey::default();
|
||||
private_key.public_key()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Keypair {
|
||||
private_key: PrivateKey,
|
||||
public_key: PublicKey,
|
||||
}
|
||||
|
||||
impl Default for Keypair {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
pub fn new() -> Self {
|
||||
let private_key = PrivateKey::default();
|
||||
let public_key = private_key.public_key();
|
||||
Self {
|
||||
private_key,
|
||||
public_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_private_key(private_key: PrivateKey) -> Self {
|
||||
let public_key = private_key.public_key();
|
||||
Self {
|
||||
private_key,
|
||||
public_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_keys(private_key: PrivateKey, public_key: PublicKey) -> Self {
|
||||
Self {
|
||||
private_key,
|
||||
public_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn private_key(&self) -> &PrivateKey {
|
||||
&self.private_key
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> &PublicKey {
|
||||
&self.public_key
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeypairReadable> for Keypair {
|
||||
fn from(keypair: KeypairReadable) -> Self {
|
||||
Self {
|
||||
private_key: PrivateKey::from_base58_string(&keypair.private).unwrap(),
|
||||
public_key: PublicKey::from_base58_string(&keypair.public).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Keypair> for KeypairReadable {
|
||||
fn from(keypair: &Keypair) -> Self {
|
||||
Self {
|
||||
private: keypair.private_key.to_base58_string(),
|
||||
public: keypair.public_key.to_base58_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FromStr for PrivateKey {
|
||||
type Err = LpError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
PrivateKey::from_base58_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PrivateKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.to_base58_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PublicKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.to_base58_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, serde::Deserialize, Clone, ToSchema, Debug)]
|
||||
pub struct KeypairReadable {
|
||||
private: String,
|
||||
public: String,
|
||||
}
|
||||
|
||||
impl KeypairReadable {
|
||||
pub fn private_key(&self) -> Result<PrivateKey, LpError> {
|
||||
PrivateKey::from_base58_string(&self.private)
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> Result<PublicKey, LpError> {
|
||||
PublicKey::from_base58_string(&self.public)
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
use crate::LpError;
|
||||
use crate::message::{KKTRequestData, KKTResponseData};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_kkt::ciphersuite::{Ciphersuite, EncapsulationKey};
|
||||
use nym_kkt::context::KKTContext;
|
||||
use nym_kkt::encryption::KKTSessionSecret;
|
||||
@@ -90,7 +90,7 @@ use nym_kkt::kkt::{handle_kem_request, request_kem_key, validate_kem_response};
|
||||
pub fn create_request(
|
||||
ciphersuite: Ciphersuite,
|
||||
signing_key: &ed25519::PrivateKey,
|
||||
responder_dh_public_key: &nym_sphinx::PublicKey,
|
||||
responder_dh_public_key: &x25519::PublicKey,
|
||||
) -> Result<(KKTSessionSecret, KKTContext, KKTRequestData), LpError> {
|
||||
// Note: Uses rand 0.9's thread_rng() to match nym-kkt's rand version
|
||||
let mut rng = rand09::rng();
|
||||
@@ -163,7 +163,7 @@ pub fn handle_request<'a>(
|
||||
request_data: &KKTRequestData,
|
||||
initiator_vk: Option<&ed25519::PublicKey>,
|
||||
responder_signing_key: &ed25519::PrivateKey,
|
||||
responder_dh_private_key: &nym_sphinx::PrivateKey,
|
||||
responder_dh_private_key: &x25519::PrivateKey,
|
||||
responder_kem_key: &EncapsulationKey<'a>,
|
||||
) -> Result<KKTResponseData, LpError> {
|
||||
let mut rng = rand09::rng();
|
||||
@@ -199,7 +199,7 @@ mod tests {
|
||||
let initiator_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(0));
|
||||
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||
|
||||
let (responder_x25519_sk, responder_x25519_pk) = generate_keypair_x25519();
|
||||
let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||
|
||||
// Generate responder's KEM keypair (X25519 for testing)
|
||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
@@ -225,7 +225,7 @@ mod tests {
|
||||
let (session_secret, context, request_data) = create_request(
|
||||
ciphersuite,
|
||||
initiator_ed25519_keypair.private_key(),
|
||||
&responder_x25519_pk,
|
||||
responder_x25519.public_key(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -234,7 +234,7 @@ mod tests {
|
||||
&request_data,
|
||||
Some(initiator_ed25519_keypair.public_key()),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_x25519_sk,
|
||||
responder_x25519.private_key(),
|
||||
&responder_kem_key,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -262,7 +262,7 @@ mod tests {
|
||||
|
||||
// let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||
|
||||
// let (responder_x25519_sk, responder_x25519_pk) = generate_keypair_x25519();
|
||||
// let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||
|
||||
// let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
// let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
||||
@@ -317,7 +317,7 @@ mod tests {
|
||||
let initiator_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(0));
|
||||
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||
|
||||
let (responder_x25519_sk, responder_x25519_pk) = generate_keypair_x25519();
|
||||
let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||
|
||||
// Different keypair for wrong signature
|
||||
let mut wrong_secret = [0u8; 32];
|
||||
@@ -338,7 +338,7 @@ mod tests {
|
||||
let (_session_secret, _context, request_data) = create_request(
|
||||
ciphersuite,
|
||||
initiator_ed25519_keypair.private_key(),
|
||||
&responder_x25519_pk,
|
||||
responder_x25519.public_key(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -347,7 +347,7 @@ mod tests {
|
||||
&request_data,
|
||||
Some(wrong_keypair.public_key()), // Wrong key!
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_x25519_sk,
|
||||
responder_x25519.private_key(),
|
||||
&responder_kem_key,
|
||||
);
|
||||
|
||||
@@ -368,7 +368,7 @@ mod tests {
|
||||
let initiator_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(0));
|
||||
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||
|
||||
let (responder_x25519_sk, responder_x25519_pk) = generate_keypair_x25519();
|
||||
let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||
|
||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
||||
@@ -387,7 +387,7 @@ mod tests {
|
||||
let (session_secret, context, request_data) = create_request(
|
||||
ciphersuite,
|
||||
initiator_ed25519_keypair.private_key(),
|
||||
&responder_x25519_pk,
|
||||
responder_x25519.public_key(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -395,7 +395,7 @@ mod tests {
|
||||
&request_data,
|
||||
Some(initiator_ed25519_keypair.public_key()),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_x25519_sk,
|
||||
responder_x25519.private_key(),
|
||||
&responder_kem_key,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -426,7 +426,7 @@ mod tests {
|
||||
rng.fill_bytes(&mut responder_secret);
|
||||
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||
|
||||
let (responder_x25519_sk, _responder_x25519_pk) = generate_keypair_x25519();
|
||||
let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||
|
||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
||||
@@ -438,7 +438,7 @@ mod tests {
|
||||
&malformed_request,
|
||||
None,
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_x25519_sk,
|
||||
responder_x25519.private_key(),
|
||||
&responder_kem_key,
|
||||
);
|
||||
|
||||
@@ -459,7 +459,7 @@ mod tests {
|
||||
let initiator_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(0));
|
||||
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||
|
||||
let (_responder_x25519_sk, responder_x25519_pk) = generate_keypair_x25519();
|
||||
let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||
|
||||
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||
KEM::X25519,
|
||||
@@ -472,7 +472,7 @@ mod tests {
|
||||
let (session_secret, context, _request_data) = create_request(
|
||||
ciphersuite,
|
||||
initiator_ed25519_keypair.private_key(),
|
||||
&responder_x25519_pk,
|
||||
responder_x25519.public_key(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
+23
-25
@@ -4,7 +4,6 @@
|
||||
pub mod codec;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod keypair;
|
||||
pub mod kkt_orchestrator;
|
||||
pub mod message;
|
||||
pub mod noise_protocol;
|
||||
@@ -30,12 +29,14 @@ pub const NOISE_PSK_INDEX: u8 = 3;
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
||||
use crate::keypair::Keypair;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use std::sync::Arc;
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
// X25519 keypairs for Noise protocol
|
||||
let keypair_1 = Keypair::default();
|
||||
let keypair_2 = Keypair::default();
|
||||
let keypair_1 = Arc::new(x25519::KeyPair::new(&mut rng));
|
||||
let keypair_2 = Arc::new(x25519::KeyPair::new(&mut rng));
|
||||
|
||||
// Use a fixed receiver_index for deterministic tests
|
||||
let receiver_index: u32 = 12345;
|
||||
@@ -44,6 +45,8 @@ pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
||||
let ed25519_keypair_1 = ed25519::KeyPair::from_secret([1u8; 32], 0);
|
||||
let ed25519_keypair_2 = ed25519::KeyPair::from_secret([2u8; 32], 1);
|
||||
|
||||
let ed25519_keypair1_pubkey = *ed25519_keypair_1.public_key();
|
||||
|
||||
// Use consistent salt for deterministic tests
|
||||
let salt = [1u8; 32];
|
||||
|
||||
@@ -52,11 +55,8 @@ pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
||||
let initiator_session = LpSession::new(
|
||||
receiver_index,
|
||||
true,
|
||||
(
|
||||
ed25519_keypair_1.private_key(),
|
||||
ed25519_keypair_1.public_key(),
|
||||
),
|
||||
keypair_1.private_key(),
|
||||
Arc::new(ed25519_keypair_1),
|
||||
keypair_1.clone(),
|
||||
ed25519_keypair_2.public_key(),
|
||||
keypair_2.public_key(),
|
||||
&salt,
|
||||
@@ -66,12 +66,9 @@ pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
||||
let responder_session = LpSession::new(
|
||||
receiver_index,
|
||||
false,
|
||||
(
|
||||
ed25519_keypair_2.private_key(),
|
||||
ed25519_keypair_2.public_key(),
|
||||
),
|
||||
keypair_2.private_key(),
|
||||
ed25519_keypair_1.public_key(),
|
||||
Arc::new(ed25519_keypair_2),
|
||||
keypair_2.clone(),
|
||||
&ed25519_keypair1_pubkey,
|
||||
keypair_1.public_key(),
|
||||
&salt,
|
||||
)
|
||||
@@ -87,6 +84,7 @@ mod tests {
|
||||
use crate::session_manager::SessionManager;
|
||||
use crate::{LpError, sessions_for_tests};
|
||||
use bytes::BytesMut;
|
||||
use std::sync::Arc;
|
||||
|
||||
// Import the new standalone functions
|
||||
use crate::codec::{parse_lp_packet, serialize_lp_packet};
|
||||
@@ -202,6 +200,10 @@ mod tests {
|
||||
let ed25519_keypair_local = ed25519::KeyPair::from_secret([8u8; 32], 0);
|
||||
let ed25519_keypair_remote = ed25519::KeyPair::from_secret([9u8; 32], 1);
|
||||
|
||||
let ed25519_keypair_local_pubkey = *ed25519_keypair_local.public_key();
|
||||
let x25519_keypair_local_pubkey = ed25519_keypair_local_pubkey.to_x25519().unwrap();
|
||||
let x25519_keypair_remote_pubkey = ed25519_keypair_remote.public_key().to_x25519().unwrap();
|
||||
|
||||
// Use fixed receiver_index for deterministic test
|
||||
let receiver_index: u32 = 54321;
|
||||
|
||||
@@ -212,11 +214,9 @@ mod tests {
|
||||
let _ = local_manager
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(
|
||||
ed25519_keypair_local.private_key(),
|
||||
ed25519_keypair_local.public_key(),
|
||||
),
|
||||
Arc::new(ed25519_keypair_local),
|
||||
ed25519_keypair_remote.public_key(),
|
||||
&x25519_keypair_remote_pubkey,
|
||||
true,
|
||||
&salt,
|
||||
)
|
||||
@@ -225,11 +225,9 @@ mod tests {
|
||||
let _ = remote_manager
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(
|
||||
ed25519_keypair_remote.private_key(),
|
||||
ed25519_keypair_remote.public_key(),
|
||||
),
|
||||
ed25519_keypair_local.public_key(),
|
||||
Arc::new(ed25519_keypair_remote),
|
||||
&ed25519_keypair_local_pubkey,
|
||||
&x25519_keypair_local_pubkey,
|
||||
false,
|
||||
&salt,
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::{BOOTSTRAP_RECEIVER_IDX, LpError};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
@@ -14,9 +15,9 @@ pub struct ClientHelloData {
|
||||
/// Auto-generated randomly by the client
|
||||
pub receiver_index: u32,
|
||||
/// Client's LP x25519 public key (32 bytes) - derived from Ed25519 key
|
||||
pub client_lp_public_key: [u8; 32],
|
||||
pub client_lp_public_key: x25519::PublicKey,
|
||||
/// Client's Ed25519 public key (32 bytes) - for PSQ authentication
|
||||
pub client_ed25519_public_key: [u8; 32],
|
||||
pub client_ed25519_public_key: ed25519::PublicKey,
|
||||
/// Salt for PSK derivation (32 bytes: 8-byte timestamp + 24-byte nonce)
|
||||
pub salt: [u8; 32],
|
||||
}
|
||||
@@ -46,8 +47,8 @@ impl ClientHelloData {
|
||||
/// * `client_lp_public_key` - Client's x25519 public key (derived from Ed25519)
|
||||
/// * `client_ed25519_public_key` - Client's Ed25519 public key (for PSQ authentication)
|
||||
pub fn new_with_fresh_salt(
|
||||
client_lp_public_key: [u8; 32],
|
||||
client_ed25519_public_key: [u8; 32],
|
||||
client_lp_public_key: x25519::PublicKey,
|
||||
client_ed25519_public_key: ed25519::PublicKey,
|
||||
timestamp: u64,
|
||||
) -> Self {
|
||||
// Generate salt: timestamp + nonce
|
||||
@@ -80,8 +81,8 @@ impl ClientHelloData {
|
||||
|
||||
pub fn encode(&self, dst: &mut BytesMut) {
|
||||
dst.put_u32_le(self.receiver_index);
|
||||
dst.put_slice(&self.client_lp_public_key);
|
||||
dst.put_slice(&self.client_ed25519_public_key);
|
||||
dst.put_slice(self.client_lp_public_key.as_bytes());
|
||||
dst.put_slice(self.client_ed25519_public_key.as_bytes());
|
||||
dst.put_slice(&self.salt);
|
||||
}
|
||||
|
||||
@@ -96,10 +97,15 @@ impl ClientHelloData {
|
||||
|
||||
// SAFETY: we checked for valid byte lengths
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let client_lp_public_key_bytes = b[4..36].try_into().unwrap();
|
||||
let client_ed25519_public_key_bytes = b[36..68].try_into().unwrap();
|
||||
|
||||
Ok(ClientHelloData {
|
||||
receiver_index: u32::from_le_bytes([b[0], b[1], b[2], b[3]]),
|
||||
client_lp_public_key: b[4..36].try_into().unwrap(),
|
||||
client_ed25519_public_key: b[36..68].try_into().unwrap(),
|
||||
client_lp_public_key: x25519::PublicKey::from_byte_array(client_lp_public_key_bytes),
|
||||
client_ed25519_public_key: ed25519::PublicKey::from_byte_array(
|
||||
client_ed25519_public_key_bytes,
|
||||
)?,
|
||||
salt: b[68..].try_into().unwrap(),
|
||||
})
|
||||
}
|
||||
@@ -635,8 +641,13 @@ mod tests {
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("System time before UNIX epoch")
|
||||
.as_secs();
|
||||
let client_key = [1u8; 32];
|
||||
let client_ed25519_key = [2u8; 32];
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||
let x25519 = ed25519.to_x25519();
|
||||
|
||||
let client_key = *x25519.public_key();
|
||||
let client_ed25519_key = *ed25519.public_key();
|
||||
let hello1 =
|
||||
ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
||||
let hello2 =
|
||||
@@ -657,8 +668,12 @@ mod tests {
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("System time before UNIX epoch")
|
||||
.as_secs();
|
||||
let client_key = [2u8; 32];
|
||||
let client_ed25519_key = [3u8; 32];
|
||||
let mut rng = rand::thread_rng();
|
||||
let ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||
let x25519 = ed25519.to_x25519();
|
||||
|
||||
let client_key = *x25519.public_key();
|
||||
let client_ed25519_key = *ed25519.public_key();
|
||||
let hello = ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
||||
|
||||
let timestamp = hello.extract_timestamp();
|
||||
@@ -677,8 +692,12 @@ mod tests {
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("System time before UNIX epoch")
|
||||
.as_secs();
|
||||
let client_key = [3u8; 32];
|
||||
let client_ed25519_key = [4u8; 32];
|
||||
let mut rng = rand::thread_rng();
|
||||
let ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||
let x25519 = ed25519.to_x25519();
|
||||
|
||||
let client_key = *x25519.public_key();
|
||||
let client_ed25519_key = *ed25519.public_key();
|
||||
let hello = ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
||||
|
||||
// First 8 bytes should be non-zero timestamp
|
||||
|
||||
+35
-32
@@ -47,12 +47,11 @@
|
||||
//! - **No cleanup needed**: No state was mutated
|
||||
|
||||
use crate::LpError;
|
||||
use crate::keypair::{PrivateKey, PublicKey};
|
||||
use libcrux_psq::v1::cred::{Authenticator, Ed25519};
|
||||
use libcrux_psq::v1::impls::X25519 as PsqX25519;
|
||||
use libcrux_psq::v1::psk_registration::{Initiator, InitiatorMsg, Responder};
|
||||
use libcrux_psq::v1::traits::{Ciphertext as PsqCiphertext, PSQ};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey};
|
||||
use std::time::Duration;
|
||||
use tls_codec::{Deserialize as TlsDeserializeTrait, Serialize as TlsSerializeTrait};
|
||||
@@ -137,8 +136,8 @@ pub struct PsqResponderResult {
|
||||
/// // Send ciphertext to gateway
|
||||
/// ```
|
||||
pub fn derive_psk_with_psq_initiator(
|
||||
local_x25519_private: &PrivateKey,
|
||||
remote_x25519_public: &PublicKey,
|
||||
local_x25519_private: &x25519::PrivateKey,
|
||||
remote_x25519_public: &x25519::PublicKey,
|
||||
remote_kem_public: &EncapsulationKey,
|
||||
salt: &[u8; 32],
|
||||
) -> Result<([u8; 32], Vec<u8>), LpError> {
|
||||
@@ -168,7 +167,7 @@ pub fn derive_psk_with_psq_initiator(
|
||||
|
||||
// Step 3: Combine ECDH + PSQ via Blake3 KDF
|
||||
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
||||
combined.extend_from_slice(ecdh_secret.as_bytes());
|
||||
combined.extend_from_slice(&ecdh_secret);
|
||||
combined.extend_from_slice(&psq_psk); // psq_psk is [u8; 32], need &
|
||||
combined.extend_from_slice(salt);
|
||||
|
||||
@@ -220,8 +219,8 @@ pub fn derive_psk_with_psq_initiator(
|
||||
/// )?;
|
||||
/// ```
|
||||
pub fn derive_psk_with_psq_responder(
|
||||
local_x25519_private: &PrivateKey,
|
||||
remote_x25519_public: &PublicKey,
|
||||
local_x25519_private: &x25519::PrivateKey,
|
||||
remote_x25519_public: &x25519::PublicKey,
|
||||
local_kem_keypair: (&DecapsulationKey, &EncapsulationKey),
|
||||
ciphertext: &[u8],
|
||||
salt: &[u8; 32],
|
||||
@@ -249,7 +248,7 @@ pub fn derive_psk_with_psq_responder(
|
||||
|
||||
// Step 5: Combine ECDH + PSQ via Blake3 KDF (same formula as initiator)
|
||||
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
||||
combined.extend_from_slice(ecdh_secret.as_bytes());
|
||||
combined.extend_from_slice(&ecdh_secret);
|
||||
combined.extend_from_slice(&psq_psk); // psq_psk is [u8; 32], need &
|
||||
combined.extend_from_slice(salt);
|
||||
|
||||
@@ -280,8 +279,8 @@ pub fn derive_psk_with_psq_responder(
|
||||
/// # Returns
|
||||
/// `PsqInitiatorResult` containing PSK, payload, and raw PQ shared secret
|
||||
pub fn psq_initiator_create_message(
|
||||
local_x25519_private: &PrivateKey,
|
||||
remote_x25519_public: &PublicKey,
|
||||
local_x25519_private: &x25519::PrivateKey,
|
||||
remote_x25519_public: &x25519::PublicKey,
|
||||
remote_kem_public: &EncapsulationKey,
|
||||
client_ed25519_sk: &ed25519::PrivateKey,
|
||||
client_ed25519_pk: &ed25519::PublicKey,
|
||||
@@ -335,7 +334,7 @@ pub fn psq_initiator_create_message(
|
||||
|
||||
// Step 3: Combine ECDH + PSQ via Blake3 KDF
|
||||
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
||||
combined.extend_from_slice(ecdh_secret.as_bytes());
|
||||
combined.extend_from_slice(&ecdh_secret);
|
||||
combined.extend_from_slice(psq_psk); // psq_psk is already a &[u8; 32]
|
||||
combined.extend_from_slice(salt);
|
||||
|
||||
@@ -375,8 +374,8 @@ pub fn psq_initiator_create_message(
|
||||
/// # Returns
|
||||
/// `PsqResponderResult` containing PSK, PSK handle, and raw PQ shared secret
|
||||
pub fn psq_responder_process_message(
|
||||
local_x25519_private: &PrivateKey,
|
||||
remote_x25519_public: &PublicKey,
|
||||
local_x25519_private: &x25519::PrivateKey,
|
||||
remote_x25519_public: &x25519::PublicKey,
|
||||
local_kem_keypair: (&DecapsulationKey, &EncapsulationKey),
|
||||
initiator_ed25519_pk: &ed25519::PublicKey,
|
||||
psq_payload: &[u8],
|
||||
@@ -444,7 +443,7 @@ pub fn psq_responder_process_message(
|
||||
|
||||
// Step 6: Combine ECDH + PSQ via Blake3 KDF (same formula as initiator)
|
||||
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
||||
combined.extend_from_slice(ecdh_secret.as_bytes());
|
||||
combined.extend_from_slice(&ecdh_secret);
|
||||
combined.extend_from_slice(&psq_psk); // psq_psk is [u8; 32], need &
|
||||
combined.extend_from_slice(salt);
|
||||
|
||||
@@ -493,12 +492,16 @@ pub fn derive_subsession_psk(pq_shared_secret: &[u8; 32], subsession_index: u64)
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::keypair::Keypair;
|
||||
use rand::thread_rng;
|
||||
|
||||
fn generate_x25519_keypair() -> x25519::KeyPair {
|
||||
x25519::KeyPair::new(&mut thread_rng())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_psk_derivation_is_symmetric() {
|
||||
let keypair_1 = Keypair::default();
|
||||
let keypair_2 = Keypair::default();
|
||||
let keypair_1 = generate_x25519_keypair();
|
||||
let keypair_2 = generate_x25519_keypair();
|
||||
let salt = [2u8; 32];
|
||||
|
||||
let mut rng = &mut rand09::rng();
|
||||
@@ -533,8 +536,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_different_salts_produce_different_psks() {
|
||||
let keypair_1 = Keypair::default();
|
||||
let keypair_2 = Keypair::default();
|
||||
let keypair_1 = generate_x25519_keypair();
|
||||
let keypair_2 = generate_x25519_keypair();
|
||||
|
||||
let salt1 = [1u8; 32];
|
||||
let salt2 = [2u8; 32];
|
||||
@@ -562,9 +565,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_different_keys_produce_different_psks() {
|
||||
let keypair_1 = Keypair::default();
|
||||
let keypair_2 = Keypair::default();
|
||||
let keypair_3 = Keypair::default();
|
||||
let keypair_1 = generate_x25519_keypair();
|
||||
let keypair_2 = generate_x25519_keypair();
|
||||
let keypair_3 = generate_x25519_keypair();
|
||||
let salt = [3u8; 32];
|
||||
|
||||
let mut rng = &mut rand09::rng();
|
||||
@@ -601,8 +604,8 @@ mod tests {
|
||||
let mut rng = rand09::rng();
|
||||
|
||||
// Generate X25519 keypairs for Noise
|
||||
let client_keypair = Keypair::default();
|
||||
let gateway_keypair = Keypair::default();
|
||||
let client_keypair = generate_x25519_keypair();
|
||||
let gateway_keypair = generate_x25519_keypair();
|
||||
|
||||
// Generate KEM keypair for PSQ
|
||||
let (kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
@@ -659,8 +662,8 @@ mod tests {
|
||||
let mut rng = rand09::rng();
|
||||
|
||||
// Generate X25519 keypairs for Noise
|
||||
let client_keypair = Keypair::default();
|
||||
let gateway_keypair = Keypair::default();
|
||||
let client_keypair = generate_x25519_keypair();
|
||||
let gateway_keypair = generate_x25519_keypair();
|
||||
|
||||
// Generate KEM keypair for PSQ
|
||||
let (kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
@@ -698,8 +701,8 @@ mod tests {
|
||||
fn test_different_kem_keys_different_psk() {
|
||||
let mut rng = rand09::rng();
|
||||
|
||||
let client_keypair = Keypair::default();
|
||||
let gateway_keypair = Keypair::default();
|
||||
let client_keypair = generate_x25519_keypair();
|
||||
let gateway_keypair = generate_x25519_keypair();
|
||||
|
||||
// Two different KEM keypairs
|
||||
let (_, kem_pk1) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
@@ -736,8 +739,8 @@ mod tests {
|
||||
fn test_psq_psk_output_length() {
|
||||
let mut rng = rand09::rng();
|
||||
|
||||
let client_keypair = Keypair::default();
|
||||
let gateway_keypair = Keypair::default();
|
||||
let client_keypair = generate_x25519_keypair();
|
||||
let gateway_keypair = generate_x25519_keypair();
|
||||
|
||||
let (_, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
let enc_key = EncapsulationKey::X25519(kem_pk);
|
||||
@@ -759,8 +762,8 @@ mod tests {
|
||||
fn test_psq_different_salts_different_psks() {
|
||||
let mut rng = rand09::rng();
|
||||
|
||||
let client_keypair = Keypair::default();
|
||||
let gateway_keypair = Keypair::default();
|
||||
let client_keypair = generate_x25519_keypair();
|
||||
let gateway_keypair = generate_x25519_keypair();
|
||||
|
||||
let (_, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||
let enc_key = EncapsulationKey::X25519(kem_pk);
|
||||
|
||||
+98
-130
@@ -7,7 +7,6 @@
|
||||
//! and Noise protocol state handling.
|
||||
|
||||
use crate::codec::OuterAeadKey;
|
||||
use crate::keypair::{PrivateKey, PublicKey};
|
||||
use crate::message::{EncryptedDataPayload, HandshakeData};
|
||||
use crate::noise_protocol::{NoiseError, NoiseProtocol, ReadResult};
|
||||
use crate::packet::LpHeader;
|
||||
@@ -16,12 +15,13 @@ use crate::psk::{
|
||||
};
|
||||
use crate::replay::ReceivingKeyCounterValidator;
|
||||
use crate::{LpError, LpMessage, LpPacket};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey};
|
||||
use nym_kkt::encryption::KKTSessionSecret;
|
||||
use nym_kkt::kkt::decrypt_kkt_response_frame;
|
||||
use parking_lot::Mutex;
|
||||
use snow::Builder;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
@@ -182,20 +182,17 @@ pub struct LpSession {
|
||||
psk_injected: AtomicBool,
|
||||
|
||||
// PSQ-related keys stored for handshake
|
||||
/// Local Ed25519 private key for PSQ authentication
|
||||
local_ed25519_private: ed25519::PrivateKey,
|
||||
|
||||
/// Local Ed25519 public key for PSQ authentication
|
||||
local_ed25519_public: ed25519::PublicKey,
|
||||
/// Local Ed25519 keys for PSQ authentication
|
||||
local_ed25519: Arc<ed25519::KeyPair>,
|
||||
|
||||
/// Remote Ed25519 public key for PSQ authentication
|
||||
remote_ed25519_public: ed25519::PublicKey,
|
||||
|
||||
/// Local X25519 private key (Noise static key)
|
||||
local_x25519_private: PrivateKey,
|
||||
/// Local x25519 keys (Noise static key)
|
||||
local_x25519: Arc<x25519::KeyPair>,
|
||||
|
||||
/// Remote X25519 public key (Noise static key)
|
||||
remote_x25519_public: PublicKey,
|
||||
remote_x25519_public: x25519::PublicKey,
|
||||
|
||||
/// Salt for PSK derivation
|
||||
salt: [u8; 32],
|
||||
@@ -276,8 +273,7 @@ impl LpSession {
|
||||
/// Defaults to 1 (current LP version). Set during handshake via
|
||||
/// `set_negotiated_version()` when ClientHello/ServerHello is processed.
|
||||
pub fn negotiated_version(&self) -> u8 {
|
||||
self.negotiated_version
|
||||
.load(std::sync::atomic::Ordering::Acquire)
|
||||
self.negotiated_version.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
/// Sets the negotiated protocol version from handshake packet header.
|
||||
@@ -285,23 +281,22 @@ impl LpSession {
|
||||
/// Should be called during handshake when processing ClientHello (responder)
|
||||
/// or ServerHello (initiator) to record the agreed protocol version.
|
||||
pub fn set_negotiated_version(&self, version: u8) {
|
||||
self.negotiated_version
|
||||
.store(version, std::sync::atomic::Ordering::Release);
|
||||
self.negotiated_version.store(version, Ordering::Release);
|
||||
}
|
||||
|
||||
/// Returns the local X25519 public key derived from the private key.
|
||||
/// Returns the local X25519 public key.
|
||||
///
|
||||
/// This is used for KKT protocol when the responder needs to send their
|
||||
/// KEM public key in the KKT response.
|
||||
pub fn local_x25519_public(&self) -> PublicKey {
|
||||
self.local_x25519_private.public_key()
|
||||
pub fn local_x25519_public(&self) -> x25519::PublicKey {
|
||||
*self.local_x25519.public_key()
|
||||
}
|
||||
|
||||
/// Returns the remote X25519 public key.
|
||||
///
|
||||
/// Used for tie-breaking in simultaneous subsession initiation.
|
||||
/// Lower key loses and becomes responder.
|
||||
pub fn remote_x25519_public(&self) -> &PublicKey {
|
||||
pub fn remote_x25519_public(&self) -> &x25519::PublicKey {
|
||||
&self.remote_x25519_public
|
||||
}
|
||||
|
||||
@@ -356,17 +351,17 @@ impl LpSession {
|
||||
/// * `id` - Session identifier
|
||||
/// * `is_initiator` - True if this side initiates the Noise handshake.
|
||||
/// * `local_ed25519_keypair` - This side's Ed25519 keypair for PSQ authentication
|
||||
/// * `local_x25519_key` - This side's X25519 private key for Noise protocol and DHKEM
|
||||
/// * `local_x25519_keypair` - This side's X25519 keypair for Noise protocol and DHKEM
|
||||
/// * `remote_ed25519_key` - Peer's Ed25519 public key for PSQ authentication
|
||||
/// * `remote_x25519_key` - Peer's X25519 public key for Noise protocol and DHKEM
|
||||
/// * `salt` - Salt for PSK derivation
|
||||
pub fn new(
|
||||
id: u32,
|
||||
is_initiator: bool,
|
||||
local_ed25519_keypair: (&ed25519::PrivateKey, &ed25519::PublicKey),
|
||||
local_x25519_key: &PrivateKey,
|
||||
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||
local_x25519_keypair: Arc<x25519::KeyPair>,
|
||||
remote_ed25519_key: &ed25519::PublicKey,
|
||||
remote_x25519_key: &PublicKey,
|
||||
remote_x25519_key: &x25519::PublicKey,
|
||||
salt: &[u8; 32],
|
||||
) -> Result<Self, LpError> {
|
||||
// XKpsk3 pattern requires remote static key known upfront (XK)
|
||||
@@ -377,8 +372,8 @@ impl LpSession {
|
||||
let params = pattern_name.parse()?;
|
||||
let builder = Builder::new(params);
|
||||
|
||||
let local_key_bytes = local_x25519_key.to_bytes();
|
||||
let builder = builder.local_private_key(&local_key_bytes);
|
||||
let local_key_bytes = local_x25519_keypair.private_key().as_bytes();
|
||||
let builder = builder.local_private_key(local_key_bytes);
|
||||
|
||||
let remote_key_bytes = remote_x25519_key.to_bytes();
|
||||
let builder = builder.remote_public_key(&remote_key_bytes);
|
||||
@@ -416,19 +411,10 @@ impl LpSession {
|
||||
sending_counter: AtomicU64::new(0),
|
||||
receiving_counter: Mutex::new(ReceivingKeyCounterValidator::default()),
|
||||
psk_injected: AtomicBool::new(false),
|
||||
// Ed25519 keys don't impl Clone, so convert to bytes and reconstruct
|
||||
local_ed25519_private: ed25519::PrivateKey::from_bytes(
|
||||
&local_ed25519_keypair.0.to_bytes(),
|
||||
)
|
||||
.expect("Valid ed25519 private key"),
|
||||
local_ed25519_public: ed25519::PublicKey::from_bytes(
|
||||
&local_ed25519_keypair.1.to_bytes(),
|
||||
)
|
||||
.expect("Valid ed25519 public key"),
|
||||
remote_ed25519_public: ed25519::PublicKey::from_bytes(&remote_ed25519_key.to_bytes())
|
||||
.expect("Valid ed25519 public key"),
|
||||
local_x25519_private: local_x25519_key.clone(),
|
||||
remote_x25519_public: remote_x25519_key.clone(),
|
||||
local_ed25519: local_ed25519_keypair.clone(),
|
||||
remote_ed25519_public: *remote_ed25519_key,
|
||||
local_x25519: local_x25519_keypair,
|
||||
remote_x25519_public: *remote_x25519_key,
|
||||
salt: *salt,
|
||||
outer_aead_key: Mutex::new(None),
|
||||
pq_shared_secret: Mutex::new(None),
|
||||
@@ -567,7 +553,7 @@ impl LpSession {
|
||||
match request_kem_key(
|
||||
&mut rng,
|
||||
ciphersuite,
|
||||
&self.local_ed25519_private,
|
||||
self.local_ed25519.private_key(),
|
||||
&self.remote_x25519_public,
|
||||
) {
|
||||
Ok((session_secret, context, request_bytes)) => {
|
||||
@@ -704,8 +690,8 @@ impl LpSession {
|
||||
&mut rng,
|
||||
request_bytes,
|
||||
Some(&self.remote_ed25519_public), // Verify initiator signature
|
||||
&self.local_ed25519_private, // Sign response
|
||||
&self.local_x25519_private,
|
||||
self.local_ed25519.private_key(), // Sign response
|
||||
self.local_x25519.private_key(),
|
||||
responder_kem_pk,
|
||||
)
|
||||
.map_err(|e| LpError::Internal(format!("KKT request handling failed: {:?}", e)))?;
|
||||
@@ -757,11 +743,11 @@ impl LpSession {
|
||||
let session_context = self.id.to_le_bytes();
|
||||
|
||||
let psq_result = match psq_initiator_create_message(
|
||||
&self.local_x25519_private,
|
||||
self.local_x25519.private_key(),
|
||||
&self.remote_x25519_public,
|
||||
remote_kem,
|
||||
&self.local_ed25519_private,
|
||||
&self.local_ed25519_public,
|
||||
self.local_ed25519.private_key(),
|
||||
self.local_ed25519.public_key(),
|
||||
&self.salt,
|
||||
&session_context,
|
||||
) {
|
||||
@@ -895,7 +881,7 @@ impl LpSession {
|
||||
let noise_payload = &payload[2 + psq_len..];
|
||||
|
||||
// Convert X25519 local keys to DecapsulationKey/EncapsulationKey (DHKEM)
|
||||
let local_private_bytes = &self.local_x25519_private.to_bytes();
|
||||
let local_private_bytes = &self.local_x25519.private_key().to_bytes();
|
||||
let libcrux_private_key = libcrux_kem::PrivateKey::decode(
|
||||
libcrux_kem::Algorithm::X25519,
|
||||
local_private_bytes,
|
||||
@@ -908,7 +894,7 @@ impl LpSession {
|
||||
})?;
|
||||
let dec_key = DecapsulationKey::X25519(libcrux_private_key);
|
||||
|
||||
let local_public_key = self.local_x25519_private.public_key();
|
||||
let local_public_key = self.local_x25519_public();
|
||||
let local_public_bytes = local_public_key.as_bytes();
|
||||
let libcrux_public_key = libcrux_kem::PublicKey::decode(
|
||||
libcrux_kem::Algorithm::X25519,
|
||||
@@ -926,7 +912,7 @@ impl LpSession {
|
||||
let session_context = self.id.to_le_bytes();
|
||||
|
||||
let psq_result = match psq_responder_process_message(
|
||||
&self.local_x25519_private,
|
||||
self.local_x25519.private_key(),
|
||||
&self.remote_x25519_public,
|
||||
(&dec_key, &enc_key),
|
||||
&self.remote_ed25519_public,
|
||||
@@ -1132,7 +1118,7 @@ impl LpSession {
|
||||
/// Test-only method to set KKT state to Completed with a mock KEM key.
|
||||
/// This allows tests to bypass KKT exchange and directly test PSQ handshake.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn set_kkt_completed_for_test(&self, remote_x25519_pub: &PublicKey) {
|
||||
pub(crate) fn set_kkt_completed_for_test(&self, remote_x25519_pub: &x25519::PublicKey) {
|
||||
// Convert remote X25519 public key to EncapsulationKey for testing
|
||||
let remote_kem_bytes = remote_x25519_pub.as_bytes();
|
||||
let libcrux_public_key =
|
||||
@@ -1185,7 +1171,7 @@ impl LpSession {
|
||||
let pattern_name = "Noise_KKpsk0_25519_ChaChaPoly_SHA256";
|
||||
let params = pattern_name.parse()?;
|
||||
|
||||
let local_key_bytes = self.local_x25519_private.to_bytes();
|
||||
let local_key_bytes = self.local_x25519.private_key().to_bytes();
|
||||
let remote_key_bytes = self.remote_x25519_public.to_bytes();
|
||||
|
||||
let builder = Builder::new(params)
|
||||
@@ -1204,22 +1190,12 @@ impl LpSession {
|
||||
noise_state: Mutex::new(NoiseProtocol::new(handshake_state)),
|
||||
is_initiator,
|
||||
// Copy key material from parent for into_session() conversion
|
||||
local_ed25519_private: ed25519::PrivateKey::from_bytes(
|
||||
&self.local_ed25519_private.to_bytes(),
|
||||
)
|
||||
.expect("Valid Ed25519 private key from parent"),
|
||||
local_ed25519_public: ed25519::PublicKey::from_bytes(
|
||||
&self.local_ed25519_public.to_bytes(),
|
||||
)
|
||||
.expect("Valid Ed25519 public key from parent"),
|
||||
remote_ed25519_public: ed25519::PublicKey::from_bytes(
|
||||
&self.remote_ed25519_public.to_bytes(),
|
||||
)
|
||||
.expect("Valid Ed25519 public key from parent"),
|
||||
local_x25519_private: self.local_x25519_private.clone(),
|
||||
remote_x25519_public: self.remote_x25519_public.clone(),
|
||||
local_ed25519: self.local_ed25519.clone(),
|
||||
remote_ed25519_public: self.remote_ed25519_public,
|
||||
remote_x25519_public: self.remote_x25519_public,
|
||||
pq_shared_secret: PqSharedSecret::new(pq_secret),
|
||||
subsession_psk,
|
||||
local_x25519: self.local_x25519.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1249,16 +1225,17 @@ pub struct SubsessionHandshake {
|
||||
is_initiator: bool,
|
||||
|
||||
// Key material inherited from parent session for into_session() conversion
|
||||
/// Local Ed25519 private key (for PSQ auth if needed)
|
||||
local_ed25519_private: ed25519::PrivateKey,
|
||||
/// Local Ed25519 public key
|
||||
local_ed25519_public: ed25519::PublicKey,
|
||||
/// Local Ed25519 keys (for PSQ auth if needed)
|
||||
local_ed25519: Arc<ed25519::KeyPair>,
|
||||
|
||||
/// Local x25519 keys (Noise static key)
|
||||
local_x25519: Arc<x25519::KeyPair>,
|
||||
|
||||
/// Remote Ed25519 public key
|
||||
remote_ed25519_public: ed25519::PublicKey,
|
||||
/// Local X25519 private key (Noise static key)
|
||||
local_x25519_private: PrivateKey,
|
||||
|
||||
/// Remote X25519 public key (Noise static key)
|
||||
remote_x25519_public: PublicKey,
|
||||
remote_x25519_public: x25519::PublicKey,
|
||||
/// PQ shared secret inherited from parent (for creating further subsessions)
|
||||
pq_shared_secret: PqSharedSecret,
|
||||
/// Subsession PSK (for deriving outer AEAD key)
|
||||
@@ -1351,10 +1328,9 @@ impl SubsessionHandshake {
|
||||
sending_counter: AtomicU64::new(0),
|
||||
receiving_counter: Mutex::new(ReceivingKeyCounterValidator::new(0)),
|
||||
psk_injected: AtomicBool::new(true), // PSK was in KKpsk0
|
||||
local_ed25519_private: self.local_ed25519_private,
|
||||
local_ed25519_public: self.local_ed25519_public,
|
||||
local_ed25519: self.local_ed25519,
|
||||
remote_ed25519_public: self.remote_ed25519_public,
|
||||
local_x25519_private: self.local_x25519_private,
|
||||
local_x25519: self.local_x25519,
|
||||
remote_x25519_public: self.remote_x25519_public,
|
||||
salt,
|
||||
outer_aead_key: Mutex::new(Some(outer_key)),
|
||||
@@ -1372,18 +1348,19 @@ impl SubsessionHandshake {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{replay::ReplayError, sessions_for_tests};
|
||||
use rand::thread_rng;
|
||||
|
||||
// Helper function to generate keypairs for tests
|
||||
fn generate_keypair() -> crate::keypair::Keypair {
|
||||
crate::keypair::Keypair::default()
|
||||
fn generate_keypair() -> x25519::KeyPair {
|
||||
x25519::KeyPair::new(&mut thread_rng())
|
||||
}
|
||||
|
||||
// Helper function to create a session with real keys for handshake tests
|
||||
fn create_handshake_test_session(
|
||||
receiver_index: u32,
|
||||
is_initiator: bool,
|
||||
local_keys: &crate::keypair::Keypair,
|
||||
remote_pub_key: &crate::keypair::PublicKey,
|
||||
local_keys: &x25519::KeyPair,
|
||||
remote_pub_key: &x25519::PublicKey,
|
||||
) -> LpSession {
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
|
||||
@@ -1396,6 +1373,9 @@ mod tests {
|
||||
};
|
||||
|
||||
let local_ed25519 = ed25519::KeyPair::from_secret(local_ed25519_seed, 0);
|
||||
let local_x25519 = x25519::PrivateKey::from_bytes(local_keys.private_key().as_bytes())
|
||||
.unwrap()
|
||||
.into();
|
||||
let remote_ed25519 = ed25519::KeyPair::from_secret(remote_ed25519_seed, 1);
|
||||
|
||||
let salt = [0u8; 32]; // Test salt
|
||||
@@ -1404,8 +1384,8 @@ mod tests {
|
||||
let session = LpSession::new(
|
||||
receiver_index,
|
||||
is_initiator,
|
||||
(local_ed25519.private_key(), local_ed25519.public_key()),
|
||||
local_keys.private_key(),
|
||||
Arc::new(local_ed25519),
|
||||
Arc::new(local_x25519),
|
||||
remote_ed25519.public_key(),
|
||||
remote_pub_key,
|
||||
&salt,
|
||||
@@ -1510,8 +1490,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_prepare_handshake_message_initial_state() {
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
let receiver_index = 12345u32;
|
||||
|
||||
let initiator_session = create_handshake_test_session(
|
||||
@@ -1542,8 +1522,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_process_handshake_message_first_step() {
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
let receiver_index = 12345u32;
|
||||
|
||||
let initiator_session = create_handshake_test_session(
|
||||
@@ -1588,8 +1568,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_handshake_driver_simulation() {
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
let initiator_session = create_handshake_test_session(
|
||||
12345u32,
|
||||
@@ -1683,8 +1663,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_encrypt_decrypt_after_handshake() {
|
||||
// --- Setup Handshake ---
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
let initiator_session = create_handshake_test_session(
|
||||
12345u32,
|
||||
@@ -1752,8 +1732,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_encrypt_decrypt_before_handshake() {
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
let initiator_session = create_handshake_test_session(
|
||||
12345u32,
|
||||
@@ -1828,8 +1808,8 @@ mod tests {
|
||||
/// Test that PSQ runs during handshake and derives a PSK
|
||||
#[test]
|
||||
fn test_psq_handshake_runs_with_psk_injection() {
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
let initiator_session = create_handshake_test_session(
|
||||
12345u32,
|
||||
@@ -1905,8 +1885,8 @@ mod tests {
|
||||
fn test_x25519_to_kem_conversion() {
|
||||
use nym_kkt::ciphersuite::EncapsulationKey;
|
||||
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
// Verify we can convert X25519 public key to KEM format (as done in session.rs)
|
||||
let x25519_public_bytes = responder_keys.public_key().as_bytes();
|
||||
@@ -1929,8 +1909,8 @@ mod tests {
|
||||
/// Test that PSQ actually derives a different PSK (not using dummy)
|
||||
#[test]
|
||||
fn test_psq_derived_psk_differs_from_dummy() {
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
// Create sessions - they start with dummy PSK [0u8; 32]
|
||||
let initiator_session = create_handshake_test_session(
|
||||
@@ -2003,8 +1983,8 @@ mod tests {
|
||||
/// Test full end-to-end handshake with PSQ integration
|
||||
#[test]
|
||||
fn test_handshake_with_psq_end_to_end() {
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
let initiator_session = create_handshake_test_session(
|
||||
12345u32,
|
||||
@@ -2089,8 +2069,8 @@ mod tests {
|
||||
/// Test that Ed25519 keys are used in PSQ authentication
|
||||
#[test]
|
||||
fn test_psq_handshake_uses_ed25519_authentication() {
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
// Create sessions with explicit Ed25519 keys
|
||||
let initiator_session = create_handshake_test_session(
|
||||
@@ -2172,8 +2152,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_handshake_abort_on_psq_failure() {
|
||||
// Test that Ed25519 auth failure causes handshake abort
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
// Create sessions with MISMATCHED Ed25519 keys
|
||||
// This simulates authentication failure
|
||||
@@ -2186,11 +2166,8 @@ mod tests {
|
||||
let initiator_session = LpSession::new(
|
||||
receiver_index,
|
||||
true,
|
||||
(
|
||||
initiator_ed25519.private_key(),
|
||||
initiator_ed25519.public_key(),
|
||||
),
|
||||
initiator_keys.private_key(),
|
||||
Arc::new(initiator_ed25519),
|
||||
initiator_keys.clone(),
|
||||
wrong_ed25519.public_key(), // Responder expects THIS key
|
||||
responder_keys.public_key(),
|
||||
&salt,
|
||||
@@ -2204,11 +2181,8 @@ mod tests {
|
||||
let responder_session = LpSession::new(
|
||||
receiver_index,
|
||||
false,
|
||||
(
|
||||
responder_ed25519.private_key(),
|
||||
responder_ed25519.public_key(),
|
||||
),
|
||||
responder_keys.private_key(),
|
||||
Arc::new(responder_ed25519),
|
||||
responder_keys.clone(),
|
||||
wrong_ed25519.public_key(), // Expects WRONG key (not initiator's)
|
||||
initiator_keys.public_key(),
|
||||
&salt,
|
||||
@@ -2241,8 +2215,8 @@ mod tests {
|
||||
fn test_psq_invalid_signature() {
|
||||
// Test Ed25519 signature validation specifically
|
||||
// Setup with matching X25519 keys but mismatched Ed25519 keys
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
// Initiator uses Ed25519 key [1u8]
|
||||
let initiator_ed25519 = ed25519::KeyPair::from_secret([1u8; 32], 0);
|
||||
@@ -2257,11 +2231,8 @@ mod tests {
|
||||
let initiator_session = LpSession::new(
|
||||
receiver_index,
|
||||
true,
|
||||
(
|
||||
initiator_ed25519.private_key(),
|
||||
initiator_ed25519.public_key(),
|
||||
),
|
||||
initiator_keys.private_key(),
|
||||
Arc::new(initiator_ed25519),
|
||||
initiator_keys.clone(),
|
||||
wrong_ed25519_public, // This doesn't matter for initiator
|
||||
responder_keys.public_key(),
|
||||
&salt,
|
||||
@@ -2275,11 +2246,8 @@ mod tests {
|
||||
let responder_session = LpSession::new(
|
||||
receiver_index,
|
||||
false,
|
||||
(
|
||||
responder_ed25519.private_key(),
|
||||
responder_ed25519.public_key(),
|
||||
),
|
||||
responder_keys.private_key(),
|
||||
Arc::new(responder_ed25519),
|
||||
responder_keys.clone(),
|
||||
wrong_ed25519_public, // Responder expects WRONG key
|
||||
initiator_keys.public_key(),
|
||||
&salt,
|
||||
@@ -2364,8 +2332,8 @@ mod tests {
|
||||
// This test verifies the safety mechanism that prevents transport mode operations
|
||||
// from running with the dummy PSK if PSQ injection fails or is skipped.
|
||||
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
// Create session but don't complete handshake (no PSK injection will occur)
|
||||
let session = create_handshake_test_session(
|
||||
@@ -2413,8 +2381,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_demote_sets_read_only() {
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
let session = create_handshake_test_session(
|
||||
12345u32,
|
||||
@@ -2438,8 +2406,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_encrypt_fails_after_demotion() {
|
||||
// --- Setup Handshake ---
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
let initiator_session = create_handshake_test_session(
|
||||
12345u32,
|
||||
@@ -2494,8 +2462,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_decrypt_works_after_demotion() {
|
||||
// --- Setup Handshake ---
|
||||
let initiator_keys = generate_keypair();
|
||||
let responder_keys = generate_keypair();
|
||||
let initiator_keys = Arc::new(generate_keypair());
|
||||
let responder_keys = Arc::new(generate_keypair());
|
||||
|
||||
let initiator_session = create_handshake_test_session(
|
||||
12345u32,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::codec::{parse_lp_packet, serialize_lp_packet};
|
||||
use crate::keypair::PublicKey;
|
||||
use crate::{
|
||||
LpError,
|
||||
message::LpMessage,
|
||||
@@ -9,7 +8,8 @@ mod tests {
|
||||
session_manager::SessionManager,
|
||||
};
|
||||
use bytes::BytesMut;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use std::sync::Arc;
|
||||
|
||||
// Function to create a test packet - similar to how it's done in codec.rs tests
|
||||
fn create_test_packet(
|
||||
@@ -53,6 +53,11 @@ mod tests {
|
||||
let ed25519_keypair_a = ed25519::KeyPair::from_secret([1u8; 32], 0);
|
||||
let ed25519_keypair_b = ed25519::KeyPair::from_secret([2u8; 32], 1);
|
||||
|
||||
let x25519_keypair_a = ed25519_keypair_a.to_x25519();
|
||||
let x25519_keypair_b = ed25519_keypair_b.to_x25519();
|
||||
|
||||
let ed25519_pubkey_a = *ed25519_keypair_a.public_key();
|
||||
|
||||
// Derive X25519 keys from Ed25519 (needed for KKT init test)
|
||||
let x25519_pub_a = ed25519_keypair_a
|
||||
.public_key()
|
||||
@@ -64,9 +69,9 @@ mod tests {
|
||||
.expect("Failed to derive X25519 from Ed25519");
|
||||
|
||||
// Convert to LP keypair types
|
||||
let lp_pub_a = PublicKey::from_bytes(x25519_pub_a.as_bytes())
|
||||
let lp_pub_a = x25519::PublicKey::from_bytes(x25519_pub_a.as_bytes())
|
||||
.expect("Failed to create PublicKey from bytes");
|
||||
let lp_pub_b = PublicKey::from_bytes(x25519_pub_b.as_bytes())
|
||||
let lp_pub_b = x25519::PublicKey::from_bytes(x25519_pub_b.as_bytes())
|
||||
.expect("Failed to create PublicKey from bytes");
|
||||
|
||||
// Use fixed receiver_index for deterministic test
|
||||
@@ -79,11 +84,9 @@ mod tests {
|
||||
let peer_a_sm = session_manager_1
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(
|
||||
ed25519_keypair_a.private_key(),
|
||||
ed25519_keypair_a.public_key(),
|
||||
),
|
||||
Arc::new(ed25519_keypair_a),
|
||||
ed25519_keypair_b.public_key(),
|
||||
x25519_keypair_b.public_key(),
|
||||
true,
|
||||
&salt,
|
||||
)
|
||||
@@ -92,11 +95,9 @@ mod tests {
|
||||
let peer_b_sm = session_manager_2
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(
|
||||
ed25519_keypair_b.private_key(),
|
||||
ed25519_keypair_b.public_key(),
|
||||
),
|
||||
ed25519_keypair_a.public_key(),
|
||||
Arc::new(ed25519_keypair_b),
|
||||
&ed25519_pubkey_a,
|
||||
x25519_keypair_a.public_key(),
|
||||
false,
|
||||
&salt,
|
||||
)
|
||||
@@ -512,6 +513,11 @@ mod tests {
|
||||
let ed25519_keypair_a = ed25519::KeyPair::from_secret([3u8; 32], 0);
|
||||
let ed25519_keypair_b = ed25519::KeyPair::from_secret([4u8; 32], 1);
|
||||
|
||||
let x25519_keypair_a = ed25519_keypair_a.to_x25519();
|
||||
let x25519_keypair_b = ed25519_keypair_b.to_x25519();
|
||||
|
||||
let ed25519_pubkey_a = *ed25519_keypair_a.public_key();
|
||||
|
||||
// Derive X25519 keys from Ed25519 (same as state machine does internally)
|
||||
let x25519_pub_a = ed25519_keypair_a
|
||||
.public_key()
|
||||
@@ -523,9 +529,9 @@ mod tests {
|
||||
.expect("Failed to derive X25519 from Ed25519");
|
||||
|
||||
// Convert to LP keypair types
|
||||
let lp_pub_a = PublicKey::from_bytes(x25519_pub_a.as_bytes())
|
||||
let lp_pub_a = x25519::PublicKey::from_bytes(x25519_pub_a.as_bytes())
|
||||
.expect("Failed to create PublicKey from bytes");
|
||||
let lp_pub_b = PublicKey::from_bytes(x25519_pub_b.as_bytes())
|
||||
let lp_pub_b = x25519::PublicKey::from_bytes(x25519_pub_b.as_bytes())
|
||||
.expect("Failed to create PublicKey from bytes");
|
||||
|
||||
// Use fixed receiver_index for test
|
||||
@@ -537,11 +543,9 @@ mod tests {
|
||||
let peer_a_sm = session_manager_1
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(
|
||||
ed25519_keypair_a.private_key(),
|
||||
ed25519_keypair_a.public_key(),
|
||||
),
|
||||
Arc::new(ed25519_keypair_a),
|
||||
ed25519_keypair_b.public_key(),
|
||||
x25519_keypair_b.public_key(),
|
||||
true,
|
||||
&salt,
|
||||
)
|
||||
@@ -549,11 +553,9 @@ mod tests {
|
||||
let peer_b_sm = session_manager_2
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(
|
||||
ed25519_keypair_b.private_key(),
|
||||
ed25519_keypair_b.public_key(),
|
||||
),
|
||||
ed25519_keypair_a.public_key(),
|
||||
Arc::new(ed25519_keypair_b),
|
||||
&ed25519_pubkey_a,
|
||||
x25519_keypair_a.public_key(),
|
||||
false,
|
||||
&salt,
|
||||
)
|
||||
@@ -720,16 +722,22 @@ mod tests {
|
||||
let session_manager = SessionManager::new();
|
||||
|
||||
// Generate Ed25519 keypair for PSQ authentication
|
||||
let ed25519_keypair = ed25519::KeyPair::from_secret([5u8; 32], 0);
|
||||
let ed25519_keypair_a = ed25519::KeyPair::from_secret([5u8; 32], 0);
|
||||
let ed25519_keypair_b = ed25519::KeyPair::from_secret([6u8; 32], 0);
|
||||
|
||||
let x25519_keypair_a = ed25519_keypair_a.to_x25519();
|
||||
let x25519_keypair_b = ed25519_keypair_b.to_x25519();
|
||||
|
||||
// Derive X25519 key from Ed25519 (same as state machine does internally)
|
||||
let x25519_pub = ed25519_keypair
|
||||
let x25519_pub = ed25519_keypair_a
|
||||
.public_key()
|
||||
.to_x25519()
|
||||
.expect("Failed to derive X25519 from Ed25519");
|
||||
|
||||
let keypair_a = Arc::new(ed25519_keypair_a);
|
||||
|
||||
// Convert to LP keypair type (still needed for init_kkt_for_test below if used)
|
||||
let _lp_pub = PublicKey::from_bytes(x25519_pub.as_bytes())
|
||||
let _lp_pub = x25519::PublicKey::from_bytes(x25519_pub.as_bytes())
|
||||
.expect("Failed to create PublicKey from bytes");
|
||||
|
||||
// Use fixed receiver_index for test
|
||||
@@ -742,8 +750,9 @@ mod tests {
|
||||
let _session = session_manager
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(ed25519_keypair.private_key(), ed25519_keypair.public_key()),
|
||||
ed25519_keypair.public_key(),
|
||||
keypair_a.clone(),
|
||||
ed25519_keypair_b.public_key(),
|
||||
x25519_keypair_b.public_key(),
|
||||
true,
|
||||
&salt,
|
||||
)
|
||||
@@ -765,8 +774,9 @@ mod tests {
|
||||
let _temp_session = session_manager
|
||||
.create_session_state_machine(
|
||||
receiver_index_temp,
|
||||
(ed25519_keypair.private_key(), ed25519_keypair.public_key()),
|
||||
ed25519_keypair.public_key(),
|
||||
keypair_a.clone(),
|
||||
ed25519_keypair_b.public_key(),
|
||||
x25519_keypair_a.public_key(),
|
||||
true,
|
||||
&salt,
|
||||
)
|
||||
@@ -849,6 +859,12 @@ mod tests {
|
||||
let ed25519_keypair_a = ed25519::KeyPair::from_secret([6u8; 32], 0);
|
||||
let ed25519_keypair_b = ed25519::KeyPair::from_secret([7u8; 32], 1);
|
||||
|
||||
let x25519_keypair_a = ed25519_keypair_a.to_x25519();
|
||||
let x25519_keypair_b = ed25519_keypair_b.to_x25519();
|
||||
|
||||
let pubkey_a = *ed25519_keypair_a.public_key();
|
||||
let pubkey_b = *ed25519_keypair_b.public_key();
|
||||
|
||||
// Use fixed receiver_index for test
|
||||
let receiver_index: u32 = 100005;
|
||||
|
||||
@@ -860,11 +876,9 @@ mod tests {
|
||||
session_manager_1
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(
|
||||
ed25519_keypair_a.private_key(),
|
||||
ed25519_keypair_a.public_key()
|
||||
),
|
||||
ed25519_keypair_b.public_key(),
|
||||
Arc::new(ed25519_keypair_a),
|
||||
&pubkey_b,
|
||||
x25519_keypair_b.public_key(),
|
||||
true,
|
||||
&salt,
|
||||
) // Initiator
|
||||
@@ -874,11 +888,9 @@ mod tests {
|
||||
session_manager_2
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(
|
||||
ed25519_keypair_b.private_key(),
|
||||
ed25519_keypair_b.public_key()
|
||||
),
|
||||
ed25519_keypair_a.public_key(),
|
||||
Arc::new(ed25519_keypair_b),
|
||||
&pubkey_a,
|
||||
x25519_keypair_a.public_key(),
|
||||
false,
|
||||
&salt,
|
||||
) // Responder
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
//! creation, retrieval, and storage of sessions.
|
||||
|
||||
use dashmap::DashMap;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::noise_protocol::ReadResult;
|
||||
use crate::state_machine::{LpAction, LpInput, LpState, LpStateBare};
|
||||
@@ -169,8 +170,9 @@ impl SessionManager {
|
||||
pub fn create_session_state_machine(
|
||||
&self,
|
||||
receiver_index: u32,
|
||||
local_ed25519_keypair: (&ed25519::PrivateKey, &ed25519::PublicKey),
|
||||
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||
remote_ed25519_key: &ed25519::PublicKey,
|
||||
remote_x25519_key: &x25519::PublicKey,
|
||||
is_initiator: bool,
|
||||
salt: &[u8; 32],
|
||||
) -> Result<u32, LpError> {
|
||||
@@ -179,6 +181,7 @@ impl SessionManager {
|
||||
is_initiator,
|
||||
local_ed25519_keypair,
|
||||
remote_ed25519_key,
|
||||
remote_x25519_key,
|
||||
salt,
|
||||
)?;
|
||||
|
||||
@@ -199,7 +202,7 @@ impl SessionManager {
|
||||
pub fn init_kkt_for_test(
|
||||
&self,
|
||||
lp_id: u32,
|
||||
remote_x25519_pub: &crate::keypair::PublicKey,
|
||||
remote_x25519_pub: &x25519::PublicKey,
|
||||
) -> Result<(), LpError> {
|
||||
self.with_state_machine(lp_id, |sm| {
|
||||
sm.session()?.set_kkt_completed_for_test(remote_x25519_pub);
|
||||
@@ -217,14 +220,19 @@ mod tests {
|
||||
fn test_session_manager_get() {
|
||||
let manager = SessionManager::new();
|
||||
let ed25519_keypair = ed25519::KeyPair::from_secret([10u8; 32], 0);
|
||||
let ed25519_keypair2 = ed25519::KeyPair::from_secret([16u8; 32], 0);
|
||||
|
||||
let x25519_keypair2 = ed25519_keypair2.to_x25519();
|
||||
|
||||
let salt = [47u8; 32];
|
||||
let receiver_index: u32 = 1001;
|
||||
|
||||
let sm_1_id = manager
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(ed25519_keypair.private_key(), ed25519_keypair.public_key()),
|
||||
ed25519_keypair.public_key(),
|
||||
Arc::new(ed25519_keypair),
|
||||
ed25519_keypair2.public_key(),
|
||||
x25519_keypair2.public_key(),
|
||||
true,
|
||||
&salt,
|
||||
)
|
||||
@@ -241,14 +249,19 @@ mod tests {
|
||||
fn test_session_manager_remove() {
|
||||
let manager = SessionManager::new();
|
||||
let ed25519_keypair = ed25519::KeyPair::from_secret([11u8; 32], 0);
|
||||
let ed25519_keypair2 = ed25519::KeyPair::from_secret([16u8; 32], 0);
|
||||
|
||||
let x25519_keypair2 = ed25519_keypair2.to_x25519();
|
||||
|
||||
let salt = [48u8; 32];
|
||||
let receiver_index: u32 = 2002;
|
||||
|
||||
let sm_1_id = manager
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
(ed25519_keypair.private_key(), ed25519_keypair.public_key()),
|
||||
ed25519_keypair.public_key(),
|
||||
Arc::new(ed25519_keypair),
|
||||
ed25519_keypair2.public_key(),
|
||||
x25519_keypair2.public_key(),
|
||||
true,
|
||||
&salt,
|
||||
)
|
||||
@@ -270,14 +283,20 @@ mod tests {
|
||||
let ed25519_keypair_3 = ed25519::KeyPair::from_secret([14u8; 32], 2);
|
||||
let salt = [49u8; 32];
|
||||
|
||||
let pubkey1 = *ed25519_keypair_1.public_key();
|
||||
let pubkey2 = *ed25519_keypair_2.public_key();
|
||||
let pubkey3 = *ed25519_keypair_3.public_key();
|
||||
|
||||
let xpubkey1 = *ed25519_keypair_1.to_x25519().public_key();
|
||||
let xpubkey2 = *ed25519_keypair_2.to_x25519().public_key();
|
||||
let xpubkey3 = *ed25519_keypair_3.to_x25519().public_key();
|
||||
|
||||
let sm_1 = manager
|
||||
.create_session_state_machine(
|
||||
3001,
|
||||
(
|
||||
ed25519_keypair_1.private_key(),
|
||||
ed25519_keypair_1.public_key(),
|
||||
),
|
||||
ed25519_keypair_1.public_key(),
|
||||
Arc::new(ed25519_keypair_1),
|
||||
&pubkey2,
|
||||
&xpubkey2,
|
||||
true,
|
||||
&salt,
|
||||
)
|
||||
@@ -286,11 +305,9 @@ mod tests {
|
||||
let sm_2 = manager
|
||||
.create_session_state_machine(
|
||||
3002,
|
||||
(
|
||||
ed25519_keypair_2.private_key(),
|
||||
ed25519_keypair_2.public_key(),
|
||||
),
|
||||
ed25519_keypair_2.public_key(),
|
||||
Arc::new(ed25519_keypair_2),
|
||||
&pubkey3,
|
||||
&xpubkey3,
|
||||
true,
|
||||
&salt,
|
||||
)
|
||||
@@ -299,11 +316,9 @@ mod tests {
|
||||
let sm_3 = manager
|
||||
.create_session_state_machine(
|
||||
3003,
|
||||
(
|
||||
ed25519_keypair_3.private_key(),
|
||||
ed25519_keypair_3.public_key(),
|
||||
),
|
||||
ed25519_keypair_3.public_key(),
|
||||
Arc::new(ed25519_keypair_3),
|
||||
&pubkey1,
|
||||
&xpubkey1,
|
||||
true,
|
||||
&salt,
|
||||
)
|
||||
@@ -324,13 +339,17 @@ mod tests {
|
||||
fn test_session_manager_create_session() {
|
||||
let manager = SessionManager::new();
|
||||
let ed25519_keypair = ed25519::KeyPair::from_secret([15u8; 32], 0);
|
||||
let ed25519_keypair2 = ed25519::KeyPair::from_secret([16u8; 32], 0);
|
||||
let salt = [50u8; 32];
|
||||
let receiver_index: u32 = 4004;
|
||||
|
||||
let x25519_keypair2 = ed25519_keypair2.to_x25519();
|
||||
|
||||
let sm = manager.create_session_state_machine(
|
||||
receiver_index,
|
||||
(ed25519_keypair.private_key(), ed25519_keypair.public_key()),
|
||||
ed25519_keypair.public_key(),
|
||||
Arc::new(ed25519_keypair),
|
||||
ed25519_keypair2.public_key(),
|
||||
x25519_keypair2.public_key(),
|
||||
true,
|
||||
&salt,
|
||||
);
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
|
||||
use crate::{
|
||||
LpError,
|
||||
keypair::{Keypair, PrivateKey as LpPrivateKey, PublicKey as LpPublicKey},
|
||||
message::{LpMessage, SubsessionKK1Data, SubsessionKK2Data, SubsessionReadyData},
|
||||
noise_protocol::NoiseError,
|
||||
packet::LpPacket,
|
||||
session::{LpSession, SubsessionHandshake},
|
||||
};
|
||||
use bytes::BytesMut;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use tracing::debug;
|
||||
|
||||
/// Represents the possible states of the Lewes Protocol connection.
|
||||
@@ -90,6 +90,7 @@ impl From<&LpState> for LpStateBare {
|
||||
}
|
||||
|
||||
/// Represents inputs that drive the state machine transitions.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug)]
|
||||
pub enum LpInput {
|
||||
/// Explicitly trigger the start of the handshake (optional, could be implicit on creation)
|
||||
@@ -189,7 +190,8 @@ impl LpStateMachine {
|
||||
/// * `is_initiator` - Whether this side initiates the handshake
|
||||
/// * `local_ed25519_keypair` - Ed25519 keypair for PSQ authentication and X25519 derivation
|
||||
/// (from client identity key or gateway signing key)
|
||||
/// * `remote_ed25519_key` - Peer's Ed25519 public key for PSQ authentication and X25519 derivation
|
||||
/// * `remote_ed25519_key` - Peer's Ed25519 public key for PSQ authentication
|
||||
/// * `remote_x25519_key` - Peer's x25519 public key for Noise protocol and DHKEM
|
||||
/// * `salt` - Fresh salt for PSK derivation (must be unique per session)
|
||||
///
|
||||
/// # Errors
|
||||
@@ -199,8 +201,9 @@ impl LpStateMachine {
|
||||
pub fn new(
|
||||
receiver_index: u32,
|
||||
is_initiator: bool,
|
||||
local_ed25519_keypair: (&ed25519::PrivateKey, &ed25519::PublicKey),
|
||||
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||
remote_ed25519_key: &ed25519::PublicKey,
|
||||
remote_x25519_key: &x25519::PublicKey,
|
||||
salt: &[u8; 32],
|
||||
) -> Result<Self, LpError> {
|
||||
// We use standard RFC 7748 conversion to derive X25519 keys from Ed25519 identity keys.
|
||||
@@ -213,23 +216,7 @@ impl LpStateMachine {
|
||||
// - PSQ ECDH baseline security (pre-quantum)
|
||||
|
||||
// Convert Ed25519 keys to X25519 for Noise protocol
|
||||
let local_x25519_private = local_ed25519_keypair.0.to_x25519();
|
||||
let local_x25519_public = local_ed25519_keypair
|
||||
.1
|
||||
.to_x25519()
|
||||
.map_err(LpError::Ed25519RecoveryError)?;
|
||||
|
||||
let remote_x25519_public = remote_ed25519_key
|
||||
.to_x25519()
|
||||
.map_err(LpError::Ed25519RecoveryError)?;
|
||||
|
||||
// Convert nym_crypto X25519 types to nym_lp keypair types
|
||||
let lp_private = LpPrivateKey::from_bytes(local_x25519_private.as_bytes());
|
||||
let lp_public = LpPublicKey::from_bytes(local_x25519_public.as_bytes())?;
|
||||
let lp_remote_public = LpPublicKey::from_bytes(remote_x25519_public.as_bytes())?;
|
||||
|
||||
// Create X25519 keypair for Noise
|
||||
let local_x25519_keypair = Keypair::from_keys(lp_private, lp_public);
|
||||
let local_x25519 = Arc::new(local_ed25519_keypair.to_x25519());
|
||||
|
||||
// Create the session with both Ed25519 (for PSQ auth) and derived X25519 keys (for Noise)
|
||||
// receiver_index is client-proposed, passed through directly
|
||||
@@ -237,9 +224,9 @@ impl LpStateMachine {
|
||||
receiver_index,
|
||||
is_initiator,
|
||||
local_ed25519_keypair,
|
||||
local_x25519_keypair.private_key(),
|
||||
local_x25519,
|
||||
remote_ed25519_key,
|
||||
&lp_remote_public,
|
||||
remote_x25519_key,
|
||||
salt,
|
||||
)?;
|
||||
|
||||
@@ -1105,6 +1092,10 @@ mod tests {
|
||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([16u8; 32], 0);
|
||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([17u8; 32], 1);
|
||||
|
||||
let ed25519_pubkey_init = *ed25519_keypair_init.public_key();
|
||||
let x25519_pubkey_init = ed25519_pubkey_init.to_x25519().unwrap();
|
||||
let x25519_pubkey_resp = *ed25519_keypair_resp.to_x25519().public_key();
|
||||
|
||||
// Test salt
|
||||
let salt = [51u8; 32];
|
||||
|
||||
@@ -1113,11 +1104,9 @@ mod tests {
|
||||
let initiator_sm = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
(
|
||||
ed25519_keypair_init.private_key(),
|
||||
ed25519_keypair_init.public_key(),
|
||||
),
|
||||
Arc::new(ed25519_keypair_init),
|
||||
ed25519_keypair_resp.public_key(),
|
||||
&x25519_pubkey_resp,
|
||||
&salt,
|
||||
);
|
||||
assert!(initiator_sm.is_ok());
|
||||
@@ -1132,11 +1121,9 @@ mod tests {
|
||||
let responder_sm = LpStateMachine::new(
|
||||
receiver_index,
|
||||
false,
|
||||
(
|
||||
ed25519_keypair_resp.private_key(),
|
||||
ed25519_keypair_resp.public_key(),
|
||||
),
|
||||
ed25519_keypair_init.public_key(),
|
||||
Arc::new(ed25519_keypair_resp),
|
||||
&ed25519_pubkey_init,
|
||||
&x25519_pubkey_init,
|
||||
&salt,
|
||||
);
|
||||
assert!(responder_sm.is_ok());
|
||||
@@ -1158,6 +1145,10 @@ mod tests {
|
||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([18u8; 32], 0);
|
||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([19u8; 32], 1);
|
||||
|
||||
let ed25519_pubkey_init = *ed25519_keypair_init.public_key();
|
||||
let x25519_pubkey_init = ed25519_pubkey_init.to_x25519().unwrap();
|
||||
let x25519_pubkey_resp = *ed25519_keypair_resp.to_x25519().public_key();
|
||||
|
||||
// Test salt
|
||||
let salt = [52u8; 32];
|
||||
let receiver_index: u32 = 88888;
|
||||
@@ -1166,11 +1157,9 @@ mod tests {
|
||||
let mut initiator = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true, // is_initiator
|
||||
(
|
||||
ed25519_keypair_init.private_key(),
|
||||
ed25519_keypair_init.public_key(),
|
||||
),
|
||||
Arc::new(ed25519_keypair_init),
|
||||
ed25519_keypair_resp.public_key(),
|
||||
&x25519_pubkey_resp,
|
||||
&salt,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1178,11 +1167,9 @@ mod tests {
|
||||
let mut responder = LpStateMachine::new(
|
||||
receiver_index,
|
||||
false, // is_initiator
|
||||
(
|
||||
ed25519_keypair_resp.private_key(),
|
||||
ed25519_keypair_resp.public_key(),
|
||||
),
|
||||
ed25519_keypair_init.public_key(),
|
||||
Arc::new(ed25519_keypair_resp),
|
||||
&ed25519_pubkey_init,
|
||||
&x25519_pubkey_init,
|
||||
&salt,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1366,6 +1353,8 @@ mod tests {
|
||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([20u8; 32], 0);
|
||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([21u8; 32], 1);
|
||||
|
||||
let x25519_pubkey_init = *ed25519_keypair_init.to_x25519().public_key();
|
||||
|
||||
let salt = [53u8; 32];
|
||||
let receiver_index: u32 = 99901;
|
||||
|
||||
@@ -1373,11 +1362,9 @@ mod tests {
|
||||
let mut initiator = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
(
|
||||
ed25519_keypair_init.private_key(),
|
||||
ed25519_keypair_init.public_key(),
|
||||
),
|
||||
Arc::new(ed25519_keypair_init),
|
||||
ed25519_keypair_resp.public_key(),
|
||||
&x25519_pubkey_init,
|
||||
&salt,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1397,6 +1384,9 @@ mod tests {
|
||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([22u8; 32], 0);
|
||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([23u8; 32], 1);
|
||||
|
||||
let ed25519_pubkey_init = *ed25519_keypair_init.public_key();
|
||||
let x25519_pubkey_init = ed25519_pubkey_init.to_x25519().unwrap();
|
||||
|
||||
let salt = [54u8; 32];
|
||||
let receiver_index: u32 = 99902;
|
||||
|
||||
@@ -1404,11 +1394,9 @@ mod tests {
|
||||
let mut responder = LpStateMachine::new(
|
||||
receiver_index,
|
||||
false,
|
||||
(
|
||||
ed25519_keypair_resp.private_key(),
|
||||
ed25519_keypair_resp.public_key(),
|
||||
),
|
||||
ed25519_keypair_init.public_key(),
|
||||
Arc::new(ed25519_keypair_resp),
|
||||
&ed25519_pubkey_init,
|
||||
&x25519_pubkey_init,
|
||||
&salt,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1428,6 +1416,10 @@ mod tests {
|
||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([24u8; 32], 0);
|
||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([25u8; 32], 1);
|
||||
|
||||
let ed25519_pubkey_init = *ed25519_keypair_init.public_key();
|
||||
let x25519_pubkey_init = ed25519_pubkey_init.to_x25519().unwrap();
|
||||
let x25519_pubkey_resp = *ed25519_keypair_resp.to_x25519().public_key();
|
||||
|
||||
let salt = [55u8; 32];
|
||||
let receiver_index: u32 = 99903;
|
||||
|
||||
@@ -1435,11 +1427,9 @@ mod tests {
|
||||
let mut initiator = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
(
|
||||
ed25519_keypair_init.private_key(),
|
||||
ed25519_keypair_init.public_key(),
|
||||
),
|
||||
Arc::new(ed25519_keypair_init),
|
||||
ed25519_keypair_resp.public_key(),
|
||||
&x25519_pubkey_resp,
|
||||
&salt,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1447,11 +1437,9 @@ mod tests {
|
||||
let mut responder = LpStateMachine::new(
|
||||
receiver_index,
|
||||
false,
|
||||
(
|
||||
ed25519_keypair_resp.private_key(),
|
||||
ed25519_keypair_resp.public_key(),
|
||||
),
|
||||
ed25519_keypair_init.public_key(),
|
||||
Arc::new(ed25519_keypair_resp),
|
||||
&ed25519_pubkey_init,
|
||||
&x25519_pubkey_init,
|
||||
&salt,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1493,6 +1481,8 @@ mod tests {
|
||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([26u8; 32], 0);
|
||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([27u8; 32], 1);
|
||||
|
||||
let x25519_pubkey_resp = *ed25519_keypair_resp.to_x25519().public_key();
|
||||
|
||||
let salt = [56u8; 32];
|
||||
let receiver_index: u32 = 99904;
|
||||
|
||||
@@ -1500,11 +1490,9 @@ mod tests {
|
||||
let mut initiator = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
(
|
||||
ed25519_keypair_init.private_key(),
|
||||
ed25519_keypair_init.public_key(),
|
||||
),
|
||||
Arc::new(ed25519_keypair_init),
|
||||
ed25519_keypair_resp.public_key(),
|
||||
&x25519_pubkey_resp,
|
||||
&salt,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1525,6 +1513,8 @@ mod tests {
|
||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([28u8; 32], 0);
|
||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([29u8; 32], 1);
|
||||
|
||||
let x25519_pubkey_resp = *ed25519_keypair_resp.to_x25519().public_key();
|
||||
|
||||
let salt = [57u8; 32];
|
||||
let receiver_index: u32 = 99905;
|
||||
|
||||
@@ -1532,11 +1522,9 @@ mod tests {
|
||||
let mut initiator = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
(
|
||||
ed25519_keypair_init.private_key(),
|
||||
ed25519_keypair_init.public_key(),
|
||||
),
|
||||
Arc::new(ed25519_keypair_init),
|
||||
ed25519_keypair_resp.public_key(),
|
||||
&x25519_pubkey_resp,
|
||||
&salt,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1570,6 +1558,11 @@ mod tests {
|
||||
let ed25519_keypair_a = ed25519::KeyPair::from_secret([30u8; 32], 0);
|
||||
let ed25519_keypair_b = ed25519::KeyPair::from_secret([31u8; 32], 1);
|
||||
|
||||
let ed25519_pubkey_a = *ed25519_keypair_a.public_key();
|
||||
|
||||
let x25519_pubkey_a = *ed25519_keypair_a.to_x25519().public_key();
|
||||
let x25519_pubkey_b = *ed25519_keypair_b.to_x25519().public_key();
|
||||
|
||||
let salt = [60u8; 32];
|
||||
let receiver_index: u32 = 111111;
|
||||
|
||||
@@ -1577,11 +1570,9 @@ mod tests {
|
||||
let mut alice = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
(
|
||||
ed25519_keypair_a.private_key(),
|
||||
ed25519_keypair_a.public_key(),
|
||||
),
|
||||
Arc::new(ed25519_keypair_a),
|
||||
ed25519_keypair_b.public_key(),
|
||||
&x25519_pubkey_b,
|
||||
&salt,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1589,11 +1580,9 @@ mod tests {
|
||||
let mut bob = LpStateMachine::new(
|
||||
receiver_index,
|
||||
false,
|
||||
(
|
||||
ed25519_keypair_b.private_key(),
|
||||
ed25519_keypair_b.public_key(),
|
||||
),
|
||||
ed25519_keypair_a.public_key(),
|
||||
Arc::new(ed25519_keypair_b),
|
||||
&ed25519_pubkey_a,
|
||||
&x25519_pubkey_a,
|
||||
&salt,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -5,9 +5,10 @@ use super::messages::LpRegistrationRequest;
|
||||
use super::registration::process_registration;
|
||||
use super::LpHandlerState;
|
||||
use crate::error::GatewayError;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_lp::{
|
||||
codec::OuterAeadKey, keypair::PublicKey, message::ForwardPacketData, packet::LpHeader,
|
||||
LpMessage, LpPacket, OuterHeader,
|
||||
codec::OuterAeadKey, message::ForwardPacketData, packet::LpHeader, LpMessage, LpPacket,
|
||||
OuterHeader,
|
||||
};
|
||||
use nym_lp_transport::traits::LpTransport;
|
||||
use nym_metrics::{add_histogram_obs, inc};
|
||||
@@ -289,31 +290,11 @@ where
|
||||
async fn handle_client_hello(&mut self, packet: LpPacket) -> Result<(), GatewayError> {
|
||||
use nym_lp::packet::LpHeader;
|
||||
use nym_lp::state_machine::{LpInput, LpStateMachine};
|
||||
let remote = self.remote_addr;
|
||||
|
||||
// Extract ClientHello data
|
||||
let (receiver_index, client_ed25519_pubkey, salt) = match packet.message() {
|
||||
LpMessage::ClientHello(hello_data) => {
|
||||
// Validate timestamp
|
||||
let timestamp = hello_data.extract_timestamp();
|
||||
Self::validate_timestamp(
|
||||
timestamp,
|
||||
self.state.lp_config.debug.timestamp_tolerance,
|
||||
)?;
|
||||
|
||||
// Extract client-proposed receiver_index
|
||||
let receiver_index = hello_data.receiver_index;
|
||||
|
||||
let client_ed25519_pubkey = nym_crypto::asymmetric::ed25519::PublicKey::from_bytes(
|
||||
&hello_data.client_ed25519_public_key,
|
||||
)
|
||||
.map_err(|e| {
|
||||
GatewayError::LpProtocolError(
|
||||
format!("Invalid client Ed25519 public key: {e}",),
|
||||
)
|
||||
})?;
|
||||
|
||||
(receiver_index, client_ed25519_pubkey, hello_data.salt)
|
||||
}
|
||||
let hello_data = match packet.message() {
|
||||
LpMessage::ClientHello(hello_data) => hello_data,
|
||||
other => {
|
||||
inc!("lp_client_hello_failed");
|
||||
return Err(GatewayError::LpProtocolError(format!(
|
||||
@@ -322,20 +303,23 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
debug!(
|
||||
"Processing ClientHello from {} (proposed receiver_index={})",
|
||||
self.remote_addr, receiver_index
|
||||
);
|
||||
// Validate timestamp
|
||||
let timestamp = hello_data.extract_timestamp();
|
||||
Self::validate_timestamp(timestamp, self.state.lp_config.debug.timestamp_tolerance)?;
|
||||
|
||||
// Extract client-proposed receiver_index
|
||||
let receiver_index = hello_data.receiver_index;
|
||||
|
||||
// let client_ed25519_pubkey = hello_data.client_ed25519_public_key;
|
||||
|
||||
debug!("Processing ClientHello from {remote} (proposed receiver_index={receiver_index})",);
|
||||
|
||||
// Collision check for client-proposed receiver_index
|
||||
// Check both handshake_states (in-progress) and session_states (established)
|
||||
if self.state.handshake_states.contains_key(&receiver_index)
|
||||
|| self.state.session_states.contains_key(&receiver_index)
|
||||
{
|
||||
warn!(
|
||||
"Receiver index collision: {} from {}",
|
||||
receiver_index, self.remote_addr
|
||||
);
|
||||
warn!("Receiver index collision: {receiver_index} from {remote}",);
|
||||
inc!("lp_receiver_index_collision");
|
||||
|
||||
// Send Collision response to tell client to retry with new receiver_index
|
||||
@@ -351,32 +335,23 @@ where
|
||||
// Collision check passed - bind this connection to the receiver_index
|
||||
// All subsequent packets on this connection must use this receiver_index
|
||||
self.bound_receiver_idx = Some(receiver_index);
|
||||
trace!(
|
||||
"Bound connection from {} to receiver_idx={} (via ClientHello)",
|
||||
self.remote_addr,
|
||||
receiver_index
|
||||
);
|
||||
trace!("Bound connection from {remote} to receiver_idx={receiver_index} (via ClientHello)",);
|
||||
|
||||
// Create state machine for this handshake using client-proposed receiver_index
|
||||
let mut state_machine = LpStateMachine::new(
|
||||
receiver_index,
|
||||
false, // responder
|
||||
(
|
||||
self.state.local_identity.private_key(),
|
||||
self.state.local_identity.public_key(),
|
||||
),
|
||||
&client_ed25519_pubkey,
|
||||
&salt,
|
||||
self.state.local_identity.clone(),
|
||||
&hello_data.client_ed25519_public_key,
|
||||
&hello_data.client_lp_public_key,
|
||||
&hello_data.salt,
|
||||
)
|
||||
.map_err(|e| {
|
||||
inc!("lp_client_hello_failed");
|
||||
GatewayError::LpHandshakeError(format!("Failed to create state machine: {}", e))
|
||||
})?;
|
||||
|
||||
debug!(
|
||||
"Created handshake state for {} (receiver_index={})",
|
||||
self.remote_addr, receiver_index
|
||||
);
|
||||
debug!("Created handshake state for {remote} (receiver_index={receiver_index})",);
|
||||
|
||||
// Transition state machine to KKTExchange (responder waits for client's KKT request)
|
||||
// For responder, StartHandshake returns None (just transitions state)
|
||||
@@ -384,8 +359,7 @@ where
|
||||
if let Some(Err(e)) = state_machine.process_input(LpInput::StartHandshake) {
|
||||
inc!("lp_client_hello_failed");
|
||||
return Err(GatewayError::LpHandshakeError(format!(
|
||||
"StartHandshake failed: {}",
|
||||
e
|
||||
"StartHandshake failed: {e}",
|
||||
)));
|
||||
// Responder (gateway) gets Ok but no packet to send - we just wait for client's next packet
|
||||
}
|
||||
@@ -396,8 +370,7 @@ where
|
||||
.insert(receiver_index, super::TimestampedState::new(state_machine));
|
||||
|
||||
debug!(
|
||||
"Stored handshake state for {} (receiver_index={}) - waiting for KKT request",
|
||||
self.remote_addr, receiver_index
|
||||
"Stored handshake state for {remote} (receiver_index={receiver_index}) - waiting for KKT request",
|
||||
);
|
||||
|
||||
// Send Ack to confirm ClientHello received
|
||||
@@ -897,14 +870,7 @@ where
|
||||
#[allow(dead_code)]
|
||||
async fn receive_client_hello(
|
||||
&mut self,
|
||||
) -> Result<
|
||||
(
|
||||
PublicKey,
|
||||
nym_crypto::asymmetric::ed25519::PublicKey,
|
||||
[u8; 32],
|
||||
),
|
||||
GatewayError,
|
||||
> {
|
||||
) -> Result<(x25519::PublicKey, ed25519::PublicKey, [u8; 32]), GatewayError> {
|
||||
// Receive first packet which should be ClientHello (no outer encryption)
|
||||
let (raw_bytes, _header) = self.receive_raw_packet().await?;
|
||||
let packet = nym_lp::codec::parse_lp_packet(&raw_bytes, None)
|
||||
@@ -931,22 +897,11 @@ where
|
||||
self.state.lp_config.debug.timestamp_tolerance.as_secs()
|
||||
);
|
||||
|
||||
// Convert bytes to X25519 PublicKey (for Noise protocol)
|
||||
let client_pubkey = PublicKey::from_bytes(&hello_data.client_lp_public_key)
|
||||
.map_err(|e| {
|
||||
GatewayError::LpProtocolError(format!("Invalid client public key: {}", e))
|
||||
})?;
|
||||
// Retrieve X25519 PublicKey (for Noise protocol)
|
||||
let client_pubkey = hello_data.client_lp_public_key;
|
||||
|
||||
// Convert bytes to Ed25519 PublicKey (for PSQ authentication)
|
||||
let client_ed25519_pubkey = nym_crypto::asymmetric::ed25519::PublicKey::from_bytes(
|
||||
&hello_data.client_ed25519_public_key,
|
||||
)
|
||||
.map_err(|e| {
|
||||
GatewayError::LpProtocolError(format!(
|
||||
"Invalid client Ed25519 public key: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
// Retrieve Ed25519 PublicKey (for PSQ authentication)
|
||||
let client_ed25519_pubkey = hello_data.client_ed25519_public_key;
|
||||
|
||||
// Extract salt for PSK derivation
|
||||
let salt = hello_data.salt;
|
||||
@@ -1705,8 +1660,13 @@ mod tests {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
let client_key = [7u8; 32];
|
||||
let client_ed25519_key = [8u8; 32];
|
||||
let mut rng = rand::thread_rng();
|
||||
let ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||
let x25519 = ed25519.to_x25519();
|
||||
|
||||
let client_key = *x25519.public_key();
|
||||
let client_ed25519_key = *ed25519.public_key();
|
||||
|
||||
let hello_data =
|
||||
ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
||||
let expected_salt = hello_data.salt; // Clone salt before moving hello_data
|
||||
@@ -1777,8 +1737,8 @@ mod tests {
|
||||
let client_x25519_public = client_ed25519_keypair.public_key().to_x25519().unwrap();
|
||||
|
||||
let hello_data = ClientHelloData::new_with_fresh_salt(
|
||||
client_x25519_public.to_bytes(),
|
||||
client_ed25519_keypair.public_key().to_bytes(),
|
||||
client_x25519_public,
|
||||
*client_ed25519_keypair.public_key(),
|
||||
timestamp,
|
||||
);
|
||||
let packet = LpPacket::new(
|
||||
@@ -1838,8 +1798,8 @@ mod tests {
|
||||
let client_x25519_public = client_ed25519_keypair.public_key().to_x25519().unwrap();
|
||||
|
||||
let mut hello_data = ClientHelloData::new_with_fresh_salt(
|
||||
client_x25519_public.to_bytes(),
|
||||
client_ed25519_keypair.public_key().to_bytes(),
|
||||
client_x25519_public,
|
||||
*client_ed25519_keypair.public_key(),
|
||||
timestamp,
|
||||
);
|
||||
|
||||
|
||||
@@ -328,6 +328,10 @@ where
|
||||
LpClientError::Crypto(format!("Failed to derive X25519 public key: {e}"))
|
||||
})?;
|
||||
|
||||
let gateway_x25519_public = self.gateway_ed25519_public_key.to_x25519().map_err(|e| {
|
||||
LpClientError::Crypto(format!("Failed to derive X25519 public key: {e}"))
|
||||
})?;
|
||||
|
||||
let timestamp = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|_| LpClientError::Other("System time before UNIX epoch".into()))?
|
||||
@@ -335,8 +339,8 @@ where
|
||||
|
||||
// Step 2: Generate ClientHelloData with fresh salt and both public keys
|
||||
let client_hello_data = nym_lp::ClientHelloData::new_with_fresh_salt(
|
||||
client_x25519_public.to_bytes(),
|
||||
self.local_ed25519_keypair.public_key().to_bytes(),
|
||||
client_x25519_public,
|
||||
*self.local_ed25519_keypair.public_key(),
|
||||
timestamp,
|
||||
);
|
||||
let salt = client_hello_data.salt;
|
||||
@@ -381,11 +385,9 @@ where
|
||||
let mut state_machine = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true, // is_initiator
|
||||
(
|
||||
self.local_ed25519_keypair.private_key(),
|
||||
self.local_ed25519_keypair.public_key(),
|
||||
),
|
||||
self.local_ed25519_keypair.clone(),
|
||||
&self.gateway_ed25519_public_key,
|
||||
&gateway_x25519_public,
|
||||
&salt,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -129,6 +129,10 @@ impl NestedLpSession {
|
||||
LpClientError::Crypto(format!("Failed to derive X25519 public key: {}", e))
|
||||
})?;
|
||||
|
||||
let gateway_x25519_public = self.exit_public_key.to_x25519().map_err(|e| {
|
||||
LpClientError::Crypto(format!("Failed to derive X25519 public key: {e}"))
|
||||
})?;
|
||||
|
||||
let timestamp = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|_| LpClientError::Other("System time before UNIX epoch".into()))?
|
||||
@@ -136,8 +140,8 @@ impl NestedLpSession {
|
||||
|
||||
// Step 2: Generate ClientHello for exit gateway
|
||||
let client_hello_data = nym_lp::ClientHelloData::new_with_fresh_salt(
|
||||
client_x25519_public.to_bytes(),
|
||||
self.client_keypair.public_key().to_bytes(),
|
||||
client_x25519_public,
|
||||
*self.client_keypair.public_key(),
|
||||
timestamp,
|
||||
);
|
||||
let salt = client_hello_data.salt;
|
||||
@@ -191,11 +195,9 @@ impl NestedLpSession {
|
||||
let mut state_machine = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true, // is_initiator
|
||||
(
|
||||
self.client_keypair.private_key(),
|
||||
self.client_keypair.public_key(),
|
||||
),
|
||||
self.client_keypair.clone(),
|
||||
&self.exit_public_key,
|
||||
&gateway_x25519_public,
|
||||
&salt,
|
||||
)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user