diff --git a/common/wireguard-types/src/registration.rs b/common/wireguard-types/src/registration.rs index c899be5d02..1da614aaa0 100644 --- a/common/wireguard-types/src/registration.rs +++ b/common/wireguard-types/src/registration.rs @@ -6,7 +6,6 @@ use crate::PeerPublicKey; use base64::{engine::general_purpose, Engine}; use dashmap::DashMap; use serde::{Deserialize, Serialize}; -use std::net::SocketAddr; use std::{fmt, ops::Deref, str::FromStr}; #[cfg(feature = "verify")] @@ -54,11 +53,17 @@ impl InitMessage { #[serde(tag = "type", rename_all = "camelCase")] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum ClientRegistrationResponse { - PendingRegistration { nonce: u64 }, - Registered { success: bool }, + PendingRegistration { + nonce: u64, + gateway_data: GatewayClient, + wg_port: u16, + }, + Registered { + success: bool, + }, } -/// Client that wants to register sends its PublicKey and SocketAddr bytes mac digest encrypted with a DH shared secret. +/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. /// Gateway/Nym node can then verify pub_key payload using the same process #[derive(Serialize, Deserialize, Debug, Clone)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] @@ -67,10 +72,6 @@ pub struct GatewayClient { #[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))] pub pub_key: PeerPublicKey, - /// Client's socket address - #[cfg_attr(feature = "openapi", schema(example = "1.2.3.4:51820", value_type = String))] - pub socket: SocketAddr, - /// Sha256 hmac on the data (alongside the prior nonce) #[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))] pub mac: ClientMac, @@ -78,12 +79,7 @@ pub struct GatewayClient { impl GatewayClient { #[cfg(feature = "verify")] - pub fn new( - local_secret: &PrivateKey, - remote_public: PublicKey, - socket_address: SocketAddr, - nonce: u64, - ) -> Self { + pub fn new(local_secret: &PrivateKey, remote_public: PublicKey, nonce: u64) -> Self { // convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek #[allow(clippy::expect_used)] let static_secret = boringtun::x25519::StaticSecret::try_from(local_secret.to_bytes()) @@ -100,13 +96,10 @@ impl GatewayClient { .expect("x25519 shared secret is always 32 bytes long"); mac.update(local_public.as_bytes()); - mac.update(socket_address.ip().to_string().as_bytes()); - mac.update(socket_address.port().to_string().as_bytes()); mac.update(&nonce.to_le_bytes()); GatewayClient { pub_key: PeerPublicKey::new(local_public), - socket: socket_address, mac: ClientMac(mac.finalize().into_bytes().to_vec()), } } @@ -128,8 +121,6 @@ impl GatewayClient { .expect("x25519 shared secret is always 32 bytes long"); mac.update(self.pub_key.as_bytes()); - mac.update(self.socket.ip().to_string().as_bytes()); - mac.update(self.socket.port().to_string().as_bytes()); mac.update(&nonce.to_le_bytes()); mac.verify_slice(&self.mac) @@ -142,10 +133,6 @@ impl GatewayClient { pub fn pub_key(&self) -> PeerPublicKey { self.pub_key } - - pub fn socket(&self) -> SocketAddr { - self.socket - } } // TODO: change the inner type into generic array of size HmacSha256::OutputSize @@ -217,13 +204,11 @@ mod tests { let gateway_key_pair = encryption::KeyPair::new(&mut rng); let client_key_pair = encryption::KeyPair::new(&mut rng); - let socket: SocketAddr = "1.2.3.4:5678".parse().unwrap(); let nonce = 1234567890; let client = GatewayClient::new( client_key_pair.private_key(), *gateway_key_pair.public_key(), - socket, nonce, ); assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok()) diff --git a/gateway/src/http/mod.rs b/gateway/src/http/mod.rs index a53a966584..d27b7873c6 100644 --- a/gateway/src/http/mod.rs +++ b/gateway/src/http/mod.rs @@ -224,7 +224,12 @@ impl<'a> HttpApiBuilder<'a> { } let wg_state = self.client_registry.map(|client_registry| { - WireguardAppState::new(self.sphinx_keypair, client_registry, Default::default()) + WireguardAppState::new( + self.sphinx_keypair, + client_registry, + Default::default(), + self.gateway_config.wireguard.bind_address.port(), + ) }); let router = nym_node::http::NymNodeRouter::new(config, wg_state); diff --git a/nym-node/nym-node-requests/src/api/client.rs b/nym-node/nym-node-requests/src/api/client.rs index 1967b6963e..6815b1a023 100644 --- a/nym-node/nym-node-requests/src/api/client.rs +++ b/nym-node/nym-node-requests/src/api/client.rs @@ -8,6 +8,7 @@ use crate::routes; use async_trait::async_trait; use http_api_client::{ApiClient, HttpClientError}; use nym_bin_common::build_information::BinaryBuildInformationOwned; +use nym_wireguard_types::{ClientMessage, ClientRegistrationResponse}; use crate::api::v1::health::models::NodeHealth; pub use http_api_client::Client; @@ -40,6 +41,17 @@ pub trait NymNodeApiClientExt: ApiClient { ) .await } + + async fn post_gateway_register_client( + &self, + client_message: &ClientMessage, + ) -> Result { + self.post_json_data_to( + routes::api::v1::gateway::client_interfaces::wireguard::client_absolute(), + client_message, + ) + .await + } } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] diff --git a/nym-node/src/http/router/api/v1/gateway/client_interfaces/wireguard/client_registry.rs b/nym-node/src/http/router/api/v1/gateway/client_interfaces/wireguard/client_registry.rs index f21a1e719f..850443e325 100644 --- a/nym-node/src/http/router/api/v1/gateway/client_interfaces/wireguard/client_registry.rs +++ b/nym-node/src/http/router/api/v1/gateway/client_interfaces/wireguard/client_registry.rs @@ -10,6 +10,7 @@ use crate::wireguard::error::WireguardError; use axum::extract::{Path, Query, State}; use axum::http::StatusCode; use axum::Json; +use nym_crypto::asymmetric::encryption::PublicKey; use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{ ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, Nonce, PeerPublicKey, }; @@ -87,8 +88,16 @@ pub(crate) async fn register_client( match payload { ClientMessage::Initial(init) => { + let remote_public = PublicKey::from_bytes(init.pub_key().as_bytes()) + .map_err(|_| RequestError::new_status(StatusCode::BAD_REQUEST))?; let nonce = process_init_message(init, state).await; - let response = ClientRegistrationResponse::PendingRegistration { nonce }; + let gateway_data = + GatewayClient::new(state.dh_keypair.private_key(), remote_public, nonce); + let response = ClientRegistrationResponse::PendingRegistration { + nonce, + gateway_data, + wg_port: state.binding_port, + }; Ok(output.to_response(response)) } ClientMessage::Final(finalize) => { diff --git a/nym-node/src/http/router/api/v1/gateway/client_interfaces/wireguard/mod.rs b/nym-node/src/http/router/api/v1/gateway/client_interfaces/wireguard/mod.rs index 8e29d8c0fb..37348be94e 100644 --- a/nym-node/src/http/router/api/v1/gateway/client_interfaces/wireguard/mod.rs +++ b/nym-node/src/http/router/api/v1/gateway/client_interfaces/wireguard/mod.rs @@ -25,12 +25,14 @@ impl WireguardAppState { dh_keypair: Arc, client_registry: Arc, registration_in_progress: Arc, + binding_port: u16, ) -> Self { WireguardAppState { inner: Some(WireguardAppStateInner { dh_keypair, client_registry, registration_in_progress, + binding_port, }), } } @@ -74,6 +76,7 @@ pub(crate) struct WireguardAppStateInner { dh_keypair: Arc, client_registry: Arc, registration_in_progress: Arc, + binding_port: u16, } pub(crate) fn routes(initial_state: WireguardAppState) -> Router { @@ -139,6 +142,7 @@ mod test { client_registry: Arc::clone(&client_registry), dh_keypair: Arc::new(gateway_key_pair), registration_in_progress: Arc::clone(®istration_in_progress), + binding_port: 8080, }), }; @@ -167,23 +171,26 @@ mod test { assert_eq!(response.status(), StatusCode::OK); assert!(!registration_in_progress.is_empty()); - let ClientRegistrationResponse::PendingRegistration { nonce } = - serde_json::from_slice(&hyper::body::to_bytes(response.into_body()).await.unwrap()) - .unwrap() + let ClientRegistrationResponse::PendingRegistration { + nonce, + gateway_data, + wg_port: 8080, + } = serde_json::from_slice(&hyper::body::to_bytes(response.into_body()).await.unwrap()) + .unwrap() else { panic!("invalid response") }; + assert!(gateway_data + .verify(client_key_pair.private_key(), nonce) + .is_ok()); let mut mac = HmacSha256::new_from_slice(client_dh.as_bytes()).unwrap(); mac.update(client_static_public.as_bytes()); - mac.update("127.0.0.1".as_bytes()); - mac.update("8080".as_bytes()); mac.update(&nonce.to_le_bytes()); let mac = mac.finalize().into_bytes(); let finalized_message = ClientMessage::Final(GatewayClient { pub_key: PeerPublicKey::new(client_static_public), - socket: "127.0.0.1:8080".parse().unwrap(), mac: ClientMac::new(mac.as_slice().to_vec()), });