Skip to content

Commit

Permalink
Feature/wg gateway data on handshake init (#4057)
Browse files Browse the repository at this point in the history
* Return wg gateway data on handshake init

* Add wg register to gateway api client

* Remove socket addr and return just wg port
  • Loading branch information
neacsu authored Oct 27, 2023
1 parent 6c43602 commit ffb4457
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 33 deletions.
35 changes: 10 additions & 25 deletions common/wireguard-types/src/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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))]
Expand All @@ -67,23 +72,14 @@ 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,
}

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())
Expand All @@ -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()),
}
}
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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())
Expand Down
7 changes: 6 additions & 1 deletion gateway/src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions nym-node/nym-node-requests/src/api/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -40,6 +41,17 @@ pub trait NymNodeApiClientExt: ApiClient {
)
.await
}

async fn post_gateway_register_client(
&self,
client_message: &ClientMessage,
) -> Result<ClientRegistrationResponse, NymNodeApiClientError> {
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))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ impl WireguardAppState {
dh_keypair: Arc<encryption::KeyPair>,
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
) -> Self {
WireguardAppState {
inner: Some(WireguardAppStateInner {
dh_keypair,
client_registry,
registration_in_progress,
binding_port,
}),
}
}
Expand Down Expand Up @@ -74,6 +76,7 @@ pub(crate) struct WireguardAppStateInner {
dh_keypair: Arc<encryption::KeyPair>,
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
}

pub(crate) fn routes<S>(initial_state: WireguardAppState) -> Router<S> {
Expand Down Expand Up @@ -139,6 +142,7 @@ mod test {
client_registry: Arc::clone(&client_registry),
dh_keypair: Arc::new(gateway_key_pair),
registration_in_progress: Arc::clone(&registration_in_progress),
binding_port: 8080,
}),
};

Expand Down Expand Up @@ -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()),
});

Expand Down

0 comments on commit ffb4457

Please sign in to comment.