From 331a0328c726f856a15e1b9bbf4bf18ef6302416 Mon Sep 17 00:00:00 2001 From: Simon Wicky Date: Fri, 28 Jul 2023 12:09:13 +0200 Subject: [PATCH] remove absolute dependence on topology --- .../client-libs/mixnet-client/src/client.rs | 17 ++-- .../mixnet-client/src/forwarder.rs | 5 +- common/nymnoise/src/lib.rs | 92 +++++++++++++------ .../client_handling/websocket/listener.rs | 75 +++++++++------ .../receiver/connection_handler.rs | 21 ++--- gateway/src/node/mod.rs | 5 +- .../node/listener/connection_handler/mod.rs | 18 ++-- mixnode/src/node/mod.rs | 8 +- 8 files changed, 146 insertions(+), 95 deletions(-) diff --git a/common/client-libs/mixnet-client/src/client.rs b/common/client-libs/mixnet-client/src/client.rs index d7b3014b04..f5871774db 100644 --- a/common/client-libs/mixnet-client/src/client.rs +++ b/common/client-libs/mixnet-client/src/client.rs @@ -1,13 +1,12 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::client::encryption::{PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE}; use futures::channel::mpsc; use futures::StreamExt; use log::*; use nym_client_core::client::topology_control::accessor::TopologyAccessor; use nym_crypto::asymmetric::encryption; -use nym_noise::upgrade_noise_initiator; +use nym_noise::upgrade_noise_initiator_with_topology; use nym_sphinx::addressing::nodes::NymNodeRoutingAddress; use nym_sphinx::framing::codec::NymCodec; use nym_sphinx::framing::packet::FramedNymPacket; @@ -64,8 +63,7 @@ pub struct Client { conn_new: HashMap, config: Config, topology_access: TopologyAccessor, - public_id_key: [u8; PUBLIC_KEY_SIZE], - private_id_key: [u8; PRIVATE_KEY_SIZE], + local_identity: Arc, } struct ConnectionSender { @@ -86,14 +84,13 @@ impl Client { pub fn new( config: Config, topology_access: TopologyAccessor, - id_key: &encryption::KeyPair, + local_identity: Arc, ) -> Client { Client { conn_new: HashMap::new(), config, topology_access, - public_id_key: id_key.public_key().to_bytes(), - private_id_key: id_key.private_key().to_bytes(), + local_identity, } } @@ -124,7 +121,7 @@ impl Client { } }; - let noise_stream = match upgrade_noise_initiator( + let noise_stream = match upgrade_noise_initiator_with_topology( stream, topology_ref, local_public_key, @@ -218,8 +215,8 @@ impl Client { let initial_connection_timeout = self.config.initial_connection_timeout; let topology_access_clone = self.topology_access.clone(); - let local_private_key = self.private_id_key.clone(); - let local_public_key = self.public_id_key.clone(); + let local_public_key = self.local_identity.public_key().to_bytes(); + let local_private_key = self.local_identity.private_key().to_bytes(); tokio::spawn(async move { // before executing the manager, wait for what was specified, if anything diff --git a/common/client-libs/mixnet-client/src/forwarder.rs b/common/client-libs/mixnet-client/src/forwarder.rs index 85c1f42a11..6a1628d18c 100644 --- a/common/client-libs/mixnet-client/src/forwarder.rs +++ b/common/client-libs/mixnet-client/src/forwarder.rs @@ -8,6 +8,7 @@ use log::*; use nym_client_core::client::topology_control::accessor::TopologyAccessor; use nym_crypto::asymmetric::encryption; use nym_sphinx::forwarding::packet::MixPacket; +use std::sync::Arc; use std::time::Duration; pub type MixForwardingSender = mpsc::UnboundedSender; @@ -29,7 +30,7 @@ impl PacketForwarder { maximum_connection_buffer_size: usize, use_legacy_version: bool, topology_access: TopologyAccessor, - sphinx_key: &encryption::KeyPair, + local_identity: Arc, shutdown: nym_task::TaskClient, ) -> (PacketForwarder, MixForwardingSender) { let client_config = Config::new( @@ -44,7 +45,7 @@ impl PacketForwarder { ( PacketForwarder { - mixnet_client: Client::new(client_config, topology_access, sphinx_key), + mixnet_client: Client::new(client_config, topology_access, local_identity), packet_receiver, shutdown, }, diff --git a/common/nymnoise/src/lib.rs b/common/nymnoise/src/lib.rs index ced0453e07..356f3da059 100644 --- a/common/nymnoise/src/lib.rs +++ b/common/nymnoise/src/lib.rs @@ -198,31 +198,14 @@ impl AsyncWrite for NoiseStream { pub async fn upgrade_noise_initiator( mut conn: TcpStream, - topology: &NymTopology, - local_public_key: &[u8], + local_public_key: Option<&[u8]>, local_private_key: &[u8], + remote_pub_key: &[u8], ) -> Result { trace!("Perform Noise Handshake, initiator side"); - //Get init material - let responder_addr = match conn.peer_addr() { - Ok(addr) => addr, - Err(err) => { - error!("Unable to extract peer address from connection - {err}"); - return Err(Error::Prereq(Prerequisite::RemotePublicKey).into()); - } - }; - let remote_pub_key = match topology.find_node_key_by_mix_host(responder_addr) { - Some(pub_key) => pub_key.to_bytes(), - None => { - error!( - "Cannot find public key for node with address {:?}", - responder_addr - ); - return Err(Error::Prereq(Prerequisite::RemotePublicKey).into()); - } - }; - let secret = [local_public_key, &remote_pub_key].concat(); + //In case the local key cannot be known by the remote party, e.g. in a client-gateway connection + let secret = [local_public_key.unwrap_or(&[]), remote_pub_key].concat(); let secret_hash = Sha256::digest(secret); let builder = Builder::new(NOISE_HS_PATTERN.parse().unwrap()); //This cannot fail, hardcoded pattern must be correct @@ -250,33 +233,50 @@ pub async fn upgrade_noise_initiator( Ok(NoiseStream::new(conn, noise)) } -pub async fn upgrade_noise_responder( - mut conn: TcpStream, +pub async fn upgrade_noise_initiator_with_topology( + conn: TcpStream, topology: &NymTopology, local_public_key: &[u8], local_private_key: &[u8], ) -> Result { - trace!("Perform Noise Handshake, responder side"); - //Get init material - let initiator_addr = match conn.peer_addr() { + let responder_addr = match conn.peer_addr() { Ok(addr) => addr, Err(err) => { error!("Unable to extract peer address from connection - {err}"); return Err(Error::Prereq(Prerequisite::RemotePublicKey).into()); } }; - let remote_pub_key = match topology.find_node_key_by_mix_host(initiator_addr) { + let remote_pub_key = match topology.find_node_key_by_mix_host(responder_addr) { Some(pub_key) => pub_key.to_bytes(), None => { error!( "Cannot find public key for node with address {:?}", - initiator_addr + responder_addr ); return Err(Error::Prereq(Prerequisite::RemotePublicKey).into()); } }; - let secret = [&remote_pub_key, local_public_key].concat(); + + upgrade_noise_initiator( + conn, + Some(local_public_key), + local_private_key, + &remote_pub_key, + ) + .await +} + +pub async fn upgrade_noise_responder( + mut conn: TcpStream, + local_public_key: &[u8], + local_private_key: &[u8], + remote_pub_key: Option<&[u8]>, +) -> Result { + trace!("Perform Noise Handshake, responder side"); + + //If the remote_key cannot be kwnown, e.g. in a client-gateway connection + let secret = [&remote_pub_key.unwrap_or(&[]), local_public_key].concat(); let secret_hash = Sha256::digest(secret); let builder = Builder::new(NOISE_HS_PATTERN.parse().unwrap()); //This cannot fail, hardcoded pattern must be correct @@ -302,6 +302,40 @@ pub async fn upgrade_noise_responder( Ok(NoiseStream::new(conn, noise)) } +pub async fn upgrade_noise_responder_with_topology( + conn: TcpStream, + topology: &NymTopology, + local_public_key: &[u8], + local_private_key: &[u8], +) -> Result { + //Get init material + let initiator_addr = match conn.peer_addr() { + Ok(addr) => addr, + Err(err) => { + error!("Unable to extract peer address from connection - {err}"); + return Err(Error::Prereq(Prerequisite::RemotePublicKey).into()); + } + }; + let remote_pub_key = match topology.find_node_key_by_mix_host(initiator_addr) { + Some(pub_key) => pub_key.to_bytes(), + None => { + error!( + "Cannot find public key for node with address {:?}", + initiator_addr + ); + return Err(Error::Prereq(Prerequisite::RemotePublicKey).into()); + } + }; + + upgrade_noise_responder( + conn, + local_public_key, + local_private_key, + Some(&remote_pub_key), + ) + .await +} + /// Hyper-basic stream transport receiver. 16-bit BE size followed by payload. async fn recv(stream: &mut TcpStream) -> io::Result> { let mut msg_len_buf = [0u8; HEADER_SIZE]; diff --git a/gateway/src/node/client_handling/websocket/listener.rs b/gateway/src/node/client_handling/websocket/listener.rs index 8811105b2e..2cbcae2579 100644 --- a/gateway/src/node/client_handling/websocket/listener.rs +++ b/gateway/src/node/client_handling/websocket/listener.rs @@ -6,8 +6,9 @@ use crate::node::client_handling::websocket::connection_handler::coconut::Coconu use crate::node::client_handling::websocket::connection_handler::FreshHandler; use crate::node::storage::Storage; use log::*; -use nym_crypto::asymmetric::identity; +use nym_crypto::asymmetric::{encryption, identity}; use nym_mixnet_client::forwarder::MixForwardingSender; +use nym_noise::upgrade_noise_responder; use rand::rngs::OsRng; use std::net::SocketAddr; use std::process; @@ -17,6 +18,7 @@ use tokio::task::JoinHandle; pub(crate) struct Listener { address: SocketAddr, local_identity: Arc, + local_sphinx: Arc, only_coconut_credentials: bool, pub(crate) coconut_verifier: Arc, } @@ -25,12 +27,14 @@ impl Listener { pub(crate) fn new( address: SocketAddr, local_identity: Arc, + local_sphinx: Arc, only_coconut_credentials: bool, coconut_verifier: Arc, ) -> Self { Listener { address, local_identity, + local_sphinx, only_coconut_credentials, coconut_verifier, } @@ -58,34 +62,49 @@ impl Listener { while !shutdown.is_shutdown() { tokio::select! { - biased; - _ = shutdown.recv() => { - log::trace!("client_handling::Listener: received shutdown"); - } - connection = tcp_listener.accept() => { - match connection { - Ok((socket, remote_addr)) => { - trace!("received a socket connection from {remote_addr}"); - // TODO: I think we *REALLY* need a mechanism for having a maximum number of connected - // clients or spawned tokio tasks -> perhaps a worker system? - let handle = FreshHandler::new( - OsRng, - socket, - self.only_coconut_credentials, - outbound_mix_sender.clone(), - Arc::clone(&self.local_identity), - storage.clone(), - active_clients_store.clone(), - Arc::clone(&self.coconut_verifier), - ); - let shutdown = shutdown.clone(); - tokio::spawn(async move { handle.start_handling(shutdown).await }); - } - Err(err) => warn!("failed to get client: {err}"), - } - } + biased; + _ = shutdown.recv() => { + log::trace!("client_handling::Listener: received shutdown"); + } + connection = tcp_listener.accept() => { + match connection { + Ok((socket, remote_addr)) => { + trace!("received a socket connection from {remote_addr}"); + // TODO: I think we *REALLY* need a mechanism for having a maximum number of connected + // clients or spawned tokio tasks -> perhaps a worker system? + // let noise_stream = match upgrade_noise_responder( + // socket, + // &self.local_sphinx.public_key().to_bytes(), + // &self.local_sphinx.private_key().to_bytes(), + // None, //connection from client, no remote pub key + // ) + // .await + // { + // Ok(noise_stream) => noise_stream, + // Err(err) => { + // error!("Failed to perform Noise handshake with {remote_addr} - {err}"); + // return; + // } + // }; + let handle = FreshHandler::new( + OsRng, + socket, + // noise_stream, + self.only_coconut_credentials, + outbound_mix_sender.clone(), + Arc::clone(&self.local_identity), + storage.clone(), + active_clients_store.clone(), + Arc::clone(&self.coconut_verifier), + ); + let shutdown = shutdown.clone(); + tokio::spawn(async move { handle.start_handling(shutdown).await }); + } + Err(err) => warn!("failed to get client: {err}"), + } + } - } + } } } diff --git a/gateway/src/node/mixnet_handling/receiver/connection_handler.rs b/gateway/src/node/mixnet_handling/receiver/connection_handler.rs index 2d4e37c71f..cda72fa990 100644 --- a/gateway/src/node/mixnet_handling/receiver/connection_handler.rs +++ b/gateway/src/node/mixnet_handling/receiver/connection_handler.rs @@ -3,7 +3,6 @@ use crate::node::client_handling::active_clients::ActiveClientsStore; use crate::node::client_handling::websocket::message_receiver::MixMessageSender; -use crate::node::encryption::{PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE}; use crate::node::mixnet_handling::receiver::packet_processing::PacketProcessor; use crate::node::storage::error::StorageError; use crate::node::storage::Storage; @@ -13,7 +12,7 @@ use nym_client_core::client::topology_control::accessor::TopologyAccessor; use nym_crypto::asymmetric::encryption; use nym_mixnet_client::forwarder::MixForwardingSender; use nym_mixnode_common::packet_processor::processor::ProcessedFinalHop; -use nym_noise::upgrade_noise_responder; +use nym_noise::upgrade_noise_responder_with_topology; use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::framing::codec::NymCodec; use nym_sphinx::framing::packet::FramedNymPacket; @@ -21,6 +20,7 @@ use nym_sphinx::DestinationAddressBytes; use nym_task::TaskClient; use std::collections::HashMap; use std::net::SocketAddr; +use std::sync::Arc; use tokio::net::TcpStream; use tokio_util::codec::Framed; @@ -36,8 +36,7 @@ pub(crate) struct ConnectionHandler { storage: St, ack_sender: MixForwardingSender, topology_access: TopologyAccessor, - public_identity_key: [u8; PUBLIC_KEY_SIZE], - private_identity_key: [u8; PRIVATE_KEY_SIZE], + local_identity: Arc, } impl Clone for ConnectionHandler { @@ -57,8 +56,7 @@ impl Clone for ConnectionHandler { storage: self.storage.clone(), ack_sender: self.ack_sender.clone(), topology_access: self.topology_access.clone(), - public_identity_key: self.public_identity_key.clone(), - private_identity_key: self.private_identity_key.clone(), + local_identity: self.local_identity.clone(), } } } @@ -70,7 +68,7 @@ impl ConnectionHandler { ack_sender: MixForwardingSender, active_clients_store: ActiveClientsStore, topology_access: TopologyAccessor, - identity_key: &encryption::KeyPair, + local_identity: Arc, ) -> Self { ConnectionHandler { packet_processor, @@ -79,8 +77,7 @@ impl ConnectionHandler { active_clients_store, ack_sender, topology_access, - public_identity_key: identity_key.public_key().to_bytes(), - private_identity_key: identity_key.private_key().to_bytes(), + local_identity, } } @@ -208,11 +205,11 @@ impl ConnectionHandler { } }; - let noise_stream = match upgrade_noise_responder( + let noise_stream = match upgrade_noise_responder_with_topology( conn, topology_ref, - &self.public_identity_key, - &self.private_identity_key, + &self.local_identity.public_key().to_bytes(), + &self.local_identity.private_key().to_bytes(), ) .await { diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index e524411441..623a2c08ba 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -146,7 +146,7 @@ impl Gateway { ack_sender, active_clients_store, topology_access, - &self.sphinx_keypair, + Arc::clone(&self.sphinx_keypair), ); let listening_address = SocketAddr::new( @@ -176,6 +176,7 @@ impl Gateway { websocket::Listener::new( listening_address, Arc::clone(&self.identity_keypair), + Arc::clone(&self.sphinx_keypair), self.config.gateway.only_coconut_credentials, coconut_verifier, ) @@ -201,7 +202,7 @@ impl Gateway { self.config.debug.maximum_connection_buffer_size, self.config.debug.use_legacy_framed_packet_version, topology_access, - &self.sphinx_keypair, + Arc::clone(&self.sphinx_keypair), shutdown, ); diff --git a/mixnode/src/node/listener/connection_handler/mod.rs b/mixnode/src/node/listener/connection_handler/mod.rs index 8e933d889d..5540a341ee 100644 --- a/mixnode/src/node/listener/connection_handler/mod.rs +++ b/mixnode/src/node/listener/connection_handler/mod.rs @@ -1,7 +1,6 @@ // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::node::encryption::{PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE}; use crate::node::listener::connection_handler::packet_processing::{ MixProcessingResult, PacketProcessor, }; @@ -11,12 +10,13 @@ use futures::StreamExt; use nym_client_core::client::topology_control::accessor::TopologyAccessor; use nym_crypto::asymmetric::encryption; use nym_mixnode_common::measure; -use nym_noise::upgrade_noise_responder; +use nym_noise::upgrade_noise_responder_with_topology; use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::framing::codec::NymCodec; use nym_sphinx::framing::packet::FramedNymPacket; use nym_sphinx::Delay as SphinxDelay; use std::net::SocketAddr; +use std::sync::Arc; use tokio::net::TcpStream; use tokio::time::Instant; use tokio_util::codec::Framed; @@ -30,8 +30,7 @@ pub(crate) struct ConnectionHandler { packet_processor: PacketProcessor, delay_forwarding_channel: PacketDelayForwardSender, topology_access: TopologyAccessor, - private_identity_key: [u8; PRIVATE_KEY_SIZE], - public_identity_key: [u8; PUBLIC_KEY_SIZE], + local_identity: Arc, } impl ConnectionHandler { @@ -39,14 +38,13 @@ impl ConnectionHandler { packet_processor: PacketProcessor, delay_forwarding_channel: PacketDelayForwardSender, topology_access: TopologyAccessor, - identity_key: &encryption::KeyPair, + local_identity: Arc, ) -> Self { ConnectionHandler { packet_processor, delay_forwarding_channel, topology_access, - public_identity_key: identity_key.public_key().to_bytes(), - private_identity_key: identity_key.private_key().to_bytes(), + local_identity, } } @@ -109,11 +107,11 @@ impl ConnectionHandler { } }; - let noise_stream = match upgrade_noise_responder( + let noise_stream = match upgrade_noise_responder_with_topology( conn, topology_ref, - &self.public_identity_key, - &self.private_identity_key, + &self.local_identity.public_key().to_bytes(), + &self.local_identity.private_key().to_bytes(), ) .await { diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 7ffb9e5886..3a21645dfb 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -164,7 +164,7 @@ impl MixNode { packet_processor, delay_forwarding_channel, topology_access, - &self.sphinx_keypair, + Arc::clone(&self.sphinx_keypair), ); let listening_address = SocketAddr::new( @@ -192,7 +192,11 @@ impl MixNode { ); let mut packet_forwarder = DelayForwarder::new( - nym_mixnet_client::Client::new(client_config, topology_access, &self.sphinx_keypair), + nym_mixnet_client::Client::new( + client_config, + topology_access, + Arc::clone(&self.sphinx_keypair), + ), node_stats_update_sender, shutdown, );