moved the e2e test to nym-lp
This commit is contained in:
+3
-2
@@ -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 = [
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user