lp: attempt to negotiate (and use) protocol version (#6399)
This commit is contained in:
committed by
GitHub
parent
dccdde108c
commit
8916b021a9
@@ -38,12 +38,25 @@ pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
||||
// Use consistent salt for deterministic tests
|
||||
let salt = [1u8; 32];
|
||||
|
||||
let initiator_session =
|
||||
LpSession::new(receiver_index, true, init.clone(), resp.as_remote(), &salt)
|
||||
.expect("Test session creation failed");
|
||||
let initiator_session = LpSession::new(
|
||||
receiver_index,
|
||||
true,
|
||||
init.clone(),
|
||||
resp.as_remote(),
|
||||
&salt,
|
||||
packet::version::CURRENT,
|
||||
)
|
||||
.expect("Test session creation failed");
|
||||
|
||||
let responder_session = LpSession::new(receiver_index, false, resp, init.as_remote(), &salt)
|
||||
.expect("Test session creation failed");
|
||||
let responder_session = LpSession::new(
|
||||
receiver_index,
|
||||
false,
|
||||
resp,
|
||||
init.as_remote(),
|
||||
&salt,
|
||||
packet::version::CURRENT,
|
||||
)
|
||||
.expect("Test session creation failed");
|
||||
|
||||
(initiator_session, responder_session)
|
||||
}
|
||||
@@ -51,7 +64,7 @@ pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::message::LpMessage;
|
||||
use crate::packet::{LpHeader, LpPacket, TRAILER_LEN};
|
||||
use crate::packet::{LpHeader, LpPacket, TRAILER_LEN, version};
|
||||
use crate::session_manager::SessionManager;
|
||||
use crate::{LpError, sessions_for_tests};
|
||||
use bytes::BytesMut;
|
||||
@@ -182,11 +195,19 @@ mod tests {
|
||||
init.clone(),
|
||||
resp.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let _ = remote_manager
|
||||
.create_session_state_machine(receiver_index, false, resp, init.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
false,
|
||||
resp,
|
||||
init.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
// === Packet 1 (Counter 0 - Should succeed) ===
|
||||
let packet1 = LpPacket {
|
||||
|
||||
@@ -203,9 +203,9 @@ impl LpHeader {
|
||||
}
|
||||
|
||||
impl LpHeader {
|
||||
pub fn new(receiver_idx: u32, counter: u64) -> Self {
|
||||
pub fn new(receiver_idx: u32, counter: u64, protocol_version: u8) -> Self {
|
||||
Self {
|
||||
protocol_version: version::CURRENT,
|
||||
protocol_version,
|
||||
reserved: [0u8; 3],
|
||||
receiver_idx,
|
||||
counter,
|
||||
|
||||
@@ -231,7 +231,7 @@ pub struct LpSession {
|
||||
/// Negotiated protocol version from handshake.
|
||||
/// Set during handshake completion from the ClientHello/ServerHello packet header.
|
||||
/// Used for future version negotiation and compatibility checks.
|
||||
negotiated_version: std::sync::atomic::AtomicU8,
|
||||
negotiated_version: u8,
|
||||
}
|
||||
|
||||
// noiserm
|
||||
@@ -276,18 +276,9 @@ impl LpSession {
|
||||
|
||||
/// Returns the negotiated protocol version from the handshake.
|
||||
///
|
||||
/// Defaults to 1 (current LP version). Set during handshake via
|
||||
/// `set_negotiated_version()` when ClientHello/ServerHello is processed.
|
||||
/// Set during `LpSession` creation after sending / receiving `ClientHelloData`
|
||||
pub fn negotiated_version(&self) -> u8 {
|
||||
self.negotiated_version.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
/// Sets the negotiated protocol version from handshake packet header.
|
||||
///
|
||||
/// Should be called during handshake when processing ClientHello (responder)
|
||||
/// or ServerHello (initiator) to record the agreed protocol version.
|
||||
pub fn set_negotiated_version(&self, version: u8) {
|
||||
self.negotiated_version.store(version, Ordering::Release);
|
||||
self.negotiated_version
|
||||
}
|
||||
|
||||
/// Returns the local X25519 public key.
|
||||
@@ -364,12 +355,14 @@ impl LpSession {
|
||||
/// * `local_peer` - This side's LP peer's keys
|
||||
/// * `remote_peer` - The remote's LP peer's keys
|
||||
/// * `salt` - Salt for PSK derivation
|
||||
/// * `protocol_version` - Protocol version to attach in all `LpPacket`s
|
||||
pub fn new(
|
||||
id: u32,
|
||||
is_initiator: bool,
|
||||
local_peer: LpLocalPeer,
|
||||
remote_peer: LpRemotePeer,
|
||||
salt: &[u8; 32],
|
||||
protocol_version: u8,
|
||||
) -> Result<Self, LpError> {
|
||||
// noiserm
|
||||
// if we're LP responder, we **must** set our kem key
|
||||
@@ -444,13 +437,13 @@ impl LpSession {
|
||||
subsession_counter: AtomicU64::new(0),
|
||||
read_only: AtomicBool::new(false),
|
||||
successor_session_id: Mutex::new(None),
|
||||
negotiated_version: std::sync::atomic::AtomicU8::new(1), // Default to version 1
|
||||
negotiated_version: protocol_version,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn next_packet(&self, message: LpMessage) -> Result<LpPacket, LpError> {
|
||||
let counter = self.next_counter();
|
||||
let header = LpHeader::new(self.id(), counter);
|
||||
let header = LpHeader::new(self.id(), counter, self.negotiated_version);
|
||||
let packet = LpPacket::new(header, message);
|
||||
Ok(packet)
|
||||
}
|
||||
@@ -1275,6 +1268,7 @@ impl LpSession {
|
||||
remote_peer: self.remote_peer.clone(),
|
||||
pq_shared_secret: PqSharedSecret::new(pq_secret),
|
||||
subsession_psk,
|
||||
negotiated_version: self.negotiated_version,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1314,8 +1308,12 @@ pub struct SubsessionHandshake {
|
||||
|
||||
/// PQ shared secret inherited from parent (for creating further subsessions)
|
||||
pq_shared_secret: PqSharedSecret,
|
||||
|
||||
/// Subsession PSK (for deriving outer AEAD key)
|
||||
subsession_psk: [u8; 32],
|
||||
|
||||
/// Negotiated protocol version from handshake.
|
||||
negotiated_version: u8,
|
||||
}
|
||||
|
||||
impl SubsessionHandshake {
|
||||
@@ -1415,7 +1413,7 @@ impl SubsessionHandshake {
|
||||
read_only: AtomicBool::new(false),
|
||||
successor_session_id: Mutex::new(None),
|
||||
// Inherit parent's protocol version
|
||||
negotiated_version: std::sync::atomic::AtomicU8::new(1),
|
||||
negotiated_version: self.negotiated_version,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1423,6 +1421,7 @@ impl SubsessionHandshake {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::packet::version;
|
||||
use crate::peer::mock_peers;
|
||||
use crate::{replay::ReplayError, sessions_for_tests};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
@@ -1448,8 +1447,15 @@ mod tests {
|
||||
let salt = [0u8; 32]; // Test salt
|
||||
|
||||
// PSQ will derive the PSK during handshake using X25519 as DHKEM
|
||||
let session = LpSession::new(receiver_index, is_initiator, local, remote.clone(), &salt)
|
||||
.expect("Test session creation failed");
|
||||
let session = LpSession::new(
|
||||
receiver_index,
|
||||
is_initiator,
|
||||
local,
|
||||
remote.clone(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.expect("Test session creation failed");
|
||||
|
||||
// Initialize KKT state to Completed for tests (bypasses KKT exchange)
|
||||
// This simulates having already received the remote party's KEM key via KKT
|
||||
@@ -2112,14 +2118,28 @@ mod tests {
|
||||
let receiver_index: u32 = 55555;
|
||||
let salt = [0u8; 32];
|
||||
|
||||
let initiator_session =
|
||||
LpSession::new(receiver_index, true, init.clone(), bad_resp, &salt).unwrap();
|
||||
let initiator_session = LpSession::new(
|
||||
receiver_index,
|
||||
true,
|
||||
init.clone(),
|
||||
bad_resp,
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Initialize KKT state for test
|
||||
initiator_session.set_kkt_completed_for_test(resp.x25519.public_key());
|
||||
|
||||
let responder_session =
|
||||
LpSession::new(receiver_index, false, resp, bad_init, &salt).unwrap();
|
||||
let responder_session = LpSession::new(
|
||||
receiver_index,
|
||||
false,
|
||||
resp,
|
||||
bad_init,
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
// Initialize KKT state for test
|
||||
responder_session.set_kkt_completed_for_test(init.x25519.public_key());
|
||||
|
||||
@@ -2154,13 +2174,27 @@ mod tests {
|
||||
let receiver_index: u32 = 66666;
|
||||
let salt = [0u8; 32];
|
||||
|
||||
let initiator_session =
|
||||
LpSession::new(receiver_index, true, init.clone(), resp.as_remote(), &salt).unwrap();
|
||||
let initiator_session = LpSession::new(
|
||||
receiver_index,
|
||||
true,
|
||||
init.clone(),
|
||||
resp.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
// Initialize KKT state for test
|
||||
initiator_session.set_kkt_completed_for_test(resp.x25519.public_key());
|
||||
|
||||
let responder_session =
|
||||
LpSession::new(receiver_index, false, resp, bad_init, &salt).unwrap();
|
||||
let responder_session = LpSession::new(
|
||||
receiver_index,
|
||||
false,
|
||||
resp,
|
||||
bad_init,
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
// Initialize KKT state for test
|
||||
responder_session.set_kkt_completed_for_test(init.x25519.public_key());
|
||||
|
||||
|
||||
@@ -58,11 +58,25 @@ mod tests {
|
||||
|
||||
// 4. Create sessions using the pre-built Noise states
|
||||
let peer_a_sm = session_manager_1
|
||||
.create_session_state_machine(receiver_index, true, a.clone(), b.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
true,
|
||||
a.clone(),
|
||||
b.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.expect("Failed to create session A");
|
||||
|
||||
let peer_b_sm = session_manager_2
|
||||
.create_session_state_machine(receiver_index, false, b.clone(), a.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
false,
|
||||
b.clone(),
|
||||
a.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.expect("Failed to create session B");
|
||||
|
||||
// Verify session count
|
||||
@@ -481,11 +495,25 @@ mod tests {
|
||||
let salt = [43u8; 32];
|
||||
|
||||
let peer_a_sm = session_manager_1
|
||||
.create_session_state_machine(receiver_index, true, a.clone(), b.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
true,
|
||||
a.clone(),
|
||||
b.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.expect("Failed to create session A");
|
||||
|
||||
let peer_b_sm = session_manager_2
|
||||
.create_session_state_machine(receiver_index, false, b.clone(), a.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
false,
|
||||
b.clone(),
|
||||
a.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.expect("Failed to create session B");
|
||||
|
||||
// Initialize KKT state for both sessions (test bypass)
|
||||
@@ -659,7 +687,14 @@ mod tests {
|
||||
|
||||
// 2. Create a session (using real noise state)
|
||||
let _session = session_manager
|
||||
.create_session_state_machine(receiver_index, true, a.clone(), b.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
true,
|
||||
a.clone(),
|
||||
b.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.expect("Failed to create session");
|
||||
|
||||
// 3. Try to get a non-existent session
|
||||
@@ -682,6 +717,7 @@ mod tests {
|
||||
a.clone(),
|
||||
b.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.expect("Failed to create temp session");
|
||||
|
||||
@@ -733,6 +769,7 @@ mod tests {
|
||||
}
|
||||
// Remove unused imports if SessionManager methods are no longer direct dependencies
|
||||
// use crate::noise_protocol::{create_noise_state, create_noise_state_responder};
|
||||
use crate::packet::version;
|
||||
use crate::peer::mock_peers;
|
||||
use crate::state_machine::LpData;
|
||||
use crate::{
|
||||
@@ -772,12 +809,26 @@ mod tests {
|
||||
// 3. Create sessions state machines
|
||||
assert!(
|
||||
session_manager_1
|
||||
.create_session_state_machine(receiver_index, true, a.clone(), b.as_remote(), &salt,) // Initiator
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
true,
|
||||
a.clone(),
|
||||
b.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT
|
||||
) // Initiator
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
session_manager_2
|
||||
.create_session_state_machine(receiver_index, false, b, a.as_remote(), &salt,) // Responder
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
false,
|
||||
b,
|
||||
a.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT
|
||||
) // Responder
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
|
||||
@@ -172,8 +172,16 @@ impl SessionManager {
|
||||
local_peer: LpLocalPeer,
|
||||
remote_peer: LpRemotePeer,
|
||||
salt: &[u8; 32],
|
||||
protocol_version: u8,
|
||||
) -> Result<u32, LpError> {
|
||||
let sm = LpStateMachine::new(receiver_index, is_initiator, local_peer, remote_peer, salt)?;
|
||||
let sm = LpStateMachine::new(
|
||||
receiver_index,
|
||||
is_initiator,
|
||||
local_peer,
|
||||
remote_peer,
|
||||
salt,
|
||||
protocol_version,
|
||||
)?;
|
||||
|
||||
self.state_machines.insert(receiver_index, sm);
|
||||
Ok(receiver_index)
|
||||
@@ -204,6 +212,7 @@ impl SessionManager {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::packet::version;
|
||||
use crate::peer::{mock_peers, random_peer};
|
||||
use nym_test_utils::helpers::deterministic_rng;
|
||||
|
||||
@@ -218,7 +227,14 @@ mod tests {
|
||||
let receiver_index: u32 = 1001;
|
||||
|
||||
let sm_1_id = manager
|
||||
.create_session_state_machine(receiver_index, true, local, peer1.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
true,
|
||||
local,
|
||||
peer1.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let retrieved = manager.state_machine_exists(sm_1_id);
|
||||
@@ -239,7 +255,14 @@ mod tests {
|
||||
let receiver_index: u32 = 2002;
|
||||
|
||||
let sm_1_id = manager
|
||||
.create_session_state_machine(receiver_index, true, local, peer1.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
receiver_index,
|
||||
true,
|
||||
local,
|
||||
peer1.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let removed = manager.remove_state_machine(sm_1_id);
|
||||
@@ -262,15 +285,36 @@ mod tests {
|
||||
let salt = [49u8; 32];
|
||||
|
||||
let sm_1 = manager
|
||||
.create_session_state_machine(3001, true, local.clone(), peer1.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
3001,
|
||||
true,
|
||||
local.clone(),
|
||||
peer1.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let sm_2 = manager
|
||||
.create_session_state_machine(3002, true, local.clone(), peer2.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
3002,
|
||||
true,
|
||||
local.clone(),
|
||||
peer2.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let sm_3 = manager
|
||||
.create_session_state_machine(3003, true, local.clone(), peer3.as_remote(), &salt)
|
||||
.create_session_state_machine(
|
||||
3003,
|
||||
true,
|
||||
local.clone(),
|
||||
peer3.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(manager.session_count(), 3);
|
||||
@@ -298,6 +342,7 @@ mod tests {
|
||||
init,
|
||||
resp.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
);
|
||||
|
||||
assert!(sm.is_ok());
|
||||
|
||||
@@ -256,16 +256,25 @@ impl LpStateMachine {
|
||||
/// * `local_peer` - This side's LP peer's keys
|
||||
/// * `remote_peer` - The remote's LP peer's keys
|
||||
/// * `salt` - Fresh salt for PSK derivation (must be unique per session)
|
||||
/// * `protocol_version` - Protocol version to attach in all `LpPacket`s
|
||||
pub fn new(
|
||||
receiver_index: u32,
|
||||
is_initiator: bool,
|
||||
local_peer: LpLocalPeer,
|
||||
remote_peer: LpRemotePeer,
|
||||
salt: &[u8; 32],
|
||||
protocol_version: u8,
|
||||
) -> Result<Self, LpError> {
|
||||
// Create the session with both Ed25519 (for PSQ auth) and derived X25519 keys (for Noise)
|
||||
// receiver_index is client-proposed, passed through directly
|
||||
let session = LpSession::new(receiver_index, is_initiator, local_peer, remote_peer, salt)?;
|
||||
let session = LpSession::new(
|
||||
receiver_index,
|
||||
is_initiator,
|
||||
local_peer,
|
||||
remote_peer,
|
||||
salt,
|
||||
protocol_version,
|
||||
)?;
|
||||
|
||||
Ok(LpStateMachine {
|
||||
state: LpState::ReadyToHandshake {
|
||||
@@ -1144,6 +1153,7 @@ impl LpStateMachine {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::packet::version;
|
||||
use crate::peer::mock_peers;
|
||||
|
||||
#[test]
|
||||
@@ -1155,8 +1165,14 @@ mod tests {
|
||||
|
||||
let receiver_index: u32 = 77777;
|
||||
|
||||
let initiator_sm =
|
||||
LpStateMachine::new(receiver_index, true, init.clone(), resp.as_remote(), &salt);
|
||||
let initiator_sm = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
init.clone(),
|
||||
resp.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
);
|
||||
assert!(initiator_sm.is_ok());
|
||||
let initiator_sm = initiator_sm.unwrap();
|
||||
assert!(matches!(
|
||||
@@ -1166,8 +1182,14 @@ mod tests {
|
||||
let init_session = initiator_sm.session().unwrap();
|
||||
assert!(init_session.is_initiator());
|
||||
|
||||
let responder_sm =
|
||||
LpStateMachine::new(receiver_index, false, resp, init.as_remote(), &salt);
|
||||
let responder_sm = LpStateMachine::new(
|
||||
receiver_index,
|
||||
false,
|
||||
resp,
|
||||
init.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
);
|
||||
assert!(responder_sm.is_ok());
|
||||
let responder_sm = responder_sm.unwrap();
|
||||
assert!(matches!(
|
||||
@@ -1196,6 +1218,7 @@ mod tests {
|
||||
init.clone(),
|
||||
resp.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -1205,6 +1228,7 @@ mod tests {
|
||||
resp,
|
||||
init.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -1389,8 +1413,15 @@ mod tests {
|
||||
let receiver_index: u32 = 99901;
|
||||
|
||||
// Create initiator state machine
|
||||
let mut initiator =
|
||||
LpStateMachine::new(receiver_index, true, init, resp.as_remote(), &salt).unwrap();
|
||||
let mut initiator = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
init,
|
||||
resp.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Verify initial state
|
||||
assert!(matches!(initiator.state, LpState::ReadyToHandshake { .. }));
|
||||
@@ -1409,8 +1440,15 @@ mod tests {
|
||||
let receiver_index: u32 = 99902;
|
||||
|
||||
// Create responder state machine
|
||||
let mut responder =
|
||||
LpStateMachine::new(receiver_index, false, resp, init.as_remote(), &salt).unwrap();
|
||||
let mut responder = LpStateMachine::new(
|
||||
receiver_index,
|
||||
false,
|
||||
resp,
|
||||
init.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Verify initial state
|
||||
assert!(matches!(responder.state, LpState::ReadyToHandshake { .. }));
|
||||
@@ -1430,12 +1468,25 @@ mod tests {
|
||||
let receiver_index: u32 = 99903;
|
||||
|
||||
// Create both state machines
|
||||
let mut initiator =
|
||||
LpStateMachine::new(receiver_index, true, init.clone(), resp.as_remote(), &salt)
|
||||
.unwrap();
|
||||
let mut initiator = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
init.clone(),
|
||||
resp.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut responder =
|
||||
LpStateMachine::new(receiver_index, false, resp, init.as_remote(), &salt).unwrap();
|
||||
let mut responder = LpStateMachine::new(
|
||||
receiver_index,
|
||||
false,
|
||||
resp,
|
||||
init.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Step 1: Initiator starts handshake, sends KKT request
|
||||
let init_action = initiator.process_input(LpInput::StartHandshake);
|
||||
@@ -1477,9 +1528,15 @@ mod tests {
|
||||
let receiver_index: u32 = 99904;
|
||||
|
||||
// Create initiator state machine
|
||||
let mut initiator =
|
||||
LpStateMachine::new(receiver_index, true, init.clone(), resp.as_remote(), &salt)
|
||||
.unwrap();
|
||||
let mut initiator = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
init.clone(),
|
||||
resp.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Start handshake to enter KKTExchange state
|
||||
initiator.process_input(LpInput::StartHandshake);
|
||||
@@ -1499,9 +1556,15 @@ mod tests {
|
||||
let receiver_index: u32 = 99905;
|
||||
|
||||
// Create initiator state machine
|
||||
let mut initiator =
|
||||
LpStateMachine::new(receiver_index, true, init.clone(), resp.as_remote(), &salt)
|
||||
.unwrap();
|
||||
let mut initiator = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
init.clone(),
|
||||
resp.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Start handshake to enter KKTExchange state
|
||||
initiator.process_input(LpInput::StartHandshake);
|
||||
@@ -1534,10 +1597,25 @@ mod tests {
|
||||
let receiver_index: u32 = 111111;
|
||||
|
||||
// Create state machines - Alice is initiator, Bob is responder
|
||||
let mut alice =
|
||||
LpStateMachine::new(receiver_index, true, a.clone(), b.as_remote(), &salt).unwrap();
|
||||
let mut alice = LpStateMachine::new(
|
||||
receiver_index,
|
||||
true,
|
||||
a.clone(),
|
||||
b.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut bob = LpStateMachine::new(receiver_index, false, b, a.as_remote(), &salt).unwrap();
|
||||
let mut bob = LpStateMachine::new(
|
||||
receiver_index,
|
||||
false,
|
||||
b,
|
||||
a.as_remote(),
|
||||
&salt,
|
||||
version::CURRENT,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// --- Complete KKT Exchange ---
|
||||
// Alice starts handshake
|
||||
|
||||
@@ -42,6 +42,10 @@ pub struct NymNodeLPInformation {
|
||||
pub expected_kem_key_hashes: HashMap<KEM, KEMKeyDigests>,
|
||||
pub expected_signing_key_hashes: HashMap<SignatureScheme, KEMKeyDigests>,
|
||||
pub x25519: x25519::PublicKey,
|
||||
|
||||
/// Supported protocol version of the remote gateway.
|
||||
/// Included in case we have to downgrade our version.
|
||||
pub lp_protocol_version: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
||||
@@ -292,6 +292,10 @@ where
|
||||
use nym_lp::state_machine::{LpInput, LpStateMachine};
|
||||
let remote = self.remote_addr;
|
||||
|
||||
// if we managed to parse out the header, i.e. we have code supporting whatever
|
||||
// protocol version has been sent - use that one instead
|
||||
let protocol_version = packet.header().protocol_version;
|
||||
|
||||
// Extract ClientHello data
|
||||
let hello_data = match packet.message() {
|
||||
LpMessage::ClientHello(hello_data) => hello_data,
|
||||
@@ -325,8 +329,10 @@ where
|
||||
// Send Collision response to tell client to retry with new receiver_index
|
||||
// No outer key - this is before PSK derivation
|
||||
// Note: Do NOT set binding on collision - client may retry with new receiver_index
|
||||
let collision_packet =
|
||||
LpPacket::new(LpHeader::new(receiver_index, 0), LpMessage::Collision);
|
||||
let collision_packet = LpPacket::new(
|
||||
LpHeader::new(receiver_index, 0, protocol_version),
|
||||
LpMessage::Collision,
|
||||
);
|
||||
self.send_lp_packet(collision_packet, None).await?;
|
||||
|
||||
return Ok(());
|
||||
@@ -344,6 +350,7 @@ where
|
||||
self.state.local_lp_peer.clone(),
|
||||
hello_data.to_remote_peer(),
|
||||
&hello_data.salt,
|
||||
protocol_version,
|
||||
)
|
||||
.map_err(|e| {
|
||||
inc!("lp_client_hello_failed");
|
||||
@@ -374,7 +381,10 @@ where
|
||||
|
||||
// Send Ack to confirm ClientHello received
|
||||
// No outer key - this is before PSK derivation
|
||||
let ack_packet = LpPacket::new(LpHeader::new(receiver_index, 0), LpMessage::Ack);
|
||||
let ack_packet = LpPacket::new(
|
||||
LpHeader::new(receiver_index, 0, protocol_version),
|
||||
LpMessage::Ack,
|
||||
);
|
||||
self.send_lp_packet(ack_packet, None).await?;
|
||||
|
||||
Ok(())
|
||||
@@ -431,13 +441,15 @@ where
|
||||
self.remote_addr, receiver_idx
|
||||
);
|
||||
|
||||
let session = state_entry.value().state.session().map_err(|err| {
|
||||
GatewayError::LpHandshakeError(format!("no session available: {err}"))
|
||||
})?;
|
||||
|
||||
// Get outer key for Ack encryption before releasing borrow
|
||||
let outer_key = state_entry
|
||||
.value()
|
||||
.state
|
||||
.session()
|
||||
.ok()
|
||||
.and_then(|s| s.outer_aead_key());
|
||||
let outer_key = session.outer_aead_key();
|
||||
|
||||
// Get previously negotiated protocol version for header creation
|
||||
let negotiated_version = session.negotiated_version();
|
||||
|
||||
// Move state machine to session_states (already in Transport state)
|
||||
// We keep the state machine (not just session) to enable
|
||||
@@ -461,7 +473,10 @@ where
|
||||
inc!("lp_handshakes_success");
|
||||
|
||||
// Send Ack to confirm handshake completion to the client
|
||||
let ack_packet = LpPacket::new(LpHeader::new(receiver_idx, 0), LpMessage::Ack);
|
||||
let ack_packet = LpPacket::new(
|
||||
LpHeader::new(receiver_idx, 0, negotiated_version),
|
||||
LpMessage::Ack,
|
||||
);
|
||||
trace!(
|
||||
"Moved session {} to transport mode, sending Ack",
|
||||
receiver_idx
|
||||
|
||||
@@ -54,6 +54,7 @@ mod tests {
|
||||
peer: LpLocalPeer,
|
||||
x25519_wg_keys: Arc<x25519::KeyPair>,
|
||||
socket_addr: SocketAddr,
|
||||
lp_version: u8,
|
||||
}
|
||||
|
||||
impl Party {
|
||||
@@ -73,6 +74,7 @@ mod tests {
|
||||
.with_kem_psq_key(lp_x25519_keys),
|
||||
x25519_wg_keys,
|
||||
socket_addr: SocketAddr::from((ip, u16::from_le_bytes(port))),
|
||||
lp_version: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -425,6 +427,7 @@ mod tests {
|
||||
client_data.base.peer.ed25519().clone(),
|
||||
entry.base.peer.as_remote(),
|
||||
entry.base.socket_addr,
|
||||
entry.base.lp_version,
|
||||
);
|
||||
|
||||
// 1. establish mock connection between client and gateway and retrieve gateway's handle
|
||||
@@ -524,6 +527,7 @@ mod tests {
|
||||
client_data.base.peer.ed25519().clone(),
|
||||
entry.base.peer.as_remote(),
|
||||
entry.base.socket_addr,
|
||||
entry.base.lp_version,
|
||||
);
|
||||
|
||||
// 1. establish mock connection between client and gateway and retrieve gateway's handle
|
||||
@@ -588,6 +592,7 @@ mod tests {
|
||||
client_data.base.peer.ed25519().clone(),
|
||||
entry.base.peer.as_remote(),
|
||||
entry.base.socket_addr,
|
||||
entry.base.lp_version,
|
||||
);
|
||||
|
||||
// START: ENTRY SETUP
|
||||
@@ -703,6 +708,7 @@ mod tests {
|
||||
exit.base.socket_addr.to_string(),
|
||||
client_data.base.peer.ed25519().clone(),
|
||||
exit.base.peer.as_remote(),
|
||||
exit.base.lp_version,
|
||||
);
|
||||
|
||||
// 13. Perform handshake and registration with exit gateway (all via entry forwarding)
|
||||
|
||||
@@ -14,6 +14,7 @@ use nym_crypto::asymmetric::x25519;
|
||||
use nym_http_api_client::UserAgent;
|
||||
use nym_kkt_ciphersuite::SignatureScheme;
|
||||
use nym_kkt_ciphersuite::{KEM, KEMKeyDigests};
|
||||
use nym_lp::packet::version;
|
||||
use nym_network_defaults::DEFAULT_NYM_NODE_HTTP_PORT;
|
||||
use nym_node_requests::api::client::NymNodeApiClientExt;
|
||||
use nym_node_requests::api::v1::node::models::AuxiliaryDetails as NodeAuxiliaryDetails;
|
||||
@@ -28,7 +29,6 @@ use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use tracing::{debug, info, warn};
|
||||
use url::Url;
|
||||
|
||||
// in the old behaviour we were getting all skimmed nodes to retrieve performance
|
||||
// that was ultimately unused
|
||||
// should we want to use it again, the code is commented out below
|
||||
@@ -135,6 +135,8 @@ impl DirectoryNode {
|
||||
expected_kem_key_hashes: lp_data.kem_keys()?,
|
||||
expected_signing_key_hashes: lp_data.signing_keys()?,
|
||||
x25519: noise_key.x25519_pubkey,
|
||||
// \/ TODO: proper derivation from build version
|
||||
lp_version: version::CURRENT,
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
@@ -480,6 +482,7 @@ pub struct TestedNodeLpDetails {
|
||||
pub expected_kem_key_hashes: HashMap<KEM, KEMKeyDigests>,
|
||||
pub expected_signing_key_hashes: HashMap<SignatureScheme, KEMKeyDigests>,
|
||||
pub x25519: x25519::PublicKey,
|
||||
pub lp_version: u8,
|
||||
}
|
||||
|
||||
impl TestedNodeDetails {
|
||||
|
||||
@@ -163,6 +163,7 @@ pub async fn lp_registration_probe(
|
||||
bandwidth_controller: &dyn BandwidthTicketProvider,
|
||||
) -> anyhow::Result<LpProbeResults> {
|
||||
let lp_address = gateway_lp_data.address;
|
||||
let lp_version = gateway_lp_data.lp_version;
|
||||
let peer = helpers::to_lp_remote_peer(gateway_identity, gateway_lp_data);
|
||||
|
||||
info!("Starting LP registration probe for gateway at {lp_address}",);
|
||||
@@ -180,6 +181,7 @@ pub async fn lp_registration_probe(
|
||||
client_ed25519_keypair,
|
||||
peer,
|
||||
lp_address,
|
||||
lp_version,
|
||||
);
|
||||
|
||||
// Step 1: Perform handshake (connection is implicit in packet-per-connection model)
|
||||
@@ -289,6 +291,9 @@ pub async fn wg_probe_lp(
|
||||
let entry_address = entry_lp_data.address;
|
||||
let exit_address = exit_lp_data.address;
|
||||
|
||||
let entry_lp_version = entry_lp_data.lp_version;
|
||||
let exit_lp_version = exit_lp_data.lp_version;
|
||||
|
||||
let entry_ip = entry_address.ip();
|
||||
let exit_ip = exit_address.ip();
|
||||
|
||||
@@ -316,6 +321,7 @@ pub async fn wg_probe_lp(
|
||||
entry_lp_keypair,
|
||||
entry_peer,
|
||||
entry_address,
|
||||
entry_lp_version,
|
||||
);
|
||||
|
||||
// Perform handshake with entry gateway (connection is implicit)
|
||||
@@ -327,8 +333,12 @@ pub async fn wg_probe_lp(
|
||||
|
||||
// STEP 2: Use nested session to register with exit gateway via forwarding
|
||||
info!("Registering with exit gateway via entry forwarding...");
|
||||
let mut nested_session =
|
||||
NestedLpSession::new(exit_address.to_string(), exit_lp_keypair, exit_peer);
|
||||
let mut nested_session = NestedLpSession::new(
|
||||
exit_address.to_string(),
|
||||
exit_lp_keypair,
|
||||
exit_peer,
|
||||
exit_lp_version,
|
||||
);
|
||||
|
||||
// Convert exit gateway identity to ed25519 public key for registration
|
||||
let exit_gateway_pubkey = ed25519::PublicKey::from_bytes(&exit_gateway.identity.to_bytes())
|
||||
|
||||
@@ -317,6 +317,7 @@ pub(crate) async fn run() -> anyhow::Result<ProbeResult> {
|
||||
expected_kem_key_hashes,
|
||||
expected_signing_key_hashes: todo!(),
|
||||
x25519: x25519_key,
|
||||
lp_version: todo!(),
|
||||
};
|
||||
let entry_details = TestedNodeDetails::from_cli(identity, entry_lp_node);
|
||||
|
||||
@@ -348,8 +349,9 @@ pub(crate) async fn run() -> anyhow::Result<ProbeResult> {
|
||||
let exit_lp_node = TestedNodeLpDetails {
|
||||
address: exit_lp_addr,
|
||||
expected_kem_key_hashes,
|
||||
expected_signing_key_hashes: Default::default(),
|
||||
expected_signing_key_hashes: todo!(),
|
||||
x25519: x25519_key,
|
||||
lp_version: todo!(),
|
||||
};
|
||||
|
||||
Some(TestedNodeDetails::from_cli(identity, exit_lp_node))
|
||||
|
||||
@@ -175,6 +175,9 @@ impl RegistrationClient {
|
||||
},
|
||||
)?;
|
||||
|
||||
let entry_lp_protocol = entry_lp_data.lp_protocol_version;
|
||||
let exit_lp_protocol = exit_lp_data.lp_protocol_version;
|
||||
|
||||
let entry_address = entry_lp_data.address;
|
||||
let exit_address = exit_lp_data.address;
|
||||
|
||||
@@ -197,6 +200,7 @@ impl RegistrationClient {
|
||||
entry_lp_keypair.clone(),
|
||||
entry_peer,
|
||||
entry_address,
|
||||
entry_lp_protocol,
|
||||
);
|
||||
|
||||
// Perform handshake with entry gateway (outer session now established)
|
||||
@@ -213,8 +217,12 @@ impl RegistrationClient {
|
||||
// STEP 2: Use nested session to register with exit gateway via forwarding
|
||||
// This hides the client's IP address from the exit gateway
|
||||
tracing::info!("Registering with exit gateway via entry forwarding");
|
||||
let mut nested_session =
|
||||
NestedLpSession::new(exit_address.to_string(), exit_lp_keypair, exit_peer);
|
||||
let mut nested_session = NestedLpSession::new(
|
||||
exit_address.to_string(),
|
||||
exit_lp_keypair,
|
||||
exit_peer,
|
||||
exit_lp_protocol,
|
||||
);
|
||||
|
||||
// Perform handshake and registration with exit gateway (all via entry forwarding)
|
||||
let exit_gateway_data = nested_session
|
||||
|
||||
@@ -18,6 +18,7 @@ use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_lp::LpPacket;
|
||||
use nym_lp::codec::{OuterAeadKey, parse_lp_packet, serialize_lp_packet};
|
||||
use nym_lp::message::ForwardPacketData;
|
||||
use nym_lp::packet::version;
|
||||
use nym_lp::peer::{LpLocalPeer, LpRemotePeer};
|
||||
use nym_lp::state_machine::{LpAction, LpData, LpInput, LpStateMachine};
|
||||
use nym_lp_transport::traits::LpTransport;
|
||||
@@ -31,6 +32,7 @@ use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use tokio::net::TcpStream;
|
||||
use tracing::warn;
|
||||
|
||||
/// LP (Lewes Protocol) registration client for direct gateway connections.
|
||||
///
|
||||
@@ -55,6 +57,10 @@ pub struct LpRegistrationClient<S = TcpStream> {
|
||||
/// Gateway LP listener address (host:port, e.g., "1.1.1.1:41264").
|
||||
gateway_lp_address: SocketAddr,
|
||||
|
||||
/// Supported protocol version of the remote gateway.
|
||||
/// Included in case we have to downgrade our version.
|
||||
gateway_supported_lp_protocol_version: u8,
|
||||
|
||||
/// LP state machine for managing connection lifecycle.
|
||||
/// Created during handshake initiation. Persists across packet-per-connection calls.
|
||||
state_machine: Option<LpStateMachine>,
|
||||
@@ -77,6 +83,7 @@ where
|
||||
/// * `local_ed25519_keypair` - Client's Ed25519 identity keypair
|
||||
/// * `gateway_lp_peer` - Encapsulates all the gateway keys needed for the Lewes Protocol
|
||||
/// * `gateway_lp_address` - Gateway's LP listener socket address
|
||||
/// * `gateway_supported_lp_protocol_version` - Gateway's LP protocol version
|
||||
/// * `config` - Configuration for timeouts and TCP parameters (use `LpConfig::default()`)
|
||||
///
|
||||
/// # Note
|
||||
@@ -85,20 +92,58 @@ where
|
||||
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||
gateway_lp_peer: LpRemotePeer,
|
||||
gateway_lp_address: SocketAddr,
|
||||
gateway_supported_lp_protocol_version: u8,
|
||||
config: LpConfig,
|
||||
) -> Self {
|
||||
let lp_protocol = if gateway_supported_lp_protocol_version > version::CURRENT {
|
||||
warn!(
|
||||
"suggested LP protocol ({gateway_supported_lp_protocol_version}) is higher than the current known version. attempting to downgrade it to {}",
|
||||
version::CURRENT
|
||||
);
|
||||
version::CURRENT
|
||||
} else {
|
||||
gateway_supported_lp_protocol_version
|
||||
};
|
||||
|
||||
let local_x25519_keypair = local_ed25519_keypair.to_x25519();
|
||||
let lp_local_peer = LpLocalPeer::new(local_ed25519_keypair, Arc::new(local_x25519_keypair));
|
||||
Self {
|
||||
lp_local_peer,
|
||||
gateway_lp_peer,
|
||||
gateway_lp_address,
|
||||
gateway_supported_lp_protocol_version: lp_protocol,
|
||||
state_machine: None,
|
||||
config,
|
||||
stream: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new LP registration client with default configuration.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `local_ed25519_keypair` - Client's Ed25519 identity keypair
|
||||
/// * `gateway_lp_peer` - Encapsulates all the gateway keys needed for the Lewes Protocol
|
||||
/// * `gateway_lp_address` - Gateway's LP listener socket address
|
||||
/// * `gateway_supported_lp_protocol_version` - Gateway's LP protocol version
|
||||
///
|
||||
/// Uses default config (LpConfig::default()) with sane timeout and TCP parameters.
|
||||
/// PSK is derived automatically during handshake inside the state machine.
|
||||
/// For custom config, use `new()` directly.
|
||||
pub fn new_with_default_config(
|
||||
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||
gateway_lp_peer: LpRemotePeer,
|
||||
gateway_lp_address: SocketAddr,
|
||||
gateway_supported_lp_protocol_version: u8,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
local_ed25519_keypair,
|
||||
gateway_lp_peer,
|
||||
gateway_lp_address,
|
||||
gateway_supported_lp_protocol_version,
|
||||
LpConfig::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn state_machine(&self) -> Result<&LpStateMachine> {
|
||||
self.state_machine.as_ref().ok_or_else(|| {
|
||||
LpClientError::transport(
|
||||
@@ -121,30 +166,6 @@ where
|
||||
.ok_or_else(|| LpClientError::transport("Cannot send: not connected"))
|
||||
}
|
||||
|
||||
/// Creates a new LP registration client with default configuration.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `local_ed25519_keypair` - Client's Ed25519 identity keypair
|
||||
/// * `gateway_lp_peer` - Encapsulates all the gateway keys needed for the Lewes Protocol
|
||||
/// * `gateway_lp_address` - Gateway's LP listener socket address
|
||||
/// * `client_ip` - Client IP address for registration
|
||||
///
|
||||
/// Uses default config (LpConfig::default()) with sane timeout and TCP parameters.
|
||||
/// PSK is derived automatically during handshake inside the state machine.
|
||||
/// For custom config, use `new()` directly.
|
||||
pub fn new_with_default_config(
|
||||
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||
gateway_lp_peer: LpRemotePeer,
|
||||
gateway_lp_address: SocketAddr,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
local_ed25519_keypair,
|
||||
gateway_lp_peer,
|
||||
gateway_lp_address,
|
||||
LpConfig::default(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns whether the client has completed the handshake and is ready for registration.
|
||||
pub fn is_handshake_complete(&self) -> bool {
|
||||
self.state_machine
|
||||
@@ -418,6 +439,7 @@ where
|
||||
let client_hello_header = nym_lp::packet::LpHeader::new(
|
||||
nym_lp::BOOTSTRAP_RECEIVER_IDX, // session_id not yet established
|
||||
0, // counter starts at 0
|
||||
self.gateway_supported_lp_protocol_version,
|
||||
);
|
||||
let client_hello_packet = nym_lp::LpPacket::new(
|
||||
client_hello_header,
|
||||
@@ -431,6 +453,8 @@ where
|
||||
let ack_response = self.try_receive_packet_with_key(None).await?;
|
||||
|
||||
// Verify we received Ack
|
||||
// this confirms that gateway is fine with our suggested protocol version
|
||||
// in the future we probably have some fancier negotiation
|
||||
match ack_response.message() {
|
||||
nym_lp::LpMessage::Ack => {
|
||||
tracing::debug!("Received Ack for ClientHello");
|
||||
@@ -451,6 +475,7 @@ where
|
||||
self.lp_local_peer.clone(),
|
||||
self.gateway_lp_peer.clone(),
|
||||
&salt,
|
||||
self.gateway_supported_lp_protocol_version,
|
||||
)?;
|
||||
|
||||
// Step 4: Start handshake - get first packet to send (KKT request)
|
||||
@@ -1201,6 +1226,7 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_lp::packet::version;
|
||||
|
||||
#[test]
|
||||
fn test_client_creation() {
|
||||
@@ -1216,6 +1242,7 @@ mod tests {
|
||||
keypair,
|
||||
gateway_peer,
|
||||
address,
|
||||
version::CURRENT,
|
||||
);
|
||||
|
||||
assert!(!client.is_handshake_complete());
|
||||
|
||||
@@ -30,6 +30,7 @@ use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_lp::codec::{OuterAeadKey, parse_lp_packet};
|
||||
use nym_lp::message::ForwardPacketData;
|
||||
use nym_lp::packet::version;
|
||||
use nym_lp::peer::{LpLocalPeer, LpRemotePeer};
|
||||
use nym_lp::state_machine::{LpAction, LpData, LpInput, LpStateMachine};
|
||||
use nym_lp::{LpMessage, LpPacket};
|
||||
@@ -42,6 +43,7 @@ use nym_wireguard_types::PeerPublicKey;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tracing::warn;
|
||||
|
||||
/// Manages a nested LP session where the client establishes a handshake with
|
||||
/// an exit gateway by forwarding packets through an entry gateway.
|
||||
@@ -73,6 +75,10 @@ pub struct NestedLpSession {
|
||||
/// Encapsulates all the exit gateway keys needed for the Lewes Protocol.
|
||||
gateway_lp_peer: LpRemotePeer,
|
||||
|
||||
/// Supported protocol version of the remote gateway.
|
||||
/// Included in case we have to downgrade our version.
|
||||
gateway_supported_lp_protocol_version: u8,
|
||||
|
||||
/// LP state machine for exit gateway session (populated after handshake)
|
||||
state_machine: Option<LpStateMachine>,
|
||||
}
|
||||
@@ -84,18 +90,31 @@ impl NestedLpSession {
|
||||
/// * `exit_address` - Exit gateway's LP address (e.g., "2.2.2.2:41264")
|
||||
/// * `client_keypair` - Client's Ed25519 keypair
|
||||
/// * `gateway_lp_peer` - Encapsulates all the gateway keys needed for the Lewes Protocol
|
||||
/// * `gateway_supported_lp_protocol_version` - Gateway's LP protocol version
|
||||
pub fn new(
|
||||
exit_address: String,
|
||||
client_keypair: Arc<ed25519::KeyPair>,
|
||||
gateway_lp_peer: LpRemotePeer,
|
||||
gateway_supported_lp_protocol_version: u8,
|
||||
) -> Self {
|
||||
let local_x25519_keypair = client_keypair.to_x25519();
|
||||
let lp_local_peer = LpLocalPeer::new(client_keypair, Arc::new(local_x25519_keypair));
|
||||
|
||||
let lp_protocol = if gateway_supported_lp_protocol_version > version::CURRENT {
|
||||
warn!(
|
||||
"suggested LP protocol ({gateway_supported_lp_protocol_version}) is higher than the current known version. attempting to downgrade it to {}",
|
||||
version::CURRENT
|
||||
);
|
||||
version::CURRENT
|
||||
} else {
|
||||
gateway_supported_lp_protocol_version
|
||||
};
|
||||
|
||||
Self {
|
||||
exit_address,
|
||||
lp_local_peer,
|
||||
gateway_lp_peer,
|
||||
gateway_supported_lp_protocol_version: lp_protocol,
|
||||
state_machine: None,
|
||||
}
|
||||
}
|
||||
@@ -194,6 +213,7 @@ impl NestedLpSession {
|
||||
let client_hello_header = nym_lp::packet::LpHeader::new(
|
||||
nym_lp::BOOTSTRAP_RECEIVER_IDX, // Use constant for bootstrap session
|
||||
0, // counter starts at 0
|
||||
self.gateway_supported_lp_protocol_version,
|
||||
);
|
||||
let client_hello_packet = nym_lp::LpPacket::new(
|
||||
client_hello_header,
|
||||
@@ -213,6 +233,8 @@ impl NestedLpSession {
|
||||
.await?;
|
||||
|
||||
// Parse and validate Ack response (cleartext, no outer key before PSK derivation)
|
||||
// this confirms that gateway is fine with our suggested protocol version
|
||||
// in the future we probably have some fancier negotiation
|
||||
let ack_response = Self::parse_packet(&response_bytes, None)?;
|
||||
match ack_response.message() {
|
||||
LpMessage::Ack => {
|
||||
@@ -238,6 +260,7 @@ impl NestedLpSession {
|
||||
self.lp_local_peer.clone(),
|
||||
self.gateway_lp_peer.clone(),
|
||||
&salt,
|
||||
self.gateway_supported_lp_protocol_version,
|
||||
)?;
|
||||
|
||||
// Step 4: Get initial packet from StartHandshake
|
||||
|
||||
@@ -32,6 +32,7 @@ use tracing::{debug, info, trace};
|
||||
|
||||
use crate::topology::{GatewayInfo, SpeedtestTopology};
|
||||
use nym_ip_packet_requests::v8::request::IpPacketRequest;
|
||||
use nym_lp::packet::version;
|
||||
use nym_lp::peer::LpRemotePeer;
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
|
||||
@@ -127,6 +128,7 @@ impl SpeedtestClient {
|
||||
self.identity_keypair.clone(),
|
||||
gw_peer,
|
||||
self.gateway.lp_address,
|
||||
self.gateway.lp_version,
|
||||
);
|
||||
|
||||
let start = Instant::now();
|
||||
@@ -173,6 +175,7 @@ impl SpeedtestClient {
|
||||
self.identity_keypair.clone(),
|
||||
gw_peer,
|
||||
self.gateway.lp_address,
|
||||
self.gateway.lp_version,
|
||||
);
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
@@ -40,6 +40,7 @@ pub struct GatewayInfo {
|
||||
pub lp_address: SocketAddr,
|
||||
/// LP data address (IP:51264) for Sphinx packets wrapped in LP
|
||||
pub lp_data_address: SocketAddr,
|
||||
pub lp_version: u8,
|
||||
}
|
||||
|
||||
/// Topology for routing Sphinx packets
|
||||
|
||||
Reference in New Issue
Block a user