diff --git a/common/nym-lp/src/peer_config.rs b/common/nym-lp/src/peer_config.rs index 02b1af34a4..d28dd6bc23 100644 --- a/common/nym-lp/src/peer_config.rs +++ b/common/nym-lp/src/peer_config.rs @@ -65,6 +65,7 @@ impl LpPeerConfig { rng.random(), ) } + /// Creates a new client to exit config. /// Inputs: /// hop_id: this value must be in the range (1..=15). This function returns an error if this is not the case. @@ -79,6 +80,7 @@ impl LpPeerConfig { { Self::new(rng, hop_id, true, false, censorship_resistance) } + /// Creates a new client to an intermediate node config. /// Inputs: /// hop_id: this value must be in the range (1..=14). This function returns an error if this is not the case. @@ -130,6 +132,7 @@ impl LpPeerConfig { rng.random(), ) } + fn build( hop_id: u8, is_exit: bool, @@ -147,6 +150,7 @@ impl LpPeerConfig { seed, } } + fn build_checked( hop_id: u8, is_exit: bool, @@ -203,6 +207,7 @@ impl LpPeerConfig { output_bytes[4..].copy_from_slice(&self.seed); output_bytes } + pub fn deserialize(bytes: &[u8]) -> Result { if bytes.len() != LP_PEER_CONFIG_SIZE { return Err(LpError::DeserializationError(format!( diff --git a/common/nym-lp/src/psq/initiator.rs b/common/nym-lp/src/psq/initiator.rs index 81fd571e01..7abc75be3b 100644 --- a/common/nym-lp/src/psq/initiator.rs +++ b/common/nym-lp/src/psq/initiator.rs @@ -24,10 +24,31 @@ use nym_kkt::message::{KKTRequest, KKTResponse}; use rand09::SeedableRng; use tracing::debug; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HandshakeMode { + // Client <> Entry + OneWayEntry, + + // Client <> Exit + OneWayExit, + + // Entry <> Exit + MutualInternode, + // in the future more variants will be supported (such as individual mix hops) +} + +impl HandshakeMode { + pub fn is_mutual(&self) -> bool { + matches!(self, HandshakeMode::MutualInternode) + } +} + pub struct PSQHandshakeStateInitiator<'a, S> { pub(super) inner_state: PSQHandshakeState<'a, S>, pub(super) initiator_data: InitiatorData, - pub(super) mutual: bool, + + /// The mode of the handshake (mutual node-node, client-entry, entry-exit) + pub(super) mode: HandshakeMode, } pub(crate) fn build_psq_principal( @@ -78,13 +99,23 @@ impl<'a, S> PSQHandshakeStateInitiator<'a, S> where S: LpHandshakeChannel + Unpin, { - pub fn set_mutual_kkt(mut self) -> Result { - if self.inner_state.local_peer.kem_keypairs.is_none() { - return Err(LpError::PSQMutualInitiatorMissingKemKey); - } + fn lp_peer_config(&self, rng: &mut R) -> Result + where + R: rand09::CryptoRng, + { + // for now we don't support censorship resistance flag + let censorship_resistance = false; - self.mutual = true; - Ok(self) + match self.mode { + HandshakeMode::OneWayEntry => Ok(LpPeerConfig::new_client_to_entry( + rng, + censorship_resistance, + )), + HandshakeMode::OneWayExit => { + LpPeerConfig::new_client_to_exit(rng, 1, censorship_resistance) + } + HandshakeMode::MutualInternode => LpPeerConfig::new_node_to_node(rng), + } } /// Attempt to send KKT request to begin the handshake @@ -132,7 +163,7 @@ where let ciphersuite = self.inner_state.local_peer.ciphersuite(); let kem = ciphersuite.kem(); - let lp_peer_config = LpPeerConfig::new_client_to_entry(rng, false); + let lp_peer_config = self.lp_peer_config(rng)?; // 1. retrieve the expected kem key hash. if we don't know it, let dir_hash = self @@ -141,7 +172,7 @@ where .expected_kem_key_hash(ciphersuite)?; // 2. prepare and send KKT request - let (mut initiator, kkt_request) = if self.mutual { + let (mut initiator, kkt_request) = if self.mode.is_mutual() { // this has been verified when setting the mutual flag let Some(local_encapsulation_key) = self.inner_state.local_peer.encoded_kem_key(kem) else { @@ -273,8 +304,8 @@ mod tests { resp.ciphersuite = ciphersuite; let initiator_data = InitiatorData::new(1, resp_remote); - let handshake_init = - PSQHandshakeState::new(conn_init, init).as_initiator(initiator_data); + let handshake_init = PSQHandshakeState::new(conn_init, init) + .as_initiator(initiator_data, HandshakeMode::OneWayEntry)?; let mut init_rng = DeterministicRng09Send::new(u64_seeded_rng_09(1)); @@ -396,8 +427,7 @@ mod tests { let initiator_data = InitiatorData::new(1, resp_remote); let handshake_init = PSQHandshakeState::new(conn_init, init) - .as_initiator(initiator_data) - .set_mutual_kkt()?; + .as_initiator(initiator_data, HandshakeMode::MutualInternode)?; let mut init_rng = DeterministicRng09Send::new(u64_seeded_rng_09(1)); diff --git a/common/nym-lp/src/psq/mod.rs b/common/nym-lp/src/psq/mod.rs index 7302e30c3a..0386e62880 100644 --- a/common/nym-lp/src/psq/mod.rs +++ b/common/nym-lp/src/psq/mod.rs @@ -12,6 +12,8 @@ mod helpers; pub mod initiator; pub mod responder; +use crate::LpError; +use crate::psq::initiator::HandshakeMode; pub use initiator::PSQHandshakeStateInitiator; pub use responder::PSQHandshakeStateResponder; @@ -107,12 +109,20 @@ where } } - pub fn as_initiator(self, initiator_data: InitiatorData) -> PSQHandshakeStateInitiator<'a, S> { - PSQHandshakeStateInitiator { + pub fn as_initiator( + self, + initiator_data: InitiatorData, + mode: HandshakeMode, + ) -> Result, LpError> { + if mode.is_mutual() && self.local_peer.kem_keypairs.is_none() { + return Err(LpError::PSQMutualInitiatorMissingKemKey); + } + + Ok(PSQHandshakeStateInitiator { initiator_data, inner_state: self, - mutual: false, - } + mode, + }) } pub fn as_responder(self, responder_data: ResponderData) -> PSQHandshakeStateResponder<'a, S> { @@ -160,8 +170,10 @@ mod tests { resp.ciphersuite = ciphersuite; let resp_remote = resp.as_remote(); - let handshake_init = PSQHandshakeState::new(conn_init, init) - .as_initiator(InitiatorData::new(1, resp_remote)); + let handshake_init = PSQHandshakeState::new(conn_init, init).as_initiator( + InitiatorData::new(1, resp_remote), + HandshakeMode::OneWayEntry, + )?; let handshake_resp = PSQHandshakeState::new(conn_resp, resp).as_responder(ResponderData::default()); @@ -232,9 +244,10 @@ mod tests { let resp_remote = resp.as_remote(); let init_remote = init.as_remote(); - let handshake_init = PSQHandshakeState::new(conn_init, init) - .as_initiator(InitiatorData::new(1, resp_remote)) - .set_mutual_kkt()?; + let handshake_init = PSQHandshakeState::new(conn_init, init).as_initiator( + InitiatorData::new(1, resp_remote), + HandshakeMode::MutualInternode, + )?; let handshake_resp = PSQHandshakeState::new(conn_resp, resp).as_responder( ResponderData::default() .with_initiator_kem_hashes(init_remote.expected_kem_key_digests), diff --git a/common/nym-lp/src/session.rs b/common/nym-lp/src/session.rs index 3d0d51efdd..0d02133374 100644 --- a/common/nym-lp/src/session.rs +++ b/common/nym-lp/src/session.rs @@ -9,6 +9,7 @@ use crate::codec::{decrypt_lp_packet, encrypt_lp_packet}; use crate::packet::{EncryptedLpPacket, LpHeader, LpMessage, LpPacket}; use crate::peer::{LpLocalPeer, LpRemotePeer}; use crate::peer_config::LpReceiverIndex; +use crate::psq::initiator::HandshakeMode; use crate::psq::{ InitiatorData, PSQHandshakeState, PSQHandshakeStateInitiator, PSQHandshakeStateResponder, ResponderData, @@ -154,12 +155,34 @@ impl LpTransportSession { local_peer: LpLocalPeer, remote_peer: LpRemotePeer, remote_protocol_version: u8, - ) -> PSQHandshakeStateInitiator<'_, S> + mode: HandshakeMode, + ) -> Result, LpError> where S: LpHandshakeChannel + Unpin, { - PSQHandshakeState::new(connection, local_peer) - .as_initiator(InitiatorData::new(remote_protocol_version, remote_peer)) + PSQHandshakeState::new(connection, local_peer).as_initiator( + InitiatorData::new(remote_protocol_version, remote_peer), + mode, + ) + } + + /// Helper function to create `PSQHandshakeState` for the handshake initiator for mutual KKT + pub fn psq_handshake_initiator_mutual_internode( + connection: &'_ mut S, + local_peer: LpLocalPeer, + remote_peer: LpRemotePeer, + remote_protocol_version: u8, + ) -> Result, LpError> + where + S: LpHandshakeChannel + Unpin, + { + Self::psq_handshake_initiator( + connection, + local_peer, + remote_peer, + remote_protocol_version, + HandshakeMode::MutualInternode, + ) } /// Helper function to create `PSQHandshakeState` for the handshake responder diff --git a/integration-tests/src/lp_registration.rs b/integration-tests/src/lp_registration.rs index ab9ef91ebc..875109dc77 100644 --- a/integration-tests/src/lp_registration.rs +++ b/integration-tests/src/lp_registration.rs @@ -17,9 +17,10 @@ mod tests { use nym_lp::peer::LpLocalPeer; use nym_node::config::{LpConfig, LpDebug}; use nym_node::node::GatewayStorage; - use nym_node::node::lp::control::ingress::client_handler::LpConnectionHandler; + use nym_node::node::lp::control::ingress::client_handler::LpClientConnectionHandler; use nym_node::node::lp::error::LpHandlerError; - use nym_node::node::lp::{SharedLpControlState, SharedLpState}; + use nym_node::node::lp::state::{ActiveLpSessions, NestedConnectionsManager}; + use nym_node::node::lp::{SharedLpClientControlState, SharedLpState}; use nym_node::wireguard::{PeerManager, PeerRegistrator}; use nym_registration_client::{LpClientError, LpRegistrationClient}; use nym_test_utils::helpers::{CryptoRng09, seeded_rng}; @@ -35,7 +36,7 @@ mod tests { use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::sync::Arc; use tokio::sync::Semaphore; - use tokio::sync::mpsc::Receiver; + use tokio::sync::mpsc::{Receiver, channel}; use tokio::task::JoinHandle; use tokio_util::sync::CancellationToken; use tracing::error; @@ -120,7 +121,7 @@ mod tests { enum SpawnedLpConnectionHandlerState { NotCreated, Ready { - handler: LpConnectionHandler, + handler: LpClientConnectionHandler, }, Running { handle: JoinHandle>>, @@ -130,7 +131,7 @@ mod tests { struct Gateway { base: Party, - lp_state: SharedLpControlState, + lp_state: SharedLpClientControlState, ip_pool: IpPool, mock_peer_controller: SpawnedPeerController, @@ -216,6 +217,9 @@ mod tests { let (mock_peer_controller, peer_controller_state) = mock_peer_controller(peer_request_rx); + let (connection_ctrl_sender, _connection_manager_receiver) = channel(42); + let nested_connections_manager = NestedConnectionsManager::new(connection_ctrl_sender); + // registering particular responses for peer controller is up to given test let ecash_verifier = Arc::new(ecash_verifier); @@ -225,7 +229,7 @@ mod tests { upgrade_mode_details, ); - let lp_state = SharedLpControlState { + let lp_state = SharedLpClientControlState { local_lp_peer: base.peer.clone(), forward_semaphore, @@ -235,8 +239,9 @@ mod tests { shared: SharedLpState { metrics: Default::default(), lp_config, - session_states: Arc::new(Default::default()), + session_states: ActiveLpSessions::new(), }, + nested_connections_manager, }; Ok(Gateway { @@ -262,7 +267,7 @@ mod tests { }; self.lp_connection_handler = SpawnedLpConnectionHandlerState::Ready { - handler: LpConnectionHandler::new( + handler: LpClientConnectionHandler::new( client_connection, client_address, self.lp_state.clone(), @@ -290,7 +295,7 @@ mod tests { } fn spawn_lp_handler(&mut self) { - let SpawnedLpConnectionHandlerState::Ready { handler } = mem::replace( + let SpawnedLpConnectionHandlerState::Ready { mut handler } = mem::replace( &mut self.lp_connection_handler, SpawnedLpConnectionHandlerState::NotCreated, ) else { diff --git a/nym-registration-client/src/lp_client/client.rs b/nym-registration-client/src/lp_client/client.rs index 53231ba5a3..c1de9db024 100644 --- a/nym-registration-client/src/lp_client/client.rs +++ b/nym-registration-client/src/lp_client/client.rs @@ -14,6 +14,7 @@ use nym_crypto::asymmetric::{ed25519, x25519}; use nym_lp::LpTransportSession; use nym_lp::peer::{DHKeyPair, LpLocalPeer, LpRemotePeer}; use nym_lp::peer_config::LpReceiverIndex; +use nym_lp::psq::initiator::HandshakeMode; use nym_lp::transport::traits::LpTransportChannel; use nym_lp::transport::{LpHandshakeChannel, LpTransportError}; use nym_lp::{Ciphersuite, packet::EncryptedLpPacket, packet::version}; @@ -396,7 +397,8 @@ where local_peer, remote_peer, protocol_version, - ) + HandshakeMode::OneWayEntry, + )? .complete_handshake() .await?; diff --git a/nym-registration-client/src/lp_client/nested_session/mod.rs b/nym-registration-client/src/lp_client/nested_session/mod.rs index c1a45f6fbe..5cf8e98434 100644 --- a/nym-registration-client/src/lp_client/nested_session/mod.rs +++ b/nym-registration-client/src/lp_client/nested_session/mod.rs @@ -28,6 +28,7 @@ use nym_crypto::asymmetric::{ed25519, x25519}; use nym_lp::packet::version; use nym_lp::packet::{EncryptedLpPacket, LpMessage}; use nym_lp::peer::{DHKeyPair, LpLocalPeer, LpRemotePeer}; +use nym_lp::psq::initiator::HandshakeMode; use nym_lp::transport::LpHandshakeChannel; use nym_lp::transport::traits::LpTransportChannel; use nym_lp::{Ciphersuite, KEM, LpTransportSession}; @@ -185,7 +186,8 @@ impl NestedLpSession { local_peer, remote_peer, protocol_version, - ) + HandshakeMode::OneWayExit, + )? .complete_handshake() .await?;