feat: Lewes Protocol with PSQv2 (#6491)
* merging georgio/lp-psqv2-integration * use authenicator on the responder's side * nym-lp crate compiling * moved the e2e test to nym-lp * move key generation to peer * moved principal generation * update KKTResponder * encapsulation key parsing * Adding concrete types within KKT exchange * initiator side of the full handshake * responder side of the handshake and full e2e test * fixed unit-tests within nym-kkt * LpSession cleanup * helpers for Transport * revamp of the transport traits and initial work on client-side transport * compiling nym-crypto * 'working' client-entry dvpn reg * Fix key conversion * Slightly reduce use of rand08 * reverted back to libcrux repo refs * intial telescoping reg * removing dead code * wip * moved data encryption into the state machine * restoring nym-lp tests * update lp api model * Add receiver index derivation * Add receiver index derivation * use derived receiver index * feat: add kem key generation to nodes * generate fresh x25519, mlkem768 and mceliece keys on config migration * add lp peer config * nym-node startup cleanup * removed dependency on pre-rand09 from nym-lp * re-expose LP information on the http API * fixed tests compilation * add peer config happy path tests * formatting * add more tests and fix bug * better docs * clippy and formatting issues * return error on mceliece within NestedSession * wasm fixes * removed legacy nym-vpn-lib-wasm * fixing wasm for real this time * additional fixes * add payload to kkt * make clippy happy * moved LP to nym-node crate * cargo fmt * integrate lpconfig payload * fix response size trait impl * Migrate receiver index * Change receiver index to u32 and regorganize crates * clippy * hopefully final wasm fixes * simple conversion method from semver to ciphersuite * updated nym-node config template * chore: remove duplicated code --------- Co-authored-by: Georgio Nicolas <me@georgio.xyz>
This commit is contained in:
committed by
GitHub
parent
e5c3f39a57
commit
f6bd511599
@@ -22,6 +22,7 @@ hex.workspace = true
|
||||
tracing.workspace = true
|
||||
pnet_packet.workspace = true
|
||||
rand.workspace = true
|
||||
rand09.workspace = true
|
||||
reqwest = { workspace = true, features = ["socks"] }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::common::nodes::TestedNodeLpDetails;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_ip_packet_requests::v8::response::{
|
||||
ControlResponse, DataResponse, InfoLevel, IpPacketResponse, IpPacketResponseData,
|
||||
};
|
||||
use nym_lp::peer::LpRemotePeer;
|
||||
use nym_sdk::{
|
||||
DebugConfig, NymApiTopologyProvider, NymApiTopologyProviderConfig, NymNetworkDetails,
|
||||
TopologyProvider, mixnet::ReconstructedMessage,
|
||||
@@ -15,13 +12,6 @@ use nym_topology::NymTopology;
|
||||
use tracing::*;
|
||||
use url::Url;
|
||||
|
||||
pub fn to_lp_remote_peer(identity: ed25519::PublicKey, data: TestedNodeLpDetails) -> LpRemotePeer {
|
||||
LpRemotePeer::new(identity, data.x25519).with_key_digests(
|
||||
data.expected_kem_key_hashes,
|
||||
data.expected_signing_key_hashes,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn mixnet_debug_config(
|
||||
min_gateway_performance: Option<u8>,
|
||||
ignore_egress_epoch_role: bool,
|
||||
|
||||
@@ -3,25 +3,25 @@
|
||||
|
||||
use anyhow::{Context, anyhow, bail};
|
||||
use nym_api_requests::models::{
|
||||
AuthenticatorDetailsV1, DeclaredRolesV1, DescribedNodeTypeV1, HostInformationV1,
|
||||
IpPacketRouterDetailsV1, NetworkRequesterDetailsV1, NymNodeDataV1,
|
||||
OffsetDateTimeJsonSchemaWrapper, WebSocketsV1, WireguardDetailsV1,
|
||||
AuthenticatorDetailsV2, DeclaredRolesV2, DescribedNodeTypeV2, HostInformationV2,
|
||||
IpPacketRouterDetailsV2, NetworkRequesterDetailsV2, NymNodeDataV2,
|
||||
OffsetDateTimeJsonSchemaWrapper, WebSocketsV2, WireguardDetailsV2,
|
||||
};
|
||||
use nym_authenticator_requests::AuthenticatorVersion;
|
||||
use nym_bin_common::build_information::BinaryBuildInformationOwned;
|
||||
use nym_crypto::asymmetric::x25519;
|
||||
use nym_http_api_client::UserAgent;
|
||||
use nym_kkt_ciphersuite::SignatureScheme;
|
||||
use nym_kkt_ciphersuite::Ciphersuite;
|
||||
use nym_kkt_ciphersuite::{KEM, KEMKeyDigests};
|
||||
use nym_lp::peer::{DHPublicKey, LpRemotePeer};
|
||||
use nym_network_defaults::DEFAULT_NYM_NODE_HTTP_PORT;
|
||||
use nym_node_requests::api::client::NymNodeApiClientExt;
|
||||
use nym_node_requests::api::v1::node::models::AuxiliaryDetails as NodeAuxiliaryDetails;
|
||||
use nym_sdk::mixnet::NodeIdentity;
|
||||
use nym_sdk::mixnet::Recipient;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::models::NymNodeDescriptionV1;
|
||||
use nym_validator_client::models::NymNodeDescriptionV2;
|
||||
use rand::prelude::IteratorRandom;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
@@ -90,7 +90,7 @@ use url::Url;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DirectoryNode {
|
||||
described: NymNodeDescriptionV1,
|
||||
described: NymNodeDescriptionV2,
|
||||
}
|
||||
|
||||
impl DirectoryNode {
|
||||
@@ -237,11 +237,11 @@ pub async fn query_gateway_by_ip(address: String) -> anyhow::Result<DirectoryNod
|
||||
.await
|
||||
.inspect_err(|e| error!("Failed to get wireguard information : {e}"))
|
||||
.ok();
|
||||
// let lp_result = client
|
||||
// .get_lewes_protocol()
|
||||
// .await
|
||||
// .inspect_err(|e| error!("Failed to get LP information : {e}"))
|
||||
// .ok();
|
||||
let lp_result = client
|
||||
.get_lewes_protocol()
|
||||
.await
|
||||
.inspect_err(|e| error!("Failed to get LP information : {e}"))
|
||||
.ok();
|
||||
|
||||
// Check required fields
|
||||
let host_info = host_info_result.context("Failed to get host information")?;
|
||||
@@ -261,22 +261,22 @@ pub async fn query_gateway_by_ip(address: String) -> anyhow::Result<DirectoryNod
|
||||
}
|
||||
|
||||
// Convert to our internal types
|
||||
let network_requester: Option<NetworkRequesterDetailsV1> =
|
||||
nr_result.map(|nr| NetworkRequesterDetailsV1 {
|
||||
let network_requester: Option<NetworkRequesterDetailsV2> =
|
||||
nr_result.map(|nr| NetworkRequesterDetailsV2 {
|
||||
address: nr.address,
|
||||
uses_exit_policy: false, // Field not availabe, to change if it becomes useful here
|
||||
});
|
||||
let ip_packet_router: Option<IpPacketRouterDetailsV1> =
|
||||
ipr_result.map(|ipr| IpPacketRouterDetailsV1 {
|
||||
let ip_packet_router: Option<IpPacketRouterDetailsV2> =
|
||||
ipr_result.map(|ipr| IpPacketRouterDetailsV2 {
|
||||
address: ipr.address,
|
||||
});
|
||||
let authenticator: Option<AuthenticatorDetailsV1> =
|
||||
authenticator_result.map(|auth| AuthenticatorDetailsV1 {
|
||||
let authenticator: Option<AuthenticatorDetailsV2> =
|
||||
authenticator_result.map(|auth| AuthenticatorDetailsV2 {
|
||||
address: auth.address,
|
||||
});
|
||||
#[allow(deprecated)]
|
||||
let wireguard: Option<WireguardDetailsV1> =
|
||||
wireguard_result.map(|wg| WireguardDetailsV1 {
|
||||
let wireguard: Option<WireguardDetailsV2> =
|
||||
wireguard_result.map(|wg| WireguardDetailsV2 {
|
||||
port: wg.tunnel_port, // Use tunnel_port for deprecated port field
|
||||
tunnel_port: wg.tunnel_port,
|
||||
metadata_port: wg.metadata_port,
|
||||
@@ -284,14 +284,14 @@ pub async fn query_gateway_by_ip(address: String) -> anyhow::Result<DirectoryNod
|
||||
});
|
||||
|
||||
// Construct NymNodeData
|
||||
let node_data = NymNodeDataV1 {
|
||||
let node_data = NymNodeDataV2 {
|
||||
last_polled: OffsetDateTimeJsonSchemaWrapper(OffsetDateTime::now_utc()),
|
||||
host_information: HostInformationV1 {
|
||||
host_information: HostInformationV2 {
|
||||
ip_address: host_info.data.ip_address,
|
||||
hostname: host_info.data.hostname,
|
||||
keys: host_info.data.keys.into(),
|
||||
},
|
||||
declared_role: DeclaredRolesV1 {
|
||||
declared_role: DeclaredRolesV2 {
|
||||
mixnode: roles.mixnode_enabled,
|
||||
entry: roles.gateway_enabled,
|
||||
exit_nr: roles.network_requester_enabled,
|
||||
@@ -314,17 +314,17 @@ pub async fn query_gateway_by_ip(address: String) -> anyhow::Result<DirectoryNod
|
||||
ip_packet_router,
|
||||
authenticator,
|
||||
wireguard,
|
||||
// lewes_protocol: lp_result.map(Into::into),
|
||||
mixnet_websockets: WebSocketsV1 {
|
||||
lewes_protocol: lp_result.map(Into::into),
|
||||
mixnet_websockets: WebSocketsV2 {
|
||||
ws_port: websockets.ws_port,
|
||||
wss_port: websockets.wss_port,
|
||||
},
|
||||
};
|
||||
|
||||
// Create NymNodeDescription
|
||||
let described = NymNodeDescriptionV1 {
|
||||
let described = NymNodeDescriptionV2 {
|
||||
node_id: 0, // We don't have a node_id from direct query
|
||||
contract_node_type: DescribedNodeTypeV1::NymNode, // All new nodes are NymNode type
|
||||
contract_node_type: DescribedNodeTypeV2::NymNode, // All new nodes are NymNode type
|
||||
description: node_data,
|
||||
};
|
||||
|
||||
@@ -361,7 +361,7 @@ impl NymApiDirectory {
|
||||
|
||||
debug!("Fetching all described nodes from nym-api...");
|
||||
let described_nodes = api_client
|
||||
.get_all_described_nodes()
|
||||
.get_all_described_nodes_v2()
|
||||
.await
|
||||
.context("nym api query failure")?;
|
||||
|
||||
@@ -482,8 +482,14 @@ pub struct TestedNodeDetails {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestedNodeLpDetails {
|
||||
pub address: SocketAddr,
|
||||
pub expected_kem_key_hashes: HashMap<KEM, KEMKeyDigests>,
|
||||
pub expected_signing_key_hashes: HashMap<SignatureScheme, KEMKeyDigests>,
|
||||
pub x25519: x25519::PublicKey,
|
||||
pub expected_kem_key_hashes: BTreeMap<KEM, KEMKeyDigests>,
|
||||
pub x25519: DHPublicKey,
|
||||
pub lp_version: u8,
|
||||
pub ciphersuite: Ciphersuite,
|
||||
}
|
||||
|
||||
impl TestedNodeLpDetails {
|
||||
pub fn into_remote_peer(self) -> LpRemotePeer {
|
||||
LpRemotePeer::new(self.x25519).with_key_digests(self.expected_kem_key_hashes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,12 @@ use nym_credentials_interface::{CredentialSpendingData, TicketType};
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_ip_packet_client::IprClientConnect;
|
||||
use nym_ip_packet_requests::{IpPair, codec::MultiIpPacketCodec};
|
||||
use nym_lp::peer::DHKeyPair;
|
||||
use nym_registration_client::{LpRegistrationClient, NestedLpSession};
|
||||
use nym_sdk::NymNetworkDetails;
|
||||
use nym_sdk::mixnet::{MixnetClient, MixnetClientBuilder, NodeIdentity, Recipient, Socks5};
|
||||
use nym_topology::{HardcodedTopologyProvider, NymTopology};
|
||||
use rand09::SeedableRng;
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||
sync::Arc,
|
||||
@@ -163,23 +165,25 @@ pub async fn lp_registration_probe(
|
||||
) -> anyhow::Result<LpProbeResults> {
|
||||
let lp_address = gateway_lp_data.address;
|
||||
let lp_version = gateway_lp_data.lp_version;
|
||||
let peer = helpers::to_lp_remote_peer(gateway_identity, gateway_lp_data);
|
||||
let lp_ciphersuite = gateway_lp_data.ciphersuite;
|
||||
let peer = gateway_lp_data.into_remote_peer();
|
||||
|
||||
info!("Starting LP registration probe for gateway at {lp_address}");
|
||||
|
||||
let mut lp_outcome = LpProbeResults::default();
|
||||
|
||||
// Generate Ed25519 keypair for this connection (X25519 will be derived internally by LP)
|
||||
let mut rng = rand::thread_rng();
|
||||
let client_ed25519_keypair = std::sync::Arc::new(ed25519::KeyPair::new(&mut rng));
|
||||
let mut rng09 = rand09::rngs::StdRng::from_os_rng();
|
||||
let client_x25519_keypair = Arc::new(DHKeyPair::new(&mut rng09));
|
||||
|
||||
// Step 0: Derive X25519 keys from Ed25519 for the gateways
|
||||
|
||||
// Create LP registration client (uses Ed25519 keys directly, derives X25519 internally)
|
||||
let mut client = LpRegistrationClient::<TcpStream>::new_with_default_config(
|
||||
client_ed25519_keypair,
|
||||
client_x25519_keypair,
|
||||
peer,
|
||||
lp_address,
|
||||
lp_ciphersuite,
|
||||
lp_version,
|
||||
);
|
||||
|
||||
@@ -209,23 +213,13 @@ pub async fn lp_registration_probe(
|
||||
let wg_keypair = nym_crypto::asymmetric::x25519::KeyPair::new(&mut rng);
|
||||
|
||||
// Convert gateway identity to ed25519 public key
|
||||
let gateway_ed25519_pubkey = match nym_crypto::asymmetric::ed25519::PublicKey::from_bytes(
|
||||
&gateway_identity.to_bytes(),
|
||||
) {
|
||||
Ok(key) => key,
|
||||
Err(e) => {
|
||||
let error_msg = format!("Failed to convert gateway identity: {}", e);
|
||||
error!("{}", error_msg);
|
||||
lp_outcome.error = Some(error_msg);
|
||||
return Ok(lp_outcome);
|
||||
}
|
||||
};
|
||||
let gateway_ed25519_pubkey = gateway_identity;
|
||||
|
||||
// Register using the new packet-per-connection API (returns GatewayData directly)
|
||||
let ticket_type = TicketType::V1WireguardEntry;
|
||||
let gateway_data = match client
|
||||
.register_dvpn(
|
||||
&mut rng,
|
||||
&mut rng09,
|
||||
&wg_keypair,
|
||||
&gateway_ed25519_pubkey,
|
||||
bandwidth_controller,
|
||||
@@ -299,21 +293,26 @@ pub async fn wg_probe_lp(
|
||||
let entry_lp_version = entry_lp_data.lp_version;
|
||||
let exit_lp_version = exit_lp_data.lp_version;
|
||||
|
||||
let entry_lp_ciphersuite = entry_lp_data.ciphersuite;
|
||||
let exit_lp_ciphersuite = exit_lp_data.ciphersuite;
|
||||
|
||||
info!("Starting LP-based WireGuard probe (entry→exit via forwarding)");
|
||||
|
||||
let mut wg_outcome = WgProbeResults::default();
|
||||
|
||||
// Generate Ed25519 keypairs for LP protocol
|
||||
let mut rng = rand::thread_rng();
|
||||
let entry_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut rng));
|
||||
let exit_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut rng));
|
||||
// Generate x25519 keypairs for LP protocol
|
||||
let mut rng09 = rand09::rngs::StdRng::from_os_rng();
|
||||
|
||||
let entry_lp_keypair = Arc::new(DHKeyPair::new(&mut rng09));
|
||||
let exit_lp_keypair = Arc::new(DHKeyPair::new(&mut rng09));
|
||||
|
||||
// Generate WireGuard keypairs for VPN registration
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
let entry_wg_keypair = x25519::KeyPair::new(&mut rng);
|
||||
let exit_wg_keypair = x25519::KeyPair::new(&mut rng);
|
||||
|
||||
let entry_peer = helpers::to_lp_remote_peer(entry_gateway.identity, entry_lp_data);
|
||||
let exit_peer = helpers::to_lp_remote_peer(exit_gateway.identity, exit_lp_data);
|
||||
let entry_peer = entry_lp_data.into_remote_peer();
|
||||
let exit_peer = exit_lp_data.into_remote_peer();
|
||||
|
||||
// STEP 1: Establish outer LP session with entry gateway
|
||||
// LpRegistrationClient uses packet-per-connection model - connect() is gone,
|
||||
@@ -323,6 +322,7 @@ pub async fn wg_probe_lp(
|
||||
entry_lp_keypair,
|
||||
entry_peer,
|
||||
entry_address,
|
||||
entry_lp_ciphersuite,
|
||||
entry_lp_version,
|
||||
);
|
||||
|
||||
@@ -335,16 +335,26 @@ pub async fn wg_probe_lp(
|
||||
|
||||
// STEP 2: Use nested session to register with exit gateway via forwarding
|
||||
info!("Registering with exit gateway via entry forwarding...");
|
||||
let mut nested_session =
|
||||
NestedLpSession::new(exit_address, exit_lp_keypair, exit_peer, exit_lp_version);
|
||||
let mut nested_session = NestedLpSession::new(
|
||||
exit_address,
|
||||
exit_lp_keypair,
|
||||
exit_peer,
|
||||
exit_lp_ciphersuite,
|
||||
exit_lp_version,
|
||||
);
|
||||
|
||||
let exit_gateway_pubkey = exit_gateway.identity;
|
||||
|
||||
// Perform handshake and registration with exit gateway via forwarding
|
||||
if let Err(err) = nested_session.perform_handshake(&mut entry_client).await {
|
||||
error!("Failed to perform handshake with exit gateway: {err}");
|
||||
return Ok(wg_outcome);
|
||||
};
|
||||
|
||||
let exit_gateway_data = match nested_session
|
||||
.handshake_and_register_dvpn(
|
||||
.register_dvpn(
|
||||
&mut entry_client,
|
||||
&mut rng,
|
||||
&mut rng09,
|
||||
&exit_wg_keypair,
|
||||
&exit_gateway_pubkey,
|
||||
bandwidth_controller,
|
||||
@@ -370,7 +380,7 @@ pub async fn wg_probe_lp(
|
||||
// Use packet-per-connection register() which returns GatewayData directly
|
||||
let entry_gateway_data = match entry_client
|
||||
.register_dvpn(
|
||||
&mut rng,
|
||||
&mut rng09,
|
||||
&entry_wg_keypair,
|
||||
&entry_gateway_pubkey,
|
||||
bandwidth_controller,
|
||||
|
||||
Reference in New Issue
Block a user