diff --git a/common/client-libs/mixnet-client/src/client.rs b/common/client-libs/mixnet-client/src/client.rs index b95810d121f..6d53deccd50 100644 --- a/common/client-libs/mixnet-client/src/client.rs +++ b/common/client-libs/mixnet-client/src/client.rs @@ -135,7 +135,6 @@ impl Client { Default::default(), &topology, epoch_id, - local_identity.public_key(), local_identity.private_key(), ) .await diff --git a/common/nymnoise/src/lib.rs b/common/nymnoise/src/lib.rs index ded97850e71..c5b28262a3f 100644 --- a/common/nymnoise/src/lib.rs +++ b/common/nymnoise/src/lib.rs @@ -15,10 +15,11 @@ pub mod connection; pub mod error; pub mod stream; +const NOISE_PSK_PREFIX: &[u8] = b"NYMTECH_NOISE_dQw4w9WgXcQ"; + pub async fn upgrade_noise_initiator( conn: TcpStream, pattern: NoisePattern, - local_public_key: Option<&encryption::PublicKey>, local_private_key: &encryption::PrivateKey, remote_pub_key: &encryption::PublicKey, epoch: u32, @@ -27,9 +28,7 @@ pub async fn upgrade_noise_initiator( //In case the local key cannot be known by the remote party, e.g. in a client-gateway connection let secret = [ - local_public_key - .map(|k| k.to_bytes().to_vec()) - .unwrap_or_default(), + NOISE_PSK_PREFIX.to_vec(), remote_pub_key.to_bytes().to_vec(), epoch.to_be_bytes().to_vec(), ] @@ -52,7 +51,6 @@ pub async fn upgrade_noise_initiator_with_topology( pattern: NoisePattern, topology: &NymTopology, epoch: u32, - local_public_key: &encryption::PublicKey, local_private_key: &encryption::PrivateKey, ) -> Result { //Get init material @@ -61,7 +59,7 @@ pub async fn upgrade_noise_initiator_with_topology( Error::Prereq(Prerequisite::RemotePublicKey) })?; - let remote_pub_key = match topology.find_node_key_by_mix_host(responder_addr) { + let remote_pub_key = match topology.find_node_key_by_mix_host(responder_addr, true) { Ok(Some(key)) => encryption::PublicKey::from_base58_string(key)?, Ok(None) => { warn!( @@ -79,15 +77,7 @@ pub async fn upgrade_noise_initiator_with_topology( } }; - upgrade_noise_initiator( - conn, - pattern, - Some(local_public_key), - local_private_key, - &remote_pub_key, - epoch, - ) - .await + upgrade_noise_initiator(conn, pattern, local_private_key, &remote_pub_key, epoch).await } pub async fn upgrade_noise_responder( @@ -95,16 +85,13 @@ pub async fn upgrade_noise_responder( pattern: NoisePattern, local_public_key: &encryption::PublicKey, local_private_key: &encryption::PrivateKey, - remote_pub_key: Option<&encryption::PublicKey>, epoch: u32, ) -> 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 - .map(|k| k.to_bytes().to_vec()) - .unwrap_or_default(), + NOISE_PSK_PREFIX.to_vec(), local_public_key.to_bytes().to_vec(), epoch.to_be_bytes().to_vec(), ] @@ -139,31 +126,26 @@ pub async fn upgrade_noise_responder_with_topology( }; //SW : for private gateway, we could try to perform the handshake without that key? - let remote_pub_key = match topology.find_node_key_by_mix_host(initiator_addr) { - Ok(Some(key)) => encryption::PublicKey::from_base58_string(key)?, + match topology.find_node_key_by_mix_host(initiator_addr, false) { + Ok(Some(_)) => { + //Existing node supporting Noise + upgrade_noise_responder(conn, pattern, local_public_key, local_private_key, epoch).await + } Ok(None) => { + //Existing node not supporting Noise yet warn!( "{:?} can't speak Noise yet, falling back to TCP", initiator_addr ); - return Ok(Connection::Tcp(conn)); + Ok(Connection::Tcp(conn)) } Err(_) => { + //Non existing node error!( "Cannot find public key for node with address {:?}", initiator_addr ); //Do we still pursue a TCP connection with that node or not? - return Err(Error::Prereq(Prerequisite::RemotePublicKey).into()); + Err(Error::Prereq(Prerequisite::RemotePublicKey).into()) } - }; - - upgrade_noise_responder( - conn, - pattern, - local_public_key, - local_private_key, - Some(&remote_pub_key), - epoch, - ) - .await + } } diff --git a/common/nymsphinx/src/receiver.rs b/common/nymsphinx/src/receiver.rs index 742de7c85e1..65c791b1f93 100644 --- a/common/nymsphinx/src/receiver.rs +++ b/common/nymsphinx/src/receiver.rs @@ -237,7 +237,7 @@ mod message_receiver { mix_id: 123, owner: "foomp1".to_string(), host: "10.20.30.40".parse().unwrap(), - mix_host: "10.20.30.40:1789".parse().unwrap(), + mix_hosts: vec!["10.20.30.40:1789".parse().unwrap()], identity_key: identity::PublicKey::from_base58_string( "3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7", ) @@ -257,7 +257,7 @@ mod message_receiver { mix_id: 234, owner: "foomp2".to_string(), host: "11.21.31.41".parse().unwrap(), - mix_host: "11.21.31.41:1789".parse().unwrap(), + mix_hosts: vec!["11.21.31.41:1789".parse().unwrap()], identity_key: identity::PublicKey::from_base58_string( "D6YaMzLSY7mANtSQRKXsmMZpqgqiVkeiagKM4V4oFPFr", ) @@ -277,7 +277,7 @@ mod message_receiver { mix_id: 456, owner: "foomp3".to_string(), host: "12.22.32.42".parse().unwrap(), - mix_host: "12.22.32.42:1789".parse().unwrap(), + mix_hosts: vec!["12.22.32.42:1789".parse().unwrap()], identity_key: identity::PublicKey::from_base58_string( "GkWDysw4AjESv1KiAiVn7JzzCMJeksxNSXVfr1PpX8wD", ) @@ -297,7 +297,7 @@ mod message_receiver { vec![gateway::Node { owner: "foomp4".to_string(), host: "1.2.3.4".parse().unwrap(), - mix_host: "1.2.3.4:1789".parse().unwrap(), + mix_hosts: vec!["1.2.3.4:1789".parse().unwrap()], clients_ws_port: 9000, clients_wss_port: None, identity_key: identity::PublicKey::from_base58_string( diff --git a/common/topology/src/gateway.rs b/common/topology/src/gateway.rs index 61b4fefc41d..dc68e412e0d 100644 --- a/common/topology/src/gateway.rs +++ b/common/topology/src/gateway.rs @@ -46,9 +46,9 @@ pub enum GatewayConversionError { pub struct Node { pub owner: String, pub host: NetworkAddress, - // we're keeping this as separate resolved field since we do not want to be resolving the potential - // hostname every time we want to construct a path via this node - pub mix_host: SocketAddr, + // we're keeping all resolved IPs as a separate field since we do not want to be resolving the potential + // hostname every time we want to construct a path via this node. When we need one, we default to the first one + pub mix_hosts: Vec, // #[serde(alias = "clients_port")] pub clients_ws_port: u16, @@ -66,7 +66,7 @@ impl std::fmt::Debug for Node { f.debug_struct("gateway::Node") .field("host", &self.host) .field("owner", &self.owner) - .field("mix_host", &self.mix_host) + .field("mix_hosts", &self.mix_hosts) .field("clients_ws_port", &self.clients_ws_port) .field("clients_wss_port", &self.clients_wss_port) .field("identity_key", &self.identity_key.to_base58_string()) @@ -88,13 +88,12 @@ impl Node { pub fn extract_mix_host( host: &NetworkAddress, mix_port: u16, - ) -> Result { - Ok(host.to_socket_addrs(mix_port).map_err(|err| { - GatewayConversionError::InvalidAddress { + ) -> Result, GatewayConversionError> { + host.to_socket_addrs(mix_port) + .map_err(|err| GatewayConversionError::InvalidAddress { value: host.to_string(), source: err, - } - })?[0]) + }) } pub fn identity(&self) -> &NodeIdentity { @@ -135,7 +134,7 @@ impl filter::Versioned for Node { impl<'a> From<&'a Node> for SphinxNode { fn from(node: &'a Node) -> Self { - let node_address_bytes = NymNodeRoutingAddress::from(node.mix_host) + let node_address_bytes = NymNodeRoutingAddress::from(node.mix_hosts[0]) .try_into() .unwrap(); @@ -151,12 +150,12 @@ impl<'a> TryFrom<&'a GatewayBond> for Node { // try to completely resolve the host in the mix situation to avoid doing it every // single time we want to construct a path - let mix_host = Self::extract_mix_host(&host, bond.gateway.mix_port)?; + let mix_hosts = Self::extract_mix_host(&host, bond.gateway.mix_port)?; Ok(Node { owner: bond.owner.as_str().to_owned(), host, - mix_host, + mix_hosts, clients_ws_port: bond.gateway.clients_port, clients_wss_port: None, identity_key: identity::PublicKey::from_base58_string(&bond.gateway.identity_key)?, @@ -196,12 +195,15 @@ impl<'a> TryFrom<&'a DescribedGateway> for Node { // get ip from the self-reported values so we wouldn't need to do any hostname resolution // (which doesn't really work in wasm) - let mix_host = SocketAddr::new(ips[0], value.bond.gateway.mix_port); + let mix_hosts = ips + .iter() + .map(|ip| SocketAddr::new(*ip, value.bond.gateway.mix_port)) + .collect(); Ok(Node { owner: value.bond.owner.as_str().to_owned(), host, - mix_host, + mix_hosts, clients_ws_port: self_described.mixnet_websockets.unwrap().ws_port, //SW gateway have that field clients_wss_port: self_described.mixnet_websockets.unwrap().wss_port, //SW gateway have that field identity_key: identity::PublicKey::from_base58_string( diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 39a54393c3c..ebc002a8776 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -189,27 +189,43 @@ impl NymTopology { pub fn find_node_key_by_mix_host( &self, mix_host: SocketAddr, + check_port: bool, ) -> Result, NymTopologyError> { for node in self.described_nodes.iter() { - let sphinx_key = match node { - DescribedNymNode::Gateway(g) => &g.bond.gateway.sphinx_key, - DescribedNymNode::Mixnode(m) => &m.bond.mix_node.sphinx_key, + let (sphinx_key, socket_addresses, description) = match node { + DescribedNymNode::Gateway(g) => { + let sphinx_key = &g.bond.gateway.sphinx_key; + let gateway_node: Option = (&g.bond).try_into().ok(); + let mix_hosts = gateway_node.map(|node| node.mix_hosts); + let description = &g.self_described; + (sphinx_key, mix_hosts, description) + } + DescribedNymNode::Mixnode(m) => { + let sphinx_key = &m.bond.mix_node.sphinx_key; + let mix_node: Option = (&m.bond).try_into().ok(); + let mix_hosts = mix_node.map(|node| node.mix_hosts); + let description = &m.self_described; + (sphinx_key, mix_hosts, description) + } }; - if let Some(description) = match node { - DescribedNymNode::Gateway(g) => &g.self_described, - DescribedNymNode::Mixnode(m) => &m.self_described, - } { - if description - .host_information - .ip_address - .contains(&mix_host.ip()) - { + if let Some(sock_addr) = socket_addresses { + let existing_node = if check_port { + //Initiator side, we know the port should be correct as well + sock_addr.contains(&mix_host) + } else { + //responder side, we don't know the port. + //SW This can lead to some troubles if two nodes shares the same IP and one support Noise but not the other. This in only for the progressive update though + let ip_addresses = sock_addr.iter().map(|addr| addr.ip()).collect::>(); + ip_addresses.contains(&mix_host.ip()) + }; + if existing_node { //we have our node - if description.noise_information.supported { - return Ok(Some(sphinx_key.to_string())); - } else { - return Ok(None); + if let Some(d) = description { + if d.noise_information.supported { + return Ok(Some(sphinx_key.to_string())); + } } + return Ok(None); } } } @@ -539,7 +555,7 @@ mod converting_mixes_to_vec { mix_id: 42, owner: "N/A".to_string(), host: "3.3.3.3".parse().unwrap(), - mix_host: "3.3.3.3:1789".parse().unwrap(), + mix_hosts: vec!["3.3.3.3:1789".parse().unwrap()], identity_key: identity::PublicKey::from_base58_string( "3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7", ) diff --git a/common/topology/src/mix.rs b/common/topology/src/mix.rs index 0481d5bc16d..ce9d59d0a15 100644 --- a/common/topology/src/mix.rs +++ b/common/topology/src/mix.rs @@ -34,9 +34,9 @@ pub struct Node { pub mix_id: MixId, pub owner: String, pub host: NetworkAddress, - // we're keeping this as separate resolved field since we do not want to be resolving the potential - // hostname every time we want to construct a path via this node - pub mix_host: SocketAddr, + // we're keeping all resolved IPs as a separate field since we do not want to be resolving the potential + // hostname every time we want to construct a path via this node. When we need one, we default to the first one + pub mix_hosts: Vec, pub identity_key: identity::PublicKey, pub sphinx_key: encryption::PublicKey, // TODO: or nymsphinx::PublicKey? both are x25519 pub layer: Layer, @@ -49,7 +49,7 @@ impl std::fmt::Debug for Node { .field("mix_id", &self.mix_id) .field("owner", &self.owner) .field("host", &self.host) - .field("mix_host", &self.mix_host) + .field("mix_hosts", &self.mix_hosts) .field("identity_key", &self.identity_key.to_base58_string()) .field("sphinx_key", &self.sphinx_key.to_base58_string()) .field("layer", &self.layer) @@ -70,13 +70,12 @@ impl Node { pub fn extract_mix_host( host: &NetworkAddress, mix_port: u16, - ) -> Result { - Ok(host.to_socket_addrs(mix_port).map_err(|err| { - MixnodeConversionError::InvalidAddress { + ) -> Result, MixnodeConversionError> { + host.to_socket_addrs(mix_port) + .map_err(|err| MixnodeConversionError::InvalidAddress { value: host.to_string(), source: err, - } - })?[0]) + }) } } @@ -89,7 +88,7 @@ impl filter::Versioned for Node { impl<'a> From<&'a Node> for SphinxNode { fn from(node: &'a Node) -> Self { - let node_address_bytes = NymNodeRoutingAddress::from(node.mix_host) + let node_address_bytes = NymNodeRoutingAddress::from(node.mix_hosts[0]) .try_into() .unwrap(); @@ -105,13 +104,13 @@ impl<'a> TryFrom<&'a MixNodeBond> for Node { // try to completely resolve the host in the mix situation to avoid doing it every // single time we want to construct a path - let mix_host = Self::extract_mix_host(&host, bond.mix_node.mix_port)?; + let mix_hosts = Self::extract_mix_host(&host, bond.mix_node.mix_port)?; Ok(Node { mix_id: bond.mix_id, owner: bond.owner.as_str().to_owned(), host, - mix_host, + mix_hosts, identity_key: identity::PublicKey::from_base58_string(&bond.mix_node.identity_key)?, sphinx_key: encryption::PublicKey::from_base58_string(&bond.mix_node.sphinx_key)?, layer: bond.layer, diff --git a/common/topology/src/serde.rs b/common/topology/src/serde.rs index 84770a48683..2e0db140f17 100644 --- a/common/topology/src/serde.rs +++ b/common/topology/src/serde.rs @@ -135,13 +135,13 @@ impl TryFrom for mix::Node { // try to completely resolve the host in the mix situation to avoid doing it every // single time we want to construct a path - let mix_host = mix::Node::extract_mix_host(&host, mix_port)?; + let mix_hosts = mix::Node::extract_mix_host(&host, mix_port)?; Ok(mix::Node { mix_id: value.mix_id, owner: value.owner, host, - mix_host, + mix_hosts, identity_key: identity::PublicKey::from_base58_string(&value.identity_key) .map_err(MixnodeConversionError::from)?, sphinx_key: encryption::PublicKey::from_base58_string(&value.sphinx_key) @@ -159,7 +159,7 @@ impl<'a> From<&'a mix::Node> for SerializableMixNode { mix_id: value.mix_id, owner: value.owner.clone(), host: value.host.to_string(), - mix_port: Some(value.mix_host.port()), + mix_port: Some(value.mix_hosts[0].port()), identity_key: value.identity_key.to_base58_string(), sphinx_key: value.sphinx_key.to_base58_string(), layer: value.layer.into(), @@ -182,7 +182,7 @@ pub struct SerializableGateway { // (thank you wasm) #[cfg_attr(feature = "wasm-serde-types", tsify(optional))] #[serde(alias = "explicit_ip")] - pub explicit_ip: Option, + pub explicit_ips: Option>, #[cfg_attr(feature = "wasm-serde-types", tsify(optional))] #[serde(alias = "mix_port")] @@ -221,8 +221,11 @@ impl TryFrom for gateway::Node { // try to completely resolve the host in the mix situation to avoid doing it every // single time we want to construct a path - let mix_host = if let Some(explicit_ip) = value.explicit_ip { - SocketAddr::new(explicit_ip, mix_port) + let mix_hosts = if let Some(explicit_ips) = value.explicit_ips { + explicit_ips + .iter() + .map(|explicit_ip| SocketAddr::new(*explicit_ip, mix_port)) + .collect() } else { gateway::Node::extract_mix_host(&host, mix_port)? }; @@ -230,7 +233,7 @@ impl TryFrom for gateway::Node { Ok(gateway::Node { owner: value.owner, host, - mix_host, + mix_hosts, clients_ws_port, clients_wss_port: value.clients_wss_port, identity_key: identity::PublicKey::from_base58_string(&value.identity_key) @@ -247,8 +250,8 @@ impl<'a> From<&'a gateway::Node> for SerializableGateway { SerializableGateway { owner: value.owner.clone(), host: value.host.to_string(), - explicit_ip: Some(value.mix_host.ip()), - mix_port: Some(value.mix_host.port()), + explicit_ips: Some(value.mix_hosts.iter().map(|addr| addr.ip()).collect()), + mix_port: Some(value.mix_hosts[0].port()), clients_ws_port: Some(value.clients_ws_port), clients_wss_port: value.clients_wss_port, identity_key: value.identity_key.to_base58_string(), diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index 2a6d50157e3..8ebf49b432f 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -5541,7 +5541,6 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.25.4", "winreg 0.50.0", ] @@ -6508,7 +6507,7 @@ dependencies = [ "thiserror", "tokio-stream", "url", - "webpki-roots 0.22.6", + "webpki-roots", ] [[package]] @@ -7478,7 +7477,6 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "rustls 0.21.7", "sha1", "thiserror", "url", @@ -7922,12 +7920,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - [[package]] name = "webview2-com" version = "0.19.1" diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 0c61b550420..5fc7e020bd2 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -4444,7 +4444,6 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", "winreg", ] @@ -6272,12 +6271,6 @@ dependencies = [ "system-deps 6.1.1", ] -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - [[package]] name = "webview2-com" version = "0.19.1" diff --git a/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs b/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs index 175a64f4cdc..5d8eb4e0a22 100644 --- a/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs +++ b/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs @@ -23,7 +23,7 @@ async fn main() { mix_id: 63, owner: "n1k52k5n45cqt5qpjh8tcwmgqm0wkt355yy0g5vu".to_string(), host: "172.105.92.48".parse().unwrap(), - mix_host: "172.105.92.48:1789".parse().unwrap(), + mix_hosts: vec!["172.105.92.48:1789".parse().unwrap()], identity_key: "GLdR2NRVZBiCoCbv4fNqt9wUJZAnNjGXHkx3TjVAUzrK" .parse() .unwrap(), @@ -40,7 +40,7 @@ async fn main() { mix_id: 23, owner: "n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47".to_string(), host: "178.79.143.65".parse().unwrap(), - mix_host: "178.79.143.65:1789".parse().unwrap(), + mix_hosts: vec!["178.79.143.65:1789".parse().unwrap()], identity_key: "4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz" .parse() .unwrap(), @@ -57,7 +57,7 @@ async fn main() { mix_id: 66, owner: "n1ae2pjd7q9p0dea65pqkvcm4x9s264v4fktpyru".to_string(), host: "139.162.247.97".parse().unwrap(), - mix_host: "139.162.247.97:1789".parse().unwrap(), + mix_hosts: vec!["139.162.247.97:1789".parse().unwrap()], identity_key: "66UngapebhJRni3Nj52EW1qcNsWYiuonjkWJzHFsmyYY" .parse() .unwrap(),