feat(auth,client-registry): add Noise IK client authentication with managed client registry and per-client ACL controls

This commit is contained in:
2026-03-29 17:04:27 +00:00
parent 187a69028b
commit 01a0d8b9f4
20 changed files with 1930 additions and 897 deletions

View File

@@ -19,6 +19,10 @@ use crate::quic_transport;
pub struct ClientConfig {
pub server_url: String,
pub server_public_key: String,
/// Client's Noise IK static private key (base64) — required for authentication.
pub client_private_key: String,
/// Client's Noise IK static public key (base64) — for reference/display.
pub client_public_key: String,
pub dns: Option<Vec<String>>,
pub mtu: Option<u16>,
pub keepalive_interval_secs: Option<u64>,
@@ -104,11 +108,15 @@ impl VpnClient {
let connected_since = self.connected_since.clone();
let link_health = self.link_health.clone();
// Decode server public key
// Decode keys
let server_pub_key = base64::Engine::decode(
&base64::engine::general_purpose::STANDARD,
&config.server_public_key,
)?;
let client_priv_key = base64::Engine::decode(
&base64::engine::general_purpose::STANDARD,
&config.client_private_key,
)?;
// Create transport based on configuration
let (mut sink, mut stream): (Box<dyn TransportSink>, Box<dyn TransportStream>) = {
@@ -171,12 +179,12 @@ impl VpnClient {
}
};
// Noise NK handshake (client side = initiator)
// Noise IK handshake (client side = initiator, presents static key)
*state.write().await = ClientState::Handshaking;
let mut initiator = crypto::create_initiator(&server_pub_key)?;
let mut initiator = crypto::create_initiator(&client_priv_key, &server_pub_key)?;
let mut buf = vec![0u8; 65535];
// -> e, es
// -> e, es, s, ss
let len = initiator.write_message(&[], &mut buf)?;
let init_frame = Frame {
packet_type: PacketType::HandshakeInit,
@@ -186,7 +194,7 @@ impl VpnClient {
<FrameCodec as tokio_util::codec::Encoder<Frame>>::encode(&mut FrameCodec, init_frame, &mut frame_bytes)?;
sink.send_reliable(frame_bytes.to_vec()).await?;
// <- e, ee
// <- e, ee, se
let resp_msg = match stream.recv_reliable().await? {
Some(data) => data,
None => anyhow::bail!("Connection closed during handshake"),