moved the e2e test to nym-lp

This commit is contained in:
Jędrzej Stuczyński
2026-02-13 14:31:11 +00:00
parent 9d021481d0
commit ad389c4fba
8 changed files with 950 additions and 987 deletions
+3 -2
View File
@@ -174,8 +174,9 @@ members = [
"wasm/node-tester",
"wasm/zknym-lib",
# "nym-gateway-probe",
"integration-tests", "common/nym-lp-transport", "common/nym-kkt-ciphersuite",
"common/nym-lp-sandbox"
"integration-tests",
"common/nym-lp-transport",
"common/nym-kkt-ciphersuite",
]
default-members = [
+2 -2
View File
@@ -50,9 +50,9 @@ impl<'a> KKTResponder<'a> {
})
} else if mlkem_encapsulation_key.is_none() && mceliece_encapsulation_key.is_none()
{
return Err(KKTError::FunctionInputError {
Err(KKTError::FunctionInputError {
info: "Did not provide an encapsulation key when instanciating a KKTResponder.",
});
})
} else {
Ok(Self {
x25519_keypair,
-42
View File
@@ -1,42 +0,0 @@
[package]
name = "nym-lp-sandbox"
version = "0.1.0"
edition = { workspace = true }
license = { workspace = true }
publish = false
[dependencies]
thiserror = { workspace = true }
parking_lot = { workspace = true }
snow = { workspace = true }
bs58 = { workspace = true }
serde = { workspace = true }
bytes = { workspace = true }
dashmap = { workspace = true }
sha2 = { workspace = true }
tracing = { workspace = true }
rand = { workspace = true }
rand09 = { workspace = true }
nym-crypto = { path = "../crypto", features = ["hashing", "asymmetric"] }
nym-kkt = { path = "../nym-kkt" }
nym-lp-common = { path = "../nym-lp-common" }
nym-kkt-ciphersuite = { path ="../nym-kkt-ciphersuite" }
# libcrux dependencies for PSQ (Post-Quantum PSK derivation)
libcrux-psq = { workspace = true, features = ["test-utils"] }
libcrux-kem = { workspace = true }
tls_codec = { workspace = true }
num_enum = { workspace = true }
chacha20poly1305 = { workspace = true }
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"] }
nym-test-utils = { workspace = true }
-262
View File
@@ -1,262 +0,0 @@
#[cfg(test)]
mod tests {
use libcrux_psq::{
Channel, IntoSession,
handshake::{
builders::{CiphersuiteBuilder, PrincipalBuilder},
ciphersuites::CiphersuiteName,
types::{Authenticator, PQEncapsulationKey},
},
session::{Session, SessionBinding},
};
use nym_kkt::{
initiator::KKTInitiator,
key_utils::{
generate_keypair_mceliece, generate_keypair_mlkem, generate_keypair_x25519,
hash_encapsulation_key,
},
responder::KKTResponder,
};
use nym_kkt_ciphersuite::{Ciphersuite, HashFunction, HashLength, KEM, SignatureScheme};
#[test]
fn test_e2e_client_node() {
let mut rng = rand09::rng();
// we should add these as consts
let aad_initiator_outer = b"Test Data I Outer";
let aad_initiator_inner = b"Test Data I Inner";
let aad_responder = b"Test Data R";
let ctx = b"Test Context";
// generate responder x25519 keys
let responder_x25519_keypair = generate_keypair_x25519(&mut rng);
let hash_function = HashFunction::Blake3;
// generate kem public keys
let responder_mlkem_keypair = generate_keypair_mlkem(&mut rng);
let responder_mceliece_keypair = generate_keypair_mceliece(&mut rng);
let r_dir_hash_mlkem = hash_encapsulation_key(
// &ciphersuite.hash_function(),
&hash_function,
// ciphersuite.hash_len(),
HashLength::Default.value(),
responder_mlkem_keypair.1.as_slice().as_slice(),
);
let _r_dir_hash_mceliece = hash_encapsulation_key(
// &ciphersuite.hash_function(),
&hash_function,
// ciphersuite.hash_len(),
HashLength::Default.value(),
responder_mceliece_keypair.1.as_ref(),
);
let kkt_responder = KKTResponder::new(
&responder_x25519_keypair,
Some(&responder_mlkem_keypair.1),
Some(&responder_mceliece_keypair.1),
&[
HashFunction::Blake3,
HashFunction::SHA256,
HashFunction::Shake128,
HashFunction::Shake256,
],
&[1],
&[SignatureScheme::Ed25519],
)
.unwrap();
// OneWay - MlKem
let psq_ciphersuite = CiphersuiteName::X25519_MLKEM768_X25519_AESGCM128_HKDFSHA256;
let responder_ciphersuite = CiphersuiteBuilder::new(psq_ciphersuite)
.longterm_x25519_keys(&responder_x25519_keypair)
.longterm_mlkem_encapsulation_key(&responder_mlkem_keypair.1)
.longterm_mlkem_decapsulation_key(&responder_mlkem_keypair.0)
.build_responder_ciphersuite()
.unwrap();
let mut responder = PrincipalBuilder::new(rand09::rng())
.context(ctx)
.outer_aad(aad_responder)
.recent_keys_upper_bound(30)
.build_responder(responder_ciphersuite)
.unwrap();
let ciphersuite = Ciphersuite::resolve_ciphersuite(
KEM::MlKem768,
hash_function,
SignatureScheme::Ed25519,
None,
)
.unwrap();
let (mut initiator, request_bytes) = KKTInitiator::generate_one_way_request(
&mut rng,
&ciphersuite,
&responder_x25519_keypair.pk,
&r_dir_hash_mlkem,
1u8,
)
.unwrap();
let (response_bytes, _) = kkt_responder.process_request(&request_bytes).unwrap();
let (i_obtained_key, _) = initiator.process_response(&response_bytes).unwrap();
assert_eq!(
i_obtained_key,
responder_mlkem_keypair.1.as_slice().as_slice(),
);
let mlkem_key =
libcrux_kem::MlKem768PublicKey::try_from(i_obtained_key.as_slice()).unwrap();
let initiator_psq_keys = generate_keypair_x25519(&mut rng);
let initiator_cbuilder = CiphersuiteBuilder::new(psq_ciphersuite)
.longterm_x25519_keys(&initiator_psq_keys)
.peer_longterm_x25519_pk(&responder_x25519_keypair.pk)
.peer_longterm_mlkem_pk(&mlkem_key);
let initiator_ciphersuite = initiator_cbuilder.build_initiator_ciphersuite().unwrap();
let mut msg_channel = vec![0u8; 8192];
let mut payload_buf_responder = vec![0u8; 4096];
let mut payload_buf_initiator = vec![0u8; 4096];
let mut initiator = PrincipalBuilder::new(rand09::rng())
.outer_aad(aad_initiator_outer)
.inner_aad(aad_initiator_inner)
.context(ctx)
.build_registration_initiator(initiator_ciphersuite)
.unwrap();
// Send first message
let registration_payload_initiator = b"Registration_init";
let len_i = initiator
.write_message(registration_payload_initiator, &mut msg_channel)
.unwrap();
// Read first message
let (len_r_deserialized, len_r_payload) = responder
.read_message(&msg_channel, &mut payload_buf_responder)
.unwrap();
// We read the same amount of data.
assert_eq!(len_r_deserialized, len_i);
assert_eq!(len_r_payload, registration_payload_initiator.len());
assert_eq!(
&payload_buf_responder[0..len_r_payload],
registration_payload_initiator
);
// Get the authenticator out here, so we can deserialize the session later.
let Some(initiator_authenticator) = responder.initiator_authenticator() else {
panic!("No initiator authenticator found")
};
// Respond
let registration_payload_responder = b"Registration_respond";
let len_r = responder
.write_message(registration_payload_responder, &mut msg_channel)
.unwrap();
// Finalize on registration initiator
let (len_i_deserialized, len_i_payload) = initiator
.read_message(&msg_channel, &mut payload_buf_initiator)
.unwrap();
// We read the same amount of data.
assert_eq!(len_r, len_i_deserialized);
assert_eq!(registration_payload_responder.len(), len_i_payload);
assert_eq!(
&payload_buf_initiator[0..len_i_payload],
registration_payload_responder
);
// Ready for transport mode
assert!(initiator.is_handshake_finished());
assert!(responder.is_handshake_finished());
let i_transport = initiator.into_session().unwrap();
let r_transport = responder.into_session().unwrap();
// test serialization, deserialization
let mut session_storage = vec![0u8; 4096];
i_transport
.serialize(
&mut session_storage,
SessionBinding {
initiator_authenticator: &Authenticator::Dh(initiator_psq_keys.pk),
responder_ecdh_pk: &responder_x25519_keypair.pk,
responder_pq_pk: Some(PQEncapsulationKey::MlKem(&mlkem_key)),
},
)
.unwrap();
let mut i_transport = Session::deserialize(
&session_storage,
SessionBinding {
initiator_authenticator: &Authenticator::Dh(initiator_psq_keys.pk),
responder_ecdh_pk: &responder_x25519_keypair.pk,
responder_pq_pk: Some(PQEncapsulationKey::MlKem(&mlkem_key)),
},
)
.unwrap();
r_transport
.serialize(
&mut session_storage,
SessionBinding {
initiator_authenticator: &initiator_authenticator,
responder_ecdh_pk: &responder_x25519_keypair.pk,
responder_pq_pk: Some(PQEncapsulationKey::MlKem(&mlkem_key)),
},
)
.unwrap();
let mut r_transport = Session::deserialize(
&session_storage,
SessionBinding {
initiator_authenticator: &initiator_authenticator,
responder_ecdh_pk: &responder_x25519_keypair.pk,
responder_pq_pk: Some(PQEncapsulationKey::MlKem(&mlkem_key)),
},
)
.unwrap();
let mut channel_i = i_transport.transport_channel().unwrap();
let mut channel_r = r_transport.transport_channel().unwrap();
assert_eq!(channel_i.identifier(), channel_r.identifier());
let app_data_i = b"Derived session hey".as_slice();
let app_data_r = b"Derived session ho".as_slice();
let len_i = channel_i
.write_message(app_data_i, &mut msg_channel)
.unwrap();
let (len_r_deserialized, len_r_payload) = channel_r
.read_message(&msg_channel, &mut payload_buf_responder)
.unwrap();
// We read the same amount of data.
assert_eq!(len_r_deserialized, len_i);
assert_eq!(len_r_payload, app_data_i.len());
assert_eq!(&payload_buf_responder[0..len_r_payload], app_data_i);
let len_r = channel_r
.write_message(app_data_r, &mut msg_channel)
.unwrap();
let (len_i_deserialized, len_i_payload) = channel_i
.read_message(&msg_channel, &mut payload_buf_initiator)
.unwrap();
assert_eq!(len_r, len_i_deserialized);
assert_eq!(app_data_r.len(), len_i_payload);
assert_eq!(&payload_buf_initiator[0..len_i_payload], app_data_r);
}
}
+219 -216
View File
@@ -34,9 +34,10 @@ pub const NOISE_PATTERN: &str = "Noise_XKpsk3_25519_ChaChaPoly_SHA256";
pub const NOISE_PSK_INDEX: u8 = 3;
#[cfg(test)]
pub fn kem_list() -> Vec<nym_kkt::ciphersuite::KEM> {
use nym_kkt::ciphersuite::KEM;
vec![KEM::MlKem768, KEM::McEliece, KEM::X25519]
pub fn kem_list() -> Vec<nym_kkt_ciphersuite::KEM> {
todo!()
// use nym_kkt::ciphersuite::KEM;
// vec![KEM::MlKem768, KEM::McEliece, KEM::X25519]
}
#[cfg(any(feature = "mock", test))]
@@ -48,106 +49,107 @@ pub struct SessionsMock {
#[cfg(any(feature = "mock", test))]
impl SessionsMock {
pub fn mock_post_handshake(session_id: u32) -> SessionsMock {
use crate::peer::mock_peers;
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey};
let (init, resp) = mock_peers();
let resp_remote = resp.as_remote();
let init_remote = init.as_remote();
let salt = [42u8; 32];
let session_id_bytes = session_id.to_le_bytes();
// skip KKT by just deriving the kem key locally
let kem_keys = resp.kem_psq.as_ref().unwrap();
let libcrux_private_key = libcrux_kem::PrivateKey::decode(
libcrux_kem::Algorithm::X25519,
kem_keys.private_key().as_bytes(),
)
.unwrap();
let decapsulation_key = DecapsulationKey::X25519(libcrux_private_key);
let libcrux_public_key = libcrux_kem::PublicKey::decode(
libcrux_kem::Algorithm::X25519,
kem_keys.public_key().as_bytes(),
)
.unwrap();
let encapsulation_key = EncapsulationKey::X25519(libcrux_public_key);
// INIT -> RESP: PSQ MSG1
let psq_initiator = crate::psk::psq_initiator_create_message(
init.x25519.private_key(),
&resp_remote.x25519_public,
&encapsulation_key,
init.ed25519.private_key(),
init.ed25519.public_key(),
&salt,
&session_id_bytes,
)
.unwrap();
let psk = psq_initiator.psk;
let psq_payload = psq_initiator.payload;
let outer_aead_key = crate::codec::OuterAeadKey::from_psk(&psk);
let noise_state_init = snow::Builder::new(crate::noise_protocol::NoiseProtocol::params())
.local_private_key(init.x25519().private_key().as_bytes())
.remote_public_key(resp_remote.x25519_public.as_bytes())
.psk(crate::NOISE_PSK_INDEX, &psk)
.build_initiator()
.unwrap();
let mut noise_protocol_init = crate::noise_protocol::NoiseProtocol::new(noise_state_init);
let noise_msg1 = noise_protocol_init.get_bytes_to_send().unwrap().unwrap();
let psq_responder = crate::psk::psq_responder_process_message(
resp.x25519.private_key(),
&init_remote.x25519_public,
(&decapsulation_key, &encapsulation_key),
&init_remote.ed25519_public,
&psq_payload,
&salt,
&session_id_bytes,
)
.unwrap();
let noise_state_resp = snow::Builder::new(crate::noise_protocol::NoiseProtocol::params())
.local_private_key(resp.x25519().private_key().as_bytes())
.remote_public_key(init_remote.x25519_public.as_bytes())
.psk(crate::NOISE_PSK_INDEX, &psk)
.build_responder()
.unwrap();
let mut noise_protocol_resp = crate::noise_protocol::NoiseProtocol::new(noise_state_resp);
noise_protocol_resp.read_message(&noise_msg1).unwrap();
let noise_msg2 = noise_protocol_resp.get_bytes_to_send().unwrap().unwrap();
noise_protocol_init.read_message(&noise_msg2).unwrap();
let noise_msg3 = noise_protocol_init.get_bytes_to_send().unwrap().unwrap();
assert!(noise_protocol_init.is_handshake_finished());
noise_protocol_resp.read_message(&noise_msg3).unwrap();
assert!(noise_protocol_resp.is_handshake_finished());
SessionsMock {
initiator: LpSession::new(
session_id,
1,
outer_aead_key.clone(),
init,
resp_remote,
crate::session::PqSharedSecret::new(psq_initiator.pq_shared_secret),
noise_protocol_init,
),
responder: LpSession::new(
session_id,
1,
outer_aead_key,
resp,
init_remote,
crate::session::PqSharedSecret::new(psq_responder.pq_shared_secret),
noise_protocol_resp,
),
}
todo!()
// use crate::peer::mock_peers;
// use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey};
//
// let (init, resp) = mock_peers();
// let resp_remote = resp.as_remote();
// let init_remote = init.as_remote();
// let salt = [42u8; 32];
// let session_id_bytes = session_id.to_le_bytes();
//
// // skip KKT by just deriving the kem key locally
// let kem_keys = resp.kem_psq.as_ref().unwrap();
//
// let libcrux_private_key = libcrux_kem::PrivateKey::decode(
// libcrux_kem::Algorithm::X25519,
// kem_keys.private_key().as_bytes(),
// )
// .unwrap();
// let decapsulation_key = DecapsulationKey::X25519(libcrux_private_key);
//
// let libcrux_public_key = libcrux_kem::PublicKey::decode(
// libcrux_kem::Algorithm::X25519,
// kem_keys.public_key().as_bytes(),
// )
// .unwrap();
// let encapsulation_key = EncapsulationKey::X25519(libcrux_public_key);
//
// // INIT -> RESP: PSQ MSG1
// let psq_initiator = crate::psk::psq_initiator_create_message(
// init.x25519.private_key(),
// &resp_remote.x25519_public,
// &encapsulation_key,
// init.ed25519.private_key(),
// init.ed25519.public_key(),
// &salt,
// &session_id_bytes,
// )
// .unwrap();
//
// let psk = psq_initiator.psk;
// let psq_payload = psq_initiator.payload;
// let outer_aead_key = crate::codec::OuterAeadKey::from_psk(&psk);
//
// let noise_state_init = snow::Builder::new(crate::noise_protocol::NoiseProtocol::params())
// .local_private_key(init.x25519().private_key().as_bytes())
// .remote_public_key(resp_remote.x25519_public.as_bytes())
// .psk(crate::NOISE_PSK_INDEX, &psk)
// .build_initiator()
// .unwrap();
// let mut noise_protocol_init = crate::noise_protocol::NoiseProtocol::new(noise_state_init);
// let noise_msg1 = noise_protocol_init.get_bytes_to_send().unwrap().unwrap();
//
// let psq_responder = crate::psk::psq_responder_process_message(
// resp.x25519.private_key(),
// &init_remote.x25519_public,
// (&decapsulation_key, &encapsulation_key),
// &init_remote.ed25519_public,
// &psq_payload,
// &salt,
// &session_id_bytes,
// )
// .unwrap();
//
// let noise_state_resp = snow::Builder::new(crate::noise_protocol::NoiseProtocol::params())
// .local_private_key(resp.x25519().private_key().as_bytes())
// .remote_public_key(init_remote.x25519_public.as_bytes())
// .psk(crate::NOISE_PSK_INDEX, &psk)
// .build_responder()
// .unwrap();
// let mut noise_protocol_resp = crate::noise_protocol::NoiseProtocol::new(noise_state_resp);
// noise_protocol_resp.read_message(&noise_msg1).unwrap();
//
// let noise_msg2 = noise_protocol_resp.get_bytes_to_send().unwrap().unwrap();
// noise_protocol_init.read_message(&noise_msg2).unwrap();
// let noise_msg3 = noise_protocol_init.get_bytes_to_send().unwrap().unwrap();
//
// assert!(noise_protocol_init.is_handshake_finished());
//
// noise_protocol_resp.read_message(&noise_msg3).unwrap();
// assert!(noise_protocol_resp.is_handshake_finished());
//
// SessionsMock {
// initiator: LpSession::new(
// session_id,
// 1,
// outer_aead_key.clone(),
// init,
// resp_remote,
// crate::session::PqSharedSecret::new(psq_initiator.pq_shared_secret),
// noise_protocol_init,
// ),
// responder: LpSession::new(
// session_id,
// 1,
// outer_aead_key,
// resp,
// init_remote,
// crate::session::PqSharedSecret::new(psq_responder.pq_shared_secret),
// noise_protocol_resp,
// ),
// }
}
// we just need a dummy 'valid' session for simpler tests
@@ -174,7 +176,7 @@ mod tests {
use crate::session_manager::SessionManager;
use crate::{LpError, SessionsMock, kem_list, mock_session_for_test};
use bytes::BytesMut;
use nym_kkt::ciphersuite::{Ciphersuite, HashFunction, SignatureScheme};
use nym_kkt_ciphersuite::{Ciphersuite, HashFunction, SignatureScheme};
// Import the new standalone functions
use crate::codec::{parse_lp_packet, serialize_lp_packet};
@@ -288,118 +290,119 @@ mod tests {
let mut remote_manager = SessionManager::new();
for kem in kem_list() {
// Generate Ed25519 keypairs for PSQ authentication
let (init, resp) = mock_peers(kem);
let mut ciphersuite = Ciphersuite::resolve_ciphersuite(
kem,
HashFunction::Blake3,
SignatureScheme::Ed25519,
None,
);
// Use fixed receiver_index for deterministic test
let receiver_index: u32 = 54321;
let sessions = SessionsMock::mock_post_handshake(receiver_index);
let local_session = sessions.initiator;
let remote_session = sessions.responder;
// Create a session via manager
let _ = local_manager.create_session_state_machine(local_session);
let _ = remote_manager.create_session_state_machine(remote_session);
// === Packet 1 (Counter 0 - Should succeed) ===
let packet1 = LpPacket {
header: LpHeader {
protocol_version: 1,
reserved: [0u8; 3],
receiver_idx: receiver_index,
counter: 0,
},
message: LpMessage::Busy,
trailer: [0u8; TRAILER_LEN],
};
// Serialize
let mut buf1 = BytesMut::new();
serialize_lp_packet(&packet1, &mut buf1, None).unwrap();
// Parse
let parsed_packet1 = parse_lp_packet(&buf1, None).unwrap();
// Process via SessionManager method (which should handle checks + marking)
// NOTE: We might need a method on SessionManager/LpSession like `process_incoming_packet`
// that encapsulates parse -> check -> process_noise -> mark.
// For now, we simulate the steps using the retrieved session.
// Perform replay check
local_manager
.receiving_counter_quick_check(receiver_index, parsed_packet1.header.counter)
.expect("Packet 1 check failed");
// Mark received
local_manager
.receiving_counter_mark(receiver_index, parsed_packet1.header.counter)
.expect("Packet 1 mark failed");
// === Packet 2 (Counter 1 - Should succeed on same session) ===
let packet2 = LpPacket {
header: LpHeader {
protocol_version: 1,
reserved: [0u8; 3],
receiver_idx: receiver_index,
counter: 1,
},
message: LpMessage::Busy,
trailer: [0u8; TRAILER_LEN],
};
// Serialize
let mut buf2 = BytesMut::new();
serialize_lp_packet(&packet2, &mut buf2, None).unwrap();
// Parse
let parsed_packet2 = parse_lp_packet(&buf2, None).unwrap();
// Perform replay check
local_manager
.receiving_counter_quick_check(receiver_index, parsed_packet2.header.counter)
.expect("Packet 2 check failed");
// Mark received
local_manager
.receiving_counter_mark(receiver_index, parsed_packet2.header.counter)
.expect("Packet 2 mark failed");
// === Packet 3 (Counter 0 - Replay, should fail check) ===
let packet3 = LpPacket {
header: LpHeader {
protocol_version: 1,
reserved: [0u8; 3],
receiver_idx: receiver_index,
counter: 0, // Replay of first packet
},
message: LpMessage::Busy,
trailer: [0u8; TRAILER_LEN],
};
// Serialize
let mut buf3 = BytesMut::new();
serialize_lp_packet(&packet3, &mut buf3, None).unwrap();
// Parse
let parsed_packet3 = parse_lp_packet(&buf3, None).unwrap();
// Perform replay check (should fail)
let replay_result = local_manager
.receiving_counter_quick_check(receiver_index, parsed_packet3.header.counter);
assert!(replay_result.is_err());
match replay_result.unwrap_err() {
LpError::Replay(e) => {
assert!(matches!(e, crate::replay::ReplayError::DuplicateCounter));
}
e => panic!("Expected replay error for packet 3, got {:?}", e),
}
// Do not mark received
todo!()
// // Generate Ed25519 keypairs for PSQ authentication
// let (init, resp) = mock_peers(kem);
//
// let mut ciphersuite = Ciphersuite::resolve_ciphersuite(
// kem,
// HashFunction::Blake3,
// SignatureScheme::Ed25519,
// None,
// );
//
// // Use fixed receiver_index for deterministic test
// let receiver_index: u32 = 54321;
//
// let sessions = SessionsMock::mock_post_handshake(receiver_index);
// let local_session = sessions.initiator;
// let remote_session = sessions.responder;
//
// // Create a session via manager
// let _ = local_manager.create_session_state_machine(local_session);
// let _ = remote_manager.create_session_state_machine(remote_session);
//
// // === Packet 1 (Counter 0 - Should succeed) ===
// let packet1 = LpPacket {
// header: LpHeader {
// protocol_version: 1,
// reserved: [0u8; 3],
// receiver_idx: receiver_index,
// counter: 0,
// },
// message: LpMessage::Busy,
// trailer: [0u8; TRAILER_LEN],
// };
//
// // Serialize
// let mut buf1 = BytesMut::new();
// serialize_lp_packet(&packet1, &mut buf1, None).unwrap();
//
// // Parse
// let parsed_packet1 = parse_lp_packet(&buf1, None).unwrap();
//
// // Process via SessionManager method (which should handle checks + marking)
// // NOTE: We might need a method on SessionManager/LpSession like `process_incoming_packet`
// // that encapsulates parse -> check -> process_noise -> mark.
// // For now, we simulate the steps using the retrieved session.
//
// // Perform replay check
// local_manager
// .receiving_counter_quick_check(receiver_index, parsed_packet1.header.counter)
// .expect("Packet 1 check failed");
// // Mark received
// local_manager
// .receiving_counter_mark(receiver_index, parsed_packet1.header.counter)
// .expect("Packet 1 mark failed");
//
// // === Packet 2 (Counter 1 - Should succeed on same session) ===
// let packet2 = LpPacket {
// header: LpHeader {
// protocol_version: 1,
// reserved: [0u8; 3],
// receiver_idx: receiver_index,
// counter: 1,
// },
// message: LpMessage::Busy,
// trailer: [0u8; TRAILER_LEN],
// };
//
// // Serialize
// let mut buf2 = BytesMut::new();
// serialize_lp_packet(&packet2, &mut buf2, None).unwrap();
//
// // Parse
// let parsed_packet2 = parse_lp_packet(&buf2, None).unwrap();
//
// // Perform replay check
// local_manager
// .receiving_counter_quick_check(receiver_index, parsed_packet2.header.counter)
// .expect("Packet 2 check failed");
// // Mark received
// local_manager
// .receiving_counter_mark(receiver_index, parsed_packet2.header.counter)
// .expect("Packet 2 mark failed");
//
// // === Packet 3 (Counter 0 - Replay, should fail check) ===
// let packet3 = LpPacket {
// header: LpHeader {
// protocol_version: 1,
// reserved: [0u8; 3],
// receiver_idx: receiver_index,
// counter: 0, // Replay of first packet
// },
// message: LpMessage::Busy,
// trailer: [0u8; TRAILER_LEN],
// };
//
// // Serialize
// let mut buf3 = BytesMut::new();
// serialize_lp_packet(&packet3, &mut buf3, None).unwrap();
//
// // Parse
// let parsed_packet3 = parse_lp_packet(&buf3, None).unwrap();
//
// // Perform replay check (should fail)
// let replay_result = local_manager
// .receiving_counter_quick_check(receiver_index, parsed_packet3.header.counter);
// assert!(replay_result.is_err());
// match replay_result.unwrap_err() {
// LpError::Replay(e) => {
// assert!(matches!(e, crate::replay::ReplayError::DuplicateCounter));
// }
// e => panic!("Expected replay error for packet 3, got {:?}", e),
// }
// // Do not mark received
}
}
}
+239 -235
View File
@@ -517,268 +517,272 @@ mod tests {
#[test]
fn test_psk_derivation_is_symmetric() {
let keypair_1 = generate_x25519_keypair();
let keypair_2 = generate_x25519_keypair();
let salt = [2u8; 32];
let mut rng = &mut rand09::rng();
let (_kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
let enc_key = EncapsulationKey::X25519(kem_pk);
let dec_key = DecapsulationKey::X25519(_kem_sk);
// Client derives PSK
let (client_psk, ciphertext) =
derive_psk_with_psq_initiator(keypair_1.sk(), &keypair_2.pk, &enc_key, &salt).unwrap();
// Gateway derives PSK from their perspective
let gateway_psk = derive_psk_with_psq_responder(
keypair_2.sk(),
&keypair_1.pk,
(&dec_key, &enc_key),
&ciphertext,
&salt,
)
.unwrap();
assert_eq!(
client_psk, gateway_psk,
"Both sides should derive identical PSK"
);
todo!()
// let keypair_1 = generate_x25519_keypair();
// let keypair_2 = generate_x25519_keypair();
// let salt = [2u8; 32];
//
// let mut rng = &mut rand09::rng();
// let (_kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
// let enc_key = EncapsulationKey::X25519(kem_pk);
// let dec_key = DecapsulationKey::X25519(_kem_sk);
//
// // Client derives PSK
// let (client_psk, ciphertext) =
// derive_psk_with_psq_initiator(keypair_1.sk(), &keypair_2.pk, &enc_key, &salt).unwrap();
//
// // Gateway derives PSK from their perspective
// let gateway_psk = derive_psk_with_psq_responder(
// keypair_2.sk(),
// &keypair_1.pk,
// (&dec_key, &enc_key),
// &ciphertext,
// &salt,
// )
// .unwrap();
//
// assert_eq!(
// client_psk, gateway_psk,
// "Both sides should derive identical PSK"
// );
}
#[test]
fn test_different_salts_produce_different_psks() {
let keypair_1 = generate_x25519_keypair();
let keypair_2 = generate_x25519_keypair();
let salt1 = [1u8; 32];
let salt2 = [2u8; 32];
let mut rng = &mut rand09::rng();
let (_kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
let enc_key = EncapsulationKey::X25519(kem_pk);
let psk1 =
derive_psk_with_psq_initiator(keypair_1.sk(), &keypair_2.pk, &enc_key, &salt1).unwrap();
let psk2 =
derive_psk_with_psq_initiator(keypair_1.sk(), &keypair_2.pk, &enc_key, &salt2).unwrap();
assert_ne!(psk1, psk2, "Different salts should produce different PSKs");
todo!()
// let keypair_1 = generate_x25519_keypair();
// let keypair_2 = generate_x25519_keypair();
//
// let salt1 = [1u8; 32];
// let salt2 = [2u8; 32];
// let mut rng = &mut rand09::rng();
// let (_kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
// let enc_key = EncapsulationKey::X25519(kem_pk);
//
// let psk1 =
// derive_psk_with_psq_initiator(keypair_1.sk(), &keypair_2.pk, &enc_key, &salt1).unwrap();
// let psk2 =
// derive_psk_with_psq_initiator(keypair_1.sk(), &keypair_2.pk, &enc_key, &salt2).unwrap();
//
// assert_ne!(psk1, psk2, "Different salts should produce different PSKs");
}
#[test]
fn test_different_keys_produce_different_psks() {
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();
let (_kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
let enc_key = EncapsulationKey::X25519(kem_pk);
let psk1 =
derive_psk_with_psq_initiator(keypair_1.sk(), &keypair_2.pk, &enc_key, &salt).unwrap();
let psk2 =
derive_psk_with_psq_initiator(keypair_1.sk(), &keypair_3.pk, &enc_key, &salt).unwrap();
assert_ne!(
psk1, psk2,
"Different remote keys should produce different PSKs"
);
todo!()
// 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();
// let (_kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
// let enc_key = EncapsulationKey::X25519(kem_pk);
//
// let psk1 =
// derive_psk_with_psq_initiator(keypair_1.sk(), &keypair_2.pk, &enc_key, &salt).unwrap();
// let psk2 =
// derive_psk_with_psq_initiator(keypair_1.sk(), &keypair_3.pk, &enc_key, &salt).unwrap();
//
// assert_ne!(
// psk1, psk2,
// "Different remote keys should produce different PSKs"
// );
}
// PSQ-enhanced PSK tests
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey, KEM};
use nym_kkt::key_utils::generate_keypair_libcrux;
#[test]
fn test_psq_derivation_deterministic() {
let mut rng = rand09::rng();
// Generate X25519 keypairs for Noise
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();
let enc_key = EncapsulationKey::X25519(kem_pk);
let dec_key = DecapsulationKey::X25519(kem_sk);
let salt = [1u8; 32];
// Derive PSK twice with same inputs (initiator side)
let (_psk1, ct1) = derive_psk_with_psq_initiator(
client_keypair.sk(),
&gateway_keypair.pk,
&enc_key,
&salt,
)
.unwrap();
let (_psk2, _ct2) = derive_psk_with_psq_initiator(
client_keypair.sk(),
&gateway_keypair.pk,
&enc_key,
&salt,
)
.unwrap();
// PSKs will be different due to randomness in PSQ, but ciphertexts too
// This test verifies the function is deterministic given the SAME ciphertext
let psk_responder1 = derive_psk_with_psq_responder(
gateway_keypair.sk(),
&client_keypair.pk,
(&dec_key, &enc_key),
&ct1,
&salt,
)
.unwrap();
let psk_responder2 = derive_psk_with_psq_responder(
gateway_keypair.sk(),
&client_keypair.pk,
(&dec_key, &enc_key),
&ct1, // Same ciphertext
&salt,
)
.unwrap();
assert_eq!(
psk_responder1, psk_responder2,
"Same ciphertext should produce same PSK"
);
todo!()
// let mut rng = rand09::rng();
//
// // Generate X25519 keypairs for Noise
// 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();
// let enc_key = EncapsulationKey::X25519(kem_pk);
// let dec_key = DecapsulationKey::X25519(kem_sk);
//
// let salt = [1u8; 32];
//
// // Derive PSK twice with same inputs (initiator side)
// let (_psk1, ct1) = derive_psk_with_psq_initiator(
// client_keypair.sk(),
// &gateway_keypair.pk,
// &enc_key,
// &salt,
// )
// .unwrap();
//
// let (_psk2, _ct2) = derive_psk_with_psq_initiator(
// client_keypair.sk(),
// &gateway_keypair.pk,
// &enc_key,
// &salt,
// )
// .unwrap();
//
// // PSKs will be different due to randomness in PSQ, but ciphertexts too
// // This test verifies the function is deterministic given the SAME ciphertext
// let psk_responder1 = derive_psk_with_psq_responder(
// gateway_keypair.sk(),
// &client_keypair.pk,
// (&dec_key, &enc_key),
// &ct1,
// &salt,
// )
// .unwrap();
//
// let psk_responder2 = derive_psk_with_psq_responder(
// gateway_keypair.sk(),
// &client_keypair.pk,
// (&dec_key, &enc_key),
// &ct1, // Same ciphertext
// &salt,
// )
// .unwrap();
//
// assert_eq!(
// psk_responder1, psk_responder2,
// "Same ciphertext should produce same PSK"
// );
}
#[test]
fn test_psq_derivation_symmetric() {
let mut rng = rand09::rng();
// Generate X25519 keypairs for Noise
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();
let enc_key = EncapsulationKey::X25519(kem_pk);
let dec_key = DecapsulationKey::X25519(kem_sk);
let salt = [2u8; 32];
// Client derives PSK (initiator)
let (client_psk, ciphertext) = derive_psk_with_psq_initiator(
client_keypair.sk(),
&gateway_keypair.pk,
&enc_key,
&salt,
)
.unwrap();
// Gateway derives PSK from ciphertext (responder)
let gateway_psk = derive_psk_with_psq_responder(
gateway_keypair.sk(),
&client_keypair.pk,
(&dec_key, &enc_key),
&ciphertext,
&salt,
)
.unwrap();
assert_eq!(
client_psk, gateway_psk,
"Both sides should derive identical PSK via PSQ"
);
todo!()
// let mut rng = rand09::rng();
//
// // Generate X25519 keypairs for Noise
// 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();
// let enc_key = EncapsulationKey::X25519(kem_pk);
// let dec_key = DecapsulationKey::X25519(kem_sk);
//
// let salt = [2u8; 32];
//
// // Client derives PSK (initiator)
// let (client_psk, ciphertext) = derive_psk_with_psq_initiator(
// client_keypair.sk(),
// &gateway_keypair.pk,
// &enc_key,
// &salt,
// )
// .unwrap();
//
// // Gateway derives PSK from ciphertext (responder)
// let gateway_psk = derive_psk_with_psq_responder(
// gateway_keypair.sk(),
// &client_keypair.pk,
// (&dec_key, &enc_key),
// &ciphertext,
// &salt,
// )
// .unwrap();
//
// assert_eq!(
// client_psk, gateway_psk,
// "Both sides should derive identical PSK via PSQ"
// );
}
#[test]
fn test_different_kem_keys_different_psk() {
let mut rng = rand09::rng();
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();
let (_, kem_pk2) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
let enc_key1 = EncapsulationKey::X25519(kem_pk1);
let enc_key2 = EncapsulationKey::X25519(kem_pk2);
let salt = [3u8; 32];
let (psk1, _) = derive_psk_with_psq_initiator(
client_keypair.sk(),
&gateway_keypair.pk,
&enc_key1,
&salt,
)
.unwrap();
let (psk2, _) = derive_psk_with_psq_initiator(
client_keypair.sk(),
&gateway_keypair.pk,
&enc_key2,
&salt,
)
.unwrap();
assert_ne!(
psk1, psk2,
"Different KEM keys should produce different PSKs"
);
todo!()
// let mut rng = rand09::rng();
//
// 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();
// let (_, kem_pk2) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
//
// let enc_key1 = EncapsulationKey::X25519(kem_pk1);
// let enc_key2 = EncapsulationKey::X25519(kem_pk2);
//
// let salt = [3u8; 32];
//
// let (psk1, _) = derive_psk_with_psq_initiator(
// client_keypair.sk(),
// &gateway_keypair.pk,
// &enc_key1,
// &salt,
// )
// .unwrap();
//
// let (psk2, _) = derive_psk_with_psq_initiator(
// client_keypair.sk(),
// &gateway_keypair.pk,
// &enc_key2,
// &salt,
// )
// .unwrap();
//
// assert_ne!(
// psk1, psk2,
// "Different KEM keys should produce different PSKs"
// );
}
#[test]
fn test_psq_psk_output_length() {
let mut rng = rand09::rng();
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);
let salt = [4u8; 32];
let (psk, _) = derive_psk_with_psq_initiator(
client_keypair.sk(),
&gateway_keypair.pk,
&enc_key,
&salt,
)
.unwrap();
assert_eq!(psk.len(), 32, "PSQ PSK should be exactly 32 bytes");
todo!()
// let mut rng = rand09::rng();
//
// 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);
//
// let salt = [4u8; 32];
//
// let (psk, _) = derive_psk_with_psq_initiator(
// client_keypair.sk(),
// &gateway_keypair.pk,
// &enc_key,
// &salt,
// )
// .unwrap();
//
// assert_eq!(psk.len(), 32, "PSQ PSK should be exactly 32 bytes");
}
#[test]
fn test_psq_different_salts_different_psks() {
let mut rng = rand09::rng();
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);
let salt1 = [1u8; 32];
let salt2 = [2u8; 32];
let (psk1, _) = derive_psk_with_psq_initiator(
client_keypair.sk(),
&gateway_keypair.pk,
&enc_key,
&salt1,
)
.unwrap();
let (psk2, _) = derive_psk_with_psq_initiator(
client_keypair.sk(),
&gateway_keypair.pk,
&enc_key,
&salt2,
)
.unwrap();
assert_ne!(psk1, psk2, "Different salts should produce different PSKs");
todo!()
// let mut rng = rand09::rng();
//
// 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);
//
// let salt1 = [1u8; 32];
// let salt2 = [2u8; 32];
//
// let (psk1, _) = derive_psk_with_psq_initiator(
// client_keypair.sk(),
// &gateway_keypair.pk,
// &enc_key,
// &salt1,
// )
// .unwrap();
//
// let (psk2, _) = derive_psk_with_psq_initiator(
// client_keypair.sk(),
// &gateway_keypair.pk,
// &enc_key,
// &salt2,
// )
// .unwrap();
//
// assert_ne!(psk1, psk2, "Different salts should produce different PSKs");
}
}
+377 -123
View File
@@ -29,10 +29,10 @@ use rand09::rngs::ThreadRng;
use std::fmt::Debug;
const AAD_INITIATOR_OUTER: &[u8] = b"Test Data I Outer";
const AAD_INITIATOR_INNER: &[u8] = b"Test Data I Inner";
const AAD_RESPONDER: &[u8] = b"Test Data R";
const SESSION_CONTEXT: &[u8] = b"Test Context";
pub(crate) const AAD_INITIATOR_OUTER: &[u8] = b"NYM-PSQ-AAD-INIT-OUTER-V1";
pub(crate) const AAD_INITIATOR_INNER: &[u8] = b"NYM-PSQ-AAD-INIT-INNER-V1";
pub(crate) const AAD_RESPONDER: &[u8] = b"NYM-PSQ-AAD-RESP-V1";
pub(crate) const SESSION_CONTEXT: &[u8] = b"NYM-PSQ-SESSION-CONTEXT-V1";
pub enum PSQState<'a> {
Initiator(RegistrationInitiator<'a, ThreadRng>),
@@ -313,8 +313,17 @@ mod tests {
use crate::peer::mock_peers;
use crate::psq::helpers::LpTransportHandshakeExt;
use crate::psq::responder::DEFAULT_TIMESTAMP_TOLERANCE;
use libcrux_psq::IntoSession;
use libcrux_psq::handshake::types::{Authenticator, PQEncapsulationKey};
use libcrux_psq::session::{Session, SessionBinding};
use mock_instant::thread_local::MockClock;
use nym_kkt::ciphersuite::{HashFunction, HashLength, KEM, SignatureScheme};
use nym_kkt::initiator::KKTInitiator;
use nym_kkt::key_utils::{
generate_keypair_mceliece, generate_keypair_mlkem, generate_keypair_x25519,
hash_encapsulation_key,
};
use nym_kkt::responder::KKTResponder;
use nym_kkt_ciphersuite::{HashFunction, HashLength, SignatureScheme};
use nym_test_utils::mocks::async_read_write::MockIOStream;
use nym_test_utils::traits::{Leak, TimeboxedSpawnable};
use std::time::Duration;
@@ -331,146 +340,391 @@ mod tests {
#[tokio::test]
async fn e2e_psq_handshake() -> anyhow::Result<()> {
let conn_init = MockIOStream::default();
let conn_resp = conn_init.try_get_remote_handle();
// leak the connections (JUST FOR THE PURPOSE OF THIS TEST!)
// so they'd get 'static lifetime
let conn_init = conn_init.leak();
let conn_resp = conn_resp.leak();
let ciphersuite = Ciphersuite::new(
KEM::X25519,
HashFunction::Blake3,
SignatureScheme::Ed25519,
HashLength::Default,
);
let (init, resp) = mock_peers();
let resp_remote = resp.as_remote();
let handshake_init = PSQHandshakeState::new(conn_init, ciphersuite, init)
.with_protocol_version(1)
.with_remote_peer(resp_remote);
let handshake_resp = PSQHandshakeState::new(conn_resp, ciphersuite, resp);
let resp_fut = handshake_resp.complete_as_responder().spawn_timeboxed();
let init_fut = handshake_init.complete_as_initiator().spawn_timeboxed();
let (session_init, session_resp) = join!(init_fut, resp_fut);
let session_init = session_init???;
let session_resp = session_resp???;
assert_eq!(session_init.id(), session_resp.id());
assert_eq!(
session_init.outer_aead_key().as_bytes(),
session_resp.outer_aead_key().as_bytes()
);
assert_eq!(
session_init.pq_shared_secret().as_bytes(),
session_resp.pq_shared_secret().as_bytes()
);
Ok(())
todo!()
// let conn_init = MockIOStream::default();
// let conn_resp = conn_init.try_get_remote_handle();
//
// // leak the connections (JUST FOR THE PURPOSE OF THIS TEST!)
// // so they'd get 'static lifetime
// let conn_init = conn_init.leak();
// let conn_resp = conn_resp.leak();
//
// let ciphersuite = Ciphersuite::new(
// KEM::X25519,
// HashFunction::Blake3,
// SignatureScheme::Ed25519,
// HashLength::Default,
// );
//
// let (init, resp) = mock_peers();
// let resp_remote = resp.as_remote();
//
// let handshake_init = PSQHandshakeState::new(conn_init, ciphersuite, init)
// .with_protocol_version(1)
// .with_remote_peer(resp_remote);
// let handshake_resp = PSQHandshakeState::new(conn_resp, ciphersuite, resp);
//
// let resp_fut = handshake_resp.complete_as_responder().spawn_timeboxed();
// let init_fut = handshake_init.complete_as_initiator().spawn_timeboxed();
//
// let (session_init, session_resp) = join!(init_fut, resp_fut);
//
// let session_init = session_init???;
// let session_resp = session_resp???;
//
// assert_eq!(session_init.id(), session_resp.id());
// assert_eq!(
// session_init.outer_aead_key().as_bytes(),
// session_resp.outer_aead_key().as_bytes()
// );
// assert_eq!(
// session_init.pq_shared_secret().as_bytes(),
// session_resp.pq_shared_secret().as_bytes()
// );
//
// Ok(())
}
#[tokio::test]
async fn preparing_client_hello_initiator() -> anyhow::Result<()> {
let mut conn_init = MockIOStream::default();
let mut conn_resp = conn_init.try_get_remote_handle();
let ciphersuite = Ciphersuite::new(
KEM::X25519,
HashFunction::Blake3,
SignatureScheme::Ed25519,
HashLength::Default,
);
let (init, resp) = mock_peers();
let resp_remote = resp.as_remote();
// as initiator
let mut handshake_init = PSQHandshakeState::new(&mut conn_init, ciphersuite, init)
.with_protocol_version(1)
.with_remote_peer(resp_remote);
// you can generate and send (valid) client hello as initiator
let client_hello = handshake_init.send_client_hello().await?;
let LpMessage::ClientHello(received_client_hello) =
conn_resp.receive_packet(None).await?.message
else {
panic!("wrong message type");
};
assert_eq!(client_hello, received_client_hello);
Ok(())
todo!()
// let mut conn_init = MockIOStream::default();
// let mut conn_resp = conn_init.try_get_remote_handle();
//
// let ciphersuite = Ciphersuite::new(
// KEM::X25519,
// HashFunction::Blake3,
// SignatureScheme::Ed25519,
// HashLength::Default,
// );
// let (init, resp) = mock_peers();
// let resp_remote = resp.as_remote();
//
// // as initiator
// let mut handshake_init = PSQHandshakeState::new(&mut conn_init, ciphersuite, init)
// .with_protocol_version(1)
// .with_remote_peer(resp_remote);
//
// // you can generate and send (valid) client hello as initiator
// let client_hello = handshake_init.send_client_hello().await?;
// let LpMessage::ClientHello(received_client_hello) =
// conn_resp.receive_packet(None).await?.message
// else {
// panic!("wrong message type");
// };
// assert_eq!(client_hello, received_client_hello);
// Ok(())
}
// essentially make sure you can't accidentally trigger the handshake as the responder
#[tokio::test]
async fn preparing_client_hello_responder() -> anyhow::Result<()> {
let conn_init = MockIOStream::default();
let mut conn_resp = conn_init.try_get_remote_handle();
let ciphersuite = Ciphersuite::new(
KEM::X25519,
HashFunction::Blake3,
SignatureScheme::Ed25519,
HashLength::Default,
);
let (_, resp) = mock_peers();
// as initiator
let mut handshake_resp = PSQHandshakeState::new(&mut conn_resp, ciphersuite, resp);
// you can generate and send (valid) client hello as initiator
let sending_res = handshake_resp.send_client_hello().await;
assert!(sending_res.is_err());
Ok(())
todo!()
// let conn_init = MockIOStream::default();
// let mut conn_resp = conn_init.try_get_remote_handle();
//
// let ciphersuite = Ciphersuite::new(
// KEM::X25519,
// HashFunction::Blake3,
// SignatureScheme::Ed25519,
// HashLength::Default,
// );
// let (_, resp) = mock_peers();
//
// // as initiator
// let mut handshake_resp = PSQHandshakeState::new(&mut conn_resp, ciphersuite, resp);
//
// // you can generate and send (valid) client hello as initiator
// let sending_res = handshake_resp.send_client_hello().await;
// assert!(sending_res.is_err());
// Ok(())
}
#[tokio::test]
async fn test_receive_client_hello_timestamp_too_skewed() -> anyhow::Result<()> {
let current_time = Duration::from_secs(10000);
MockClock::set_system_time(current_time);
todo!()
// let current_time = Duration::from_secs(10000);
// MockClock::set_system_time(current_time);
//
// let too_old = current_time - DEFAULT_TIMESTAMP_TOLERANCE - Duration::from_secs(1);
// let too_recent = current_time + DEFAULT_TIMESTAMP_TOLERANCE + Duration::from_secs(1);
//
// let ciphersuite = Ciphersuite::new(
// KEM::X25519,
// HashFunction::Blake3,
// SignatureScheme::Ed25519,
// HashLength::Default,
// );
//
// // TOO OLD
// let mut conn_init = MockIOStream::default();
// let mut conn_resp = conn_init.try_get_remote_handle();
// let (init, resp) = mock_peers();
//
// let mut handshake_resp = PSQHandshakeState::new(&mut conn_resp, ciphersuite, resp);
// let client_hello_too_old = init.build_client_hello_data(too_old.as_secs());
//
// conn_init
// .send_packet(client_hello_too_old.into_lp_packet(1), None)
// .await?;
// let err = handshake_resp.receive_client_hello().await.unwrap_err();
// assert!(err.to_string().contains("too old"));
//
// // TOO RECENT
// let mut conn_init = MockIOStream::default();
// let mut conn_resp = conn_init.try_get_remote_handle();
// let (init, resp) = mock_peers();
//
// let mut handshake_resp = PSQHandshakeState::new(&mut conn_resp, ciphersuite, resp);
// let client_hello_too_recent = init.build_client_hello_data(too_recent.as_secs());
//
// conn_init
// .send_packet(client_hello_too_recent.into_lp_packet(1), None)
// .await?;
// let err = handshake_resp.receive_client_hello().await.unwrap_err();
//
// assert!(err.to_string().contains("too future"));
// Ok(())
}
let too_old = current_time - DEFAULT_TIMESTAMP_TOLERANCE - Duration::from_secs(1);
let too_recent = current_time + DEFAULT_TIMESTAMP_TOLERANCE + Duration::from_secs(1);
// plain test without any wrappers
#[test]
fn e2e_test_plain() {
let mut rng = rand09::rng();
let ciphersuite = Ciphersuite::new(
KEM::X25519,
HashFunction::Blake3,
SignatureScheme::Ed25519,
HashLength::Default,
// we should add these as consts
let aad_initiator_outer = b"Test Data I Outer";
let aad_initiator_inner = b"Test Data I Inner";
let aad_responder = b"Test Data R";
let ctx = b"Test Context";
// generate responder x25519 keys
let responder_x25519_keypair = generate_keypair_x25519(&mut rng);
let hash_function = HashFunction::Blake3;
// generate kem public keys
let responder_mlkem_keypair = generate_keypair_mlkem(&mut rng);
let responder_mceliece_keypair = generate_keypair_mceliece(&mut rng);
let r_dir_hash_mlkem = hash_encapsulation_key(
// &ciphersuite.hash_function(),
hash_function,
// ciphersuite.hash_len(),
HashLength::Default.value(),
responder_mlkem_keypair.1.as_slice().as_slice(),
);
// TOO OLD
let mut conn_init = MockIOStream::default();
let mut conn_resp = conn_init.try_get_remote_handle();
let (init, resp) = mock_peers();
let _r_dir_hash_mceliece = hash_encapsulation_key(
// &ciphersuite.hash_function(),
hash_function,
// ciphersuite.hash_len(),
HashLength::Default.value(),
responder_mceliece_keypair.1.as_ref(),
);
let mut handshake_resp = PSQHandshakeState::new(&mut conn_resp, ciphersuite, resp);
let client_hello_too_old = init.build_client_hello_data(too_old.as_secs());
let kkt_responder = KKTResponder::new(
&responder_x25519_keypair,
Some(&responder_mlkem_keypair.1),
Some(&responder_mceliece_keypair.1),
&[
HashFunction::Blake3,
HashFunction::SHA256,
HashFunction::Shake128,
HashFunction::Shake256,
],
&[1],
&[SignatureScheme::Ed25519],
)
.unwrap();
conn_init
.send_packet(client_hello_too_old.into_lp_packet(1), None)
.await?;
let err = handshake_resp.receive_client_hello().await.unwrap_err();
assert!(err.to_string().contains("too old"));
// OneWay - MlKem
let psq_ciphersuite = CiphersuiteName::X25519_MLKEM768_X25519_AESGCM128_HKDFSHA256;
// TOO RECENT
let mut conn_init = MockIOStream::default();
let mut conn_resp = conn_init.try_get_remote_handle();
let (init, resp) = mock_peers();
let responder_ciphersuite = CiphersuiteBuilder::new(psq_ciphersuite)
.longterm_x25519_keys(&responder_x25519_keypair)
.longterm_mlkem_encapsulation_key(&responder_mlkem_keypair.1)
.longterm_mlkem_decapsulation_key(&responder_mlkem_keypair.0)
.build_responder_ciphersuite()
.unwrap();
let mut handshake_resp = PSQHandshakeState::new(&mut conn_resp, ciphersuite, resp);
let client_hello_too_recent = init.build_client_hello_data(too_recent.as_secs());
let mut responder = PrincipalBuilder::new(rand09::rng())
.context(ctx)
.outer_aad(aad_responder)
.recent_keys_upper_bound(30)
.build_responder(responder_ciphersuite)
.unwrap();
conn_init
.send_packet(client_hello_too_recent.into_lp_packet(1), None)
.await?;
let err = handshake_resp.receive_client_hello().await.unwrap_err();
let ciphersuite = Ciphersuite::resolve_ciphersuite(
KEM::MlKem768,
hash_function,
SignatureScheme::Ed25519,
None,
)
.unwrap();
assert!(err.to_string().contains("too future"));
Ok(())
let (mut initiator, request_bytes) = KKTInitiator::generate_one_way_request(
&mut rng,
&ciphersuite,
&responder_x25519_keypair.pk,
&r_dir_hash_mlkem,
1u8,
)
.unwrap();
let (response_bytes, _) = kkt_responder.process_request(&request_bytes).unwrap();
let (i_obtained_key, _) = initiator.process_response(&response_bytes).unwrap();
assert_eq!(
i_obtained_key,
responder_mlkem_keypair.1.as_slice().as_slice(),
);
let mlkem_key =
libcrux_kem::MlKem768PublicKey::try_from(i_obtained_key.as_slice()).unwrap();
let initiator_psq_keys = generate_keypair_x25519(&mut rng);
let initiator_cbuilder = CiphersuiteBuilder::new(psq_ciphersuite)
.longterm_x25519_keys(&initiator_psq_keys)
.peer_longterm_x25519_pk(&responder_x25519_keypair.pk)
.peer_longterm_mlkem_pk(&mlkem_key);
let initiator_ciphersuite = initiator_cbuilder.build_initiator_ciphersuite().unwrap();
let mut msg_channel = vec![0u8; 8192];
let mut payload_buf_responder = vec![0u8; 4096];
let mut payload_buf_initiator = vec![0u8; 4096];
let mut initiator = PrincipalBuilder::new(rand09::rng())
.outer_aad(aad_initiator_outer)
.inner_aad(aad_initiator_inner)
.context(ctx)
.build_registration_initiator(initiator_ciphersuite)
.unwrap();
// Send first message
let registration_payload_initiator = b"Registration_init";
let len_i = initiator
.write_message(registration_payload_initiator, &mut msg_channel)
.unwrap();
// Read first message
let (len_r_deserialized, len_r_payload) = responder
.read_message(&msg_channel, &mut payload_buf_responder)
.unwrap();
// We read the same amount of data.
assert_eq!(len_r_deserialized, len_i);
assert_eq!(len_r_payload, registration_payload_initiator.len());
assert_eq!(
&payload_buf_responder[0..len_r_payload],
registration_payload_initiator
);
// Get the authenticator out here, so we can deserialize the session later.
let Some(initiator_authenticator) = responder.initiator_authenticator() else {
panic!("No initiator authenticator found")
};
// Respond
let registration_payload_responder = b"Registration_respond";
let len_r = responder
.write_message(registration_payload_responder, &mut msg_channel)
.unwrap();
// Finalize on registration initiator
let (len_i_deserialized, len_i_payload) = initiator
.read_message(&msg_channel, &mut payload_buf_initiator)
.unwrap();
// We read the same amount of data.
assert_eq!(len_r, len_i_deserialized);
assert_eq!(registration_payload_responder.len(), len_i_payload);
assert_eq!(
&payload_buf_initiator[0..len_i_payload],
registration_payload_responder
);
// Ready for transport mode
assert!(initiator.is_handshake_finished());
assert!(responder.is_handshake_finished());
let i_transport = initiator.into_session().unwrap();
let r_transport = responder.into_session().unwrap();
// test serialization, deserialization
let mut session_storage = vec![0u8; 4096];
i_transport
.serialize(
&mut session_storage,
SessionBinding {
initiator_authenticator: &Authenticator::Dh(initiator_psq_keys.pk),
responder_ecdh_pk: &responder_x25519_keypair.pk,
responder_pq_pk: Some(PQEncapsulationKey::MlKem(&mlkem_key)),
},
)
.unwrap();
let mut i_transport = Session::deserialize(
&session_storage,
SessionBinding {
initiator_authenticator: &Authenticator::Dh(initiator_psq_keys.pk),
responder_ecdh_pk: &responder_x25519_keypair.pk,
responder_pq_pk: Some(PQEncapsulationKey::MlKem(&mlkem_key)),
},
)
.unwrap();
r_transport
.serialize(
&mut session_storage,
SessionBinding {
initiator_authenticator: &initiator_authenticator,
responder_ecdh_pk: &responder_x25519_keypair.pk,
responder_pq_pk: Some(PQEncapsulationKey::MlKem(&mlkem_key)),
},
)
.unwrap();
let mut r_transport = Session::deserialize(
&session_storage,
SessionBinding {
initiator_authenticator: &initiator_authenticator,
responder_ecdh_pk: &responder_x25519_keypair.pk,
responder_pq_pk: Some(PQEncapsulationKey::MlKem(&mlkem_key)),
},
)
.unwrap();
let mut channel_i = i_transport.transport_channel().unwrap();
let mut channel_r = r_transport.transport_channel().unwrap();
assert_eq!(channel_i.identifier(), channel_r.identifier());
let app_data_i = b"Derived session hey".as_slice();
let app_data_r = b"Derived session ho".as_slice();
let len_i = channel_i
.write_message(app_data_i, &mut msg_channel)
.unwrap();
let (len_r_deserialized, len_r_payload) = channel_r
.read_message(&msg_channel, &mut payload_buf_responder)
.unwrap();
// We read the same amount of data.
assert_eq!(len_r_deserialized, len_i);
assert_eq!(len_r_payload, app_data_i.len());
assert_eq!(&payload_buf_responder[0..len_r_payload], app_data_i);
let len_r = channel_r
.write_message(app_data_r, &mut msg_channel)
.unwrap();
let (len_i_deserialized, len_i_payload) = channel_i
.read_message(&msg_channel, &mut payload_buf_initiator)
.unwrap();
assert_eq!(len_r, len_i_deserialized);
assert_eq!(app_data_r.len(), len_i_payload);
assert_eq!(&payload_buf_initiator[0..len_i_payload], app_data_r);
}
}
+110 -105
View File
@@ -609,7 +609,7 @@ impl SubsessionHandshake {
#[cfg(test)]
mod tests {
use super::*;
use crate::{SessionsMock, replay::ReplayError, sessions_for_tests};
use crate::{SessionsMock, kem_list, replay::ReplayError, sessions_for_tests};
use rand::thread_rng;
// Helper function to generate keypairs for tests
@@ -619,18 +619,19 @@ mod tests {
#[test]
fn test_session_creation() {
let mut session = sessions_for_tests().0;
for kem in kem_list() {
let session = sessions_for_tests(kem).0;
// Initial counter should be zero
let counter = session.next_counter();
assert_eq!(counter, 0);
// Counter should increment
let counter = session.next_counter();
assert_eq!(counter, 1);
}
todo!()
// let mut session = sessions_for_tests().0;
// for kem in kem_list() {
// let session = sessions_for_tests(kem).0;
//
// // Initial counter should be zero
// let counter = session.next_counter();
// assert_eq!(counter, 0);
//
// // Counter should increment
// let counter = session.next_counter();
// assert_eq!(counter, 1);
// }
}
// NOTE: These tests are obsolete after removing optional KEM parameters.
@@ -650,69 +651,72 @@ mod tests {
#[test]
fn test_replay_protection_sequential() {
for kem in kem_list() {
let mut session = sessions_for_tests(kem).1;
// Sequential counters should be accepted
assert!(session.receiving_counter_quick_check(0).is_ok());
assert!(session.receiving_counter_mark(0).is_ok());
assert!(session.receiving_counter_quick_check(1).is_ok());
assert!(session.receiving_counter_mark(1).is_ok());
// Duplicates should be rejected
assert!(session.receiving_counter_quick_check(0).is_err());
let err = session.receiving_counter_mark(0).unwrap_err();
match err {
LpError::Replay(replay_error) => {
assert!(matches!(replay_error, ReplayError::DuplicateCounter));
}
_ => panic!("Expected replay error"),
}
}
todo!()
// for kem in kem_list() {
// let mut session = sessions_for_tests(kem).1;
//
// // Sequential counters should be accepted
// assert!(session.receiving_counter_quick_check(0).is_ok());
// assert!(session.receiving_counter_mark(0).is_ok());
//
// assert!(session.receiving_counter_quick_check(1).is_ok());
// assert!(session.receiving_counter_mark(1).is_ok());
//
// // Duplicates should be rejected
// assert!(session.receiving_counter_quick_check(0).is_err());
// let err = session.receiving_counter_mark(0).unwrap_err();
// match err {
// LpError::Replay(replay_error) => {
// assert!(matches!(replay_error, ReplayError::DuplicateCounter));
// }
// _ => panic!("Expected replay error"),
// }
// }
}
#[test]
fn test_replay_protection_out_of_order() {
for kem in kem_list() {
let mut session = sessions_for_tests(kem).1;
// Receive packets in order
assert!(session.receiving_counter_mark(0).is_ok());
assert!(session.receiving_counter_mark(1).is_ok());
assert!(session.receiving_counter_mark(2).is_ok());
// Skip ahead
assert!(session.receiving_counter_mark(10).is_ok());
// Can still receive out-of-order packets within window
assert!(session.receiving_counter_quick_check(5).is_ok());
assert!(session.receiving_counter_mark(5).is_ok());
// But duplicates are still rejected
assert!(session.receiving_counter_quick_check(5).is_err());
assert!(session.receiving_counter_mark(5).is_err());
}
todo!()
// for kem in kem_list() {
// let mut session = sessions_for_tests(kem).1;
//
// // Receive packets in order
// assert!(session.receiving_counter_mark(0).is_ok());
// assert!(session.receiving_counter_mark(1).is_ok());
// assert!(session.receiving_counter_mark(2).is_ok());
//
// // Skip ahead
// assert!(session.receiving_counter_mark(10).is_ok());
//
// // Can still receive out-of-order packets within window
// assert!(session.receiving_counter_quick_check(5).is_ok());
// assert!(session.receiving_counter_mark(5).is_ok());
//
// // But duplicates are still rejected
// assert!(session.receiving_counter_quick_check(5).is_err());
// assert!(session.receiving_counter_mark(5).is_err());
// }
}
#[test]
fn test_packet_stats() {
for kem in kem_list() {
let mut session = sessions_for_tests(kem).1;
// Initial stats
let (next, received) = session.current_packet_cnt();
assert_eq!(next, 0);
assert_eq!(received, 0);
// After receiving packets
assert!(session.receiving_counter_mark(0).is_ok());
assert!(session.receiving_counter_mark(1).is_ok());
let (next, received) = session.current_packet_cnt();
assert_eq!(next, 2);
assert_eq!(received, 2);
}
todo!()
// for kem in kem_list() {
// let mut session = sessions_for_tests(kem).1;
//
// // Initial stats
// let (next, received) = session.current_packet_cnt();
// assert_eq!(next, 0);
// assert_eq!(received, 0);
//
// // After receiving packets
// assert!(session.receiving_counter_mark(0).is_ok());
// assert!(session.receiving_counter_mark(1).is_ok());
//
// let (next, received) = session.current_packet_cnt();
// assert_eq!(next, 2);
// assert_eq!(received, 2);
// }
}
/*
@@ -754,47 +758,48 @@ mod tests {
/// Test that X25519 keys are correctly converted to KEM format
#[test]
fn test_x25519_to_kem_conversion() {
use nym_kkt::ciphersuite::EncapsulationKey;
let initiator_keys = generate_x25519_keypair();
let responder_keys = generate_x25519_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();
let libcrux_public_key =
libcrux_kem::PublicKey::decode(libcrux_kem::Algorithm::X25519, x25519_public_bytes)
.expect("X25519 public key should convert to libcrux PublicKey");
let _kem_key = EncapsulationKey::X25519(libcrux_public_key);
// Verify we can convert X25519 private key to KEM format
let x25519_private_bytes = initiator_keys.private_key().to_bytes();
let _libcrux_private_key =
libcrux_kem::PrivateKey::decode(libcrux_kem::Algorithm::X25519, &x25519_private_bytes)
.expect("X25519 private key should convert to libcrux PrivateKey");
// Successful conversion is sufficient - actual encapsulation is tested in psk.rs
// (libcrux_kem::PrivateKey is an enum with no len() method, conversion success is enough)
todo!()
//
// let initiator_keys = generate_x25519_keypair();
// let responder_keys = generate_x25519_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();
// let libcrux_public_key =
// libcrux_kem::PublicKey::decode(libcrux_kem::Algorithm::X25519, x25519_public_bytes)
// .expect("X25519 public key should convert to libcrux PublicKey");
//
// let _kem_key = EncapsulationKey::X25519(libcrux_public_key);
//
// // Verify we can convert X25519 private key to KEM format
// let x25519_private_bytes = initiator_keys.private_key().to_bytes();
// let _libcrux_private_key =
// libcrux_kem::PrivateKey::decode(libcrux_kem::Algorithm::X25519, &x25519_private_bytes)
// .expect("X25519 private key should convert to libcrux PrivateKey");
//
// // Successful conversion is sufficient - actual encapsulation is tested in psk.rs
// // (libcrux_kem::PrivateKey is an enum with no len() method, conversion success is enough)
}
#[test]
fn test_demote_sets_read_only() {
let sessions = SessionsMock::mock_post_handshake(12345);
let mut session = sessions.initiator;
for kem in kem_list() {
let session = create_handshake_test_session(kem, 12345u32, true);
// Initially not read-only
assert!(!session.is_read_only());
assert!(session.successor_session_id().is_none());
// Demote the session
session.demote(99999);
// Now read-only with successor
assert!(session.is_read_only());
assert_eq!(session.successor_session_id(), Some(99999));
}
todo!()
// let sessions = SessionsMock::mock_post_handshake(12345);
// let mut session = sessions.initiator;
// for kem in kem_list() {
// let session = create_handshake_test_session(kem, 12345u32, true);
//
// // Initially not read-only
// assert!(!session.is_read_only());
// assert!(session.successor_session_id().is_none());
//
// // Demote the session
// session.demote(99999);
//
// // Now read-only with successor
// assert!(session.is_read_only());
// assert_eq!(session.successor_session_id(), Some(99999));
// }
}
#[test]