Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21870e730f | |||
| ea99e485b1 | |||
| 4a623a86da | |||
| 691148a070 | |||
| 2e27d2374c | |||
| d2df3bcdc8 |
Generated
+1
@@ -8466,6 +8466,7 @@ dependencies = [
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"x25519-dalek",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+3
-2
@@ -173,7 +173,8 @@ members = [
|
||||
"wasm/zknym-lib",
|
||||
# "nym-gateway-probe",
|
||||
"integration-tests",
|
||||
"common/nym-kkt-ciphersuite", "common/nym-kkt-context",
|
||||
"common/nym-kkt-ciphersuite",
|
||||
"common/nym-kkt-context",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -190,7 +191,7 @@ default-members = [
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
"tools/nymvisor",
|
||||
"nym-registration-client"
|
||||
"nym-registration-client",
|
||||
]
|
||||
|
||||
exclude = ["contracts", "nym-wallet", "cpu-cycles"]
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
pub const NYM_API_DOMAIN: &str = "validator.nymtech.net";
|
||||
pub const NYM_API_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(212, 71, 233, 232))];
|
||||
pub const NYM_API_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(92, 39, 63, 14))];
|
||||
|
||||
pub const NYM_VPN_API_DOMAIN: &str = "nymvpn.com";
|
||||
pub const NYM_VPN_API_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(76, 76, 21, 21))];
|
||||
|
||||
@@ -7,6 +7,7 @@ use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_ip_packet_requests::IpPair;
|
||||
use nym_kkt_ciphersuite::{Ciphersuite, KEM, KEMKeyDigests};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_wireguard_types::PresharedKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
@@ -44,11 +45,11 @@ pub struct WireguardRegistrationData {
|
||||
pub private_ipv6: Ipv6Addr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct WireguardConfiguration {
|
||||
#[serde(with = "bs58_x25519_pubkey")]
|
||||
pub public_key: x25519::PublicKey,
|
||||
pub psk: Option<[u8; 32]>,
|
||||
pub psk: Option<PresharedKey>,
|
||||
pub endpoint: SocketAddr,
|
||||
pub private_ipv4: Ipv4Addr,
|
||||
pub private_ipv6: Ipv6Addr,
|
||||
|
||||
@@ -15,6 +15,7 @@ description = "Wireguard public key and config definitions"
|
||||
base64 = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
zeroize.workspace = true
|
||||
|
||||
x25519-dalek = { workspace = true, features = ["static_secrets"] }
|
||||
nym-crypto = { workspace = true, features = ["asymmetric"] }
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod preshared_key;
|
||||
pub mod public_key;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
pub use config::Config;
|
||||
pub use error::Error;
|
||||
pub use preshared_key::PresharedKey;
|
||||
pub use public_key::PeerPublicKey;
|
||||
|
||||
pub const DEFAULT_PEER_TIMEOUT_CHECK: Duration = Duration::from_secs(5); // 5 seconds
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
|
||||
pub struct PresharedKey([u8; 32]);
|
||||
|
||||
impl PresharedKey {
|
||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 32]> for PresharedKey {
|
||||
fn from(key: [u8; 32]) -> PresharedKey {
|
||||
PresharedKey(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PresharedKey> for [u8; 32] {
|
||||
fn from(key: PresharedKey) -> [u8; 32] {
|
||||
key.0
|
||||
}
|
||||
}
|
||||
@@ -296,11 +296,14 @@ fn translate_digests(
|
||||
ToSchema,
|
||||
Ord,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
#[non_exhaustive]
|
||||
pub enum LPKEM {
|
||||
#[serde(alias = "ml_kem768")]
|
||||
MlKem768,
|
||||
|
||||
#[serde(alias = "mc_eliece")]
|
||||
McEliece,
|
||||
}
|
||||
|
||||
@@ -320,7 +323,7 @@ pub enum LPKEM {
|
||||
ToSchema,
|
||||
Ord,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
#[non_exhaustive]
|
||||
pub enum LPHashFunction {
|
||||
@@ -345,7 +348,7 @@ pub enum LPHashFunction {
|
||||
EnumString,
|
||||
ToSchema,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
#[non_exhaustive]
|
||||
pub enum LPSignatureScheme {
|
||||
|
||||
@@ -242,7 +242,7 @@ pub async fn lp_registration_probe(
|
||||
" - PSK: {:?}",
|
||||
gateway_data
|
||||
.psk
|
||||
.map(|k| general_purpose::STANDARD.encode(k))
|
||||
.map(|k| general_purpose::STANDARD.encode(k.as_bytes()))
|
||||
);
|
||||
info!(" - Private IPv4: {}", gateway_data.private_ipv4);
|
||||
info!(" - Private IPv6: {}", gateway_data.private_ipv6);
|
||||
|
||||
@@ -71,11 +71,14 @@ impl LewesProtocol {
|
||||
EnumString,
|
||||
Ord,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
pub enum LPKEM {
|
||||
#[serde(alias = "ml_kem768")]
|
||||
MlKem768,
|
||||
|
||||
#[serde(alias = "mc_eliece")]
|
||||
McEliece,
|
||||
}
|
||||
|
||||
@@ -113,7 +116,7 @@ impl From<nym_kkt_ciphersuite::KEM> for LPKEM {
|
||||
EnumIter,
|
||||
Ord,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
pub enum LPHashFunction {
|
||||
@@ -160,7 +163,7 @@ impl From<nym_kkt_ciphersuite::HashFunction> for LPHashFunction {
|
||||
EnumString,
|
||||
EnumIter,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
pub enum LPSignatureScheme {
|
||||
|
||||
@@ -41,7 +41,7 @@ pub struct BuilderConfig {
|
||||
|
||||
// Toggle
|
||||
#[builder(default)]
|
||||
pub enable_lp_regitration: bool,
|
||||
pub enable_lp_registration: bool,
|
||||
|
||||
// Mixnet based only option
|
||||
pub mixnet_client_config: MixnetClientConfig,
|
||||
|
||||
@@ -14,7 +14,9 @@ use nym_validator_client::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
RegistrationClient, clients::MixnetBasedRegistrationClient, config::RegistrationMode,
|
||||
RegistrationClient,
|
||||
clients::{LpBasedRegistrationClient, MixnetBasedRegistrationClient},
|
||||
config::RegistrationMode,
|
||||
error::RegistrationClientError,
|
||||
};
|
||||
use config::BuilderConfig;
|
||||
@@ -31,7 +33,7 @@ impl RegistrationClientBuilder {
|
||||
}
|
||||
|
||||
pub fn use_lp(&self) -> bool {
|
||||
let lp_enabled = self.config.enable_lp_regitration;
|
||||
let lp_enabled = self.config.enable_lp_registration;
|
||||
let lp_info_available = self.config.entry_node.node.lp_data.is_some()
|
||||
&& self.config.exit_node.node.lp_data.is_some();
|
||||
// To remove when LP supports Mixnet registration
|
||||
@@ -48,10 +50,7 @@ impl RegistrationClientBuilder {
|
||||
pub async fn build(self) -> Result<RegistrationClient, RegistrationClientError> {
|
||||
if self.use_lp() {
|
||||
tracing::debug!("Using LP for registration");
|
||||
Err(RegistrationClientError::LpRegistrationNotPossible {
|
||||
node_id: "any".to_string(),
|
||||
})
|
||||
// Ok(RegistrationClient::Lp(Box::new(self.build_lp().await?)))
|
||||
Ok(RegistrationClient::Lp(Box::new(self.build_lp().await?)))
|
||||
} else {
|
||||
tracing::debug!("Using Mixnet for registration");
|
||||
Ok(RegistrationClient::Mixnet(Box::new(
|
||||
@@ -124,29 +123,29 @@ impl RegistrationClientBuilder {
|
||||
})
|
||||
}
|
||||
|
||||
// async fn build_lp(self) -> Result<LpBasedRegistrationClient, RegistrationClientError> {
|
||||
// let storage = self.config.setup_credential_storage().await?;
|
||||
// let config = self.config.registration_client_config();
|
||||
//
|
||||
// let nyxd_client = get_nyxd_client(&self.config.network_env)?;
|
||||
//
|
||||
// let bandwidth_controller: Box<dyn BandwidthTicketProvider> =
|
||||
// if let Some(credential_storage) = storage {
|
||||
// Box::new(BandwidthController::new(credential_storage, nyxd_client))
|
||||
// } else {
|
||||
// Box::new(BandwidthController::new(
|
||||
// EphemeralCredentialStorage::default(),
|
||||
// nyxd_client,
|
||||
// ))
|
||||
// };
|
||||
//
|
||||
// Ok(LpBasedRegistrationClient {
|
||||
// config,
|
||||
// bandwidth_controller,
|
||||
// cancel_token: self.config.cancel_token.clone(),
|
||||
// fallback_client_builder: Some(self),
|
||||
// })
|
||||
// }
|
||||
async fn build_lp(self) -> Result<LpBasedRegistrationClient, RegistrationClientError> {
|
||||
let storage = self.config.setup_credential_storage().await?;
|
||||
let config = self.config.registration_client_config();
|
||||
|
||||
let nyxd_client = get_nyxd_client(&self.config.network_env)?;
|
||||
|
||||
let bandwidth_controller: Box<dyn BandwidthTicketProvider> =
|
||||
if let Some(credential_storage) = storage {
|
||||
Box::new(BandwidthController::new(credential_storage, nyxd_client))
|
||||
} else {
|
||||
Box::new(BandwidthController::new(
|
||||
EphemeralCredentialStorage::default(),
|
||||
nyxd_client,
|
||||
))
|
||||
};
|
||||
|
||||
Ok(LpBasedRegistrationClient {
|
||||
config,
|
||||
bandwidth_controller,
|
||||
cancel_token: self.config.cancel_token.clone(),
|
||||
fallback_client_builder: Some(self),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// temporary while we use the legacy bandwidth-controller
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::builder::RegistrationClientBuilder;
|
||||
use crate::config::RegistrationClientConfig;
|
||||
use crate::config::RegistrationMode;
|
||||
use crate::error::RegistrationClientError;
|
||||
use crate::lp_client::helpers::to_lp_remote_peer;
|
||||
use crate::lp_client::{LpRegistrationClient, NestedLpSession};
|
||||
use crate::types::{LpRegistrationResult, RegistrationResult};
|
||||
use crate::types::{RegistrationResult, WireguardRegistrationResult};
|
||||
|
||||
use nym_bandwidth_controller::BandwidthTicketProvider;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
|
||||
use nym_lp::peer::DHKeyPair;
|
||||
use rand09::{CryptoRng, RngCore};
|
||||
use rand09::{CryptoRng, RngCore, SeedableRng};
|
||||
use std::sync::Arc;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
@@ -150,15 +148,15 @@ impl LpBasedRegistrationClient {
|
||||
// All data flows through WireGuard after this point.
|
||||
// Each LP packet used its own TCP connection which was closed after the exchange.
|
||||
// Exit registration was completed via forwarding through entry gateway.
|
||||
Ok(RegistrationResult::Lp(Box::new(LpRegistrationResult {
|
||||
Ok(RegistrationResult::wireguard_lp(
|
||||
entry_gateway_data,
|
||||
exit_gateway_data,
|
||||
bw_controller: self.bandwidth_controller,
|
||||
})))
|
||||
self.bandwidth_controller,
|
||||
))
|
||||
}
|
||||
|
||||
async fn register_wg(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
let mut rng = rand09::rng();
|
||||
let mut rng = rand09::rngs::StdRng::from_os_rng();
|
||||
|
||||
self.register_wg_with_rng(&mut rng).await
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::AuthenticatorRegistrationResult;
|
||||
use crate::config::RegistrationClientConfig;
|
||||
use crate::config::RegistrationMode;
|
||||
use crate::error::RegistrationClientError;
|
||||
@@ -84,19 +85,17 @@ impl MixnetBasedRegistrationClient {
|
||||
}
|
||||
};
|
||||
|
||||
Ok(RegistrationResult::Mixnet(Box::new(
|
||||
MixnetRegistrationResult {
|
||||
mixnet_client: ipr_client.into_mixnet_client(),
|
||||
assigned_addresses: AssignedAddresses {
|
||||
interface_addresses,
|
||||
exit_mix_address: ipr_address,
|
||||
mixnet_client_address: self.mixnet_client_address,
|
||||
entry_mixnet_gateway_ip,
|
||||
exit_mixnet_gateway_ip,
|
||||
},
|
||||
event_rx: self.event_rx,
|
||||
Ok(RegistrationResult::mixnet(
|
||||
ipr_client.into_mixnet_client(),
|
||||
AssignedAddresses {
|
||||
interface_addresses,
|
||||
exit_mix_address: ipr_address,
|
||||
mixnet_client_address: self.mixnet_client_address,
|
||||
entry_mixnet_gateway_ip,
|
||||
exit_mixnet_gateway_ip,
|
||||
},
|
||||
)))
|
||||
self.event_rx,
|
||||
))
|
||||
}
|
||||
|
||||
async fn register_wg(self) -> Result<RegistrationResult, RegistrationError> {
|
||||
@@ -199,16 +198,14 @@ impl MixnetBasedRegistrationClient {
|
||||
}
|
||||
};
|
||||
|
||||
Ok(RegistrationResult::Wireguard(Box::new(
|
||||
WireguardRegistrationResult {
|
||||
entry_gateway_client: entry_auth_client,
|
||||
exit_gateway_client: exit_auth_client,
|
||||
entry_gateway_data: entry,
|
||||
exit_gateway_data: exit,
|
||||
authenticator_listener_handle: mixnet_listener,
|
||||
bw_controller: self.bandwidth_controller,
|
||||
},
|
||||
)))
|
||||
Ok(RegistrationResult::wireguard_legacy(
|
||||
entry_auth_client,
|
||||
exit_auth_client,
|
||||
entry,
|
||||
exit,
|
||||
mixnet_listener,
|
||||
self.bandwidth_controller,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn register(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
|
||||
@@ -12,7 +12,8 @@ pub use lp_client::{
|
||||
LpRegistrationClient, LpRegistrationConfig, NestedLpSession, error::LpClientError,
|
||||
};
|
||||
pub use types::{
|
||||
LpRegistrationResult, MixnetRegistrationResult, RegistrationResult, WireguardRegistrationResult,
|
||||
AuthenticatorRegistrationResult, LpRegistrationResult, MixnetRegistrationResult,
|
||||
RegistrationResult, WireguardRegistrationResult,
|
||||
};
|
||||
|
||||
mod builder;
|
||||
@@ -24,14 +25,14 @@ mod types;
|
||||
|
||||
pub enum RegistrationClient {
|
||||
Mixnet(Box<clients::MixnetBasedRegistrationClient>),
|
||||
// Lp(Box<clients::LpBasedRegistrationClient>),
|
||||
Lp(Box<clients::LpBasedRegistrationClient>),
|
||||
}
|
||||
|
||||
impl RegistrationClient {
|
||||
pub async fn register(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
match self {
|
||||
RegistrationClient::Mixnet(client) => client.register().await,
|
||||
// RegistrationClient::Lp(client) => client.register().await,
|
||||
RegistrationClient::Lp(client) => client.register().await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,7 +595,7 @@ where
|
||||
|
||||
Ok(WireguardConfiguration {
|
||||
public_key: final_response.public_key,
|
||||
psk: Some(psk),
|
||||
psk: Some(psk.into()),
|
||||
endpoint: SocketAddr::new(self.gateway_lp_address.ip(), final_response.port),
|
||||
private_ipv4: final_response.private_ipv4,
|
||||
private_ipv6: final_response.private_ipv6,
|
||||
|
||||
@@ -398,10 +398,9 @@ impl NestedLpSession {
|
||||
}
|
||||
};
|
||||
|
||||
// JS/SW TODO Adapt this to new gateway response
|
||||
Ok(WireguardConfiguration {
|
||||
public_key: final_response.public_key,
|
||||
psk: Some(psk),
|
||||
psk: Some(psk.into()),
|
||||
endpoint: SocketAddr::new(self.exit_address.ip(), final_response.port),
|
||||
private_ipv4: final_response.private_ipv4,
|
||||
private_ipv6: final_response.private_ipv6,
|
||||
|
||||
@@ -9,7 +9,54 @@ use nym_sdk::mixnet::{EventReceiver, MixnetClient};
|
||||
pub enum RegistrationResult {
|
||||
Mixnet(Box<MixnetRegistrationResult>),
|
||||
Wireguard(Box<WireguardRegistrationResult>),
|
||||
Lp(Box<LpRegistrationResult>),
|
||||
}
|
||||
|
||||
impl RegistrationResult {
|
||||
pub fn mixnet(
|
||||
mixnet_client: MixnetClient,
|
||||
assigned_addresses: AssignedAddresses,
|
||||
event_rx: EventReceiver,
|
||||
) -> Self {
|
||||
RegistrationResult::Mixnet(Box::new(MixnetRegistrationResult {
|
||||
assigned_addresses,
|
||||
mixnet_client,
|
||||
event_rx,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn wireguard_legacy(
|
||||
entry_gateway_client: AuthenticatorClient,
|
||||
exit_gateway_client: AuthenticatorClient,
|
||||
entry_gateway_data: WireguardConfiguration,
|
||||
exit_gateway_data: WireguardConfiguration,
|
||||
authenticator_listener_handle: AuthClientMixnetListenerHandle,
|
||||
bw_controller: Box<dyn BandwidthTicketProvider>,
|
||||
) -> Self {
|
||||
RegistrationResult::Wireguard(Box::new(WireguardRegistrationResult::Legacy(Box::new(
|
||||
AuthenticatorRegistrationResult {
|
||||
entry_gateway_client,
|
||||
exit_gateway_client,
|
||||
entry_gateway_data,
|
||||
exit_gateway_data,
|
||||
authenticator_listener_handle,
|
||||
bw_controller,
|
||||
},
|
||||
))))
|
||||
}
|
||||
|
||||
pub fn wireguard_lp(
|
||||
entry_gateway_data: WireguardConfiguration,
|
||||
exit_gateway_data: WireguardConfiguration,
|
||||
bw_controller: Box<dyn BandwidthTicketProvider>,
|
||||
) -> Self {
|
||||
RegistrationResult::Wireguard(Box::new(WireguardRegistrationResult::LewesProtocol(
|
||||
Box::new(LpRegistrationResult {
|
||||
entry_gateway_data,
|
||||
exit_gateway_data,
|
||||
bw_controller,
|
||||
}),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MixnetRegistrationResult {
|
||||
@@ -18,7 +65,28 @@ pub struct MixnetRegistrationResult {
|
||||
pub event_rx: EventReceiver,
|
||||
}
|
||||
|
||||
pub struct WireguardRegistrationResult {
|
||||
pub enum WireguardRegistrationResult {
|
||||
Legacy(Box<AuthenticatorRegistrationResult>),
|
||||
LewesProtocol(Box<LpRegistrationResult>),
|
||||
}
|
||||
|
||||
impl WireguardRegistrationResult {
|
||||
pub fn entry_gateway_data(&self) -> &WireguardConfiguration {
|
||||
match self {
|
||||
Self::Legacy(res) => &res.entry_gateway_data,
|
||||
Self::LewesProtocol(res) => &res.entry_gateway_data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit_gateway_data(&self) -> &WireguardConfiguration {
|
||||
match self {
|
||||
Self::Legacy(res) => &res.exit_gateway_data,
|
||||
Self::LewesProtocol(res) => &res.exit_gateway_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AuthenticatorRegistrationResult {
|
||||
pub entry_gateway_client: AuthenticatorClient,
|
||||
pub exit_gateway_client: AuthenticatorClient,
|
||||
pub entry_gateway_data: WireguardConfiguration,
|
||||
|
||||
Reference in New Issue
Block a user