Feature/wg gateway data on handshake init (#4057)
* Return wg gateway data on handshake init * Add wg register to gateway api client * Remove socket addr and return just wg port
This commit is contained in:
committed by
GitHub
parent
6c436024f7
commit
ffb4457427
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<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))]
|
||||
|
||||
+10
-1
@@ -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) => {
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
@@ -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()),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user