Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f008c33eb5 | |||
| deb214d176 | |||
| a376fec990 | |||
| 02459f5d53 | |||
| d1303947c0 |
@@ -43,7 +43,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
args: --workspace --release --features wireguard
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
args: --workspace --release --features wireguard
|
||||
|
||||
- name: Prepare build output
|
||||
shell: bash
|
||||
|
||||
Generated
+11
@@ -4282,6 +4282,15 @@ version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8eca9f51da27bc908ef3dd85c21e1bbba794edaf94d7841e37356275b82d31e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
version = "0.18.0"
|
||||
@@ -6532,6 +6541,7 @@ dependencies = [
|
||||
"futures",
|
||||
"humantime-serde",
|
||||
"hyper",
|
||||
"ipnetwork 0.16.0",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"nym-api-requests",
|
||||
@@ -6898,6 +6908,7 @@ dependencies = [
|
||||
"fastrand 2.0.1",
|
||||
"hmac 0.12.1",
|
||||
"hyper",
|
||||
"ipnetwork 0.16.0",
|
||||
"mime",
|
||||
"nym-config",
|
||||
"nym-crypto",
|
||||
|
||||
@@ -74,8 +74,8 @@ pub async fn current_gateways<R: Rng>(
|
||||
.collect::<Vec<gateway::Node>>();
|
||||
|
||||
// we were always filtering by version so I'm not removing that 'feature'
|
||||
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
|
||||
Ok(filtered_gateways)
|
||||
// let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
|
||||
Ok(valid_gateways)
|
||||
}
|
||||
|
||||
pub async fn current_mixnodes<R: Rng>(
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::PeerPublicKey;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use dashmap::DashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::net::IpAddr;
|
||||
use std::{fmt, ops::Deref, str::FromStr};
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
@@ -17,11 +18,13 @@ use sha2::Sha256;
|
||||
|
||||
pub type GatewayClientRegistry = DashMap<PeerPublicKey, GatewayClient>;
|
||||
pub type PendingRegistrations = DashMap<PeerPublicKey, Nonce>;
|
||||
pub type PrivateIPs = DashMap<IpAddr, Free>;
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
pub type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
pub type Nonce = u64;
|
||||
pub type Free = bool;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
@@ -72,6 +75,9 @@ pub struct GatewayClient {
|
||||
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Assigned private IP
|
||||
pub private_ip: IpAddr,
|
||||
|
||||
/// Sha256 hmac on the data (alongside the prior nonce)
|
||||
#[cfg_attr(feature = "openapi", schema(value_type = String, format = Byte))]
|
||||
pub mac: ClientMac,
|
||||
@@ -79,7 +85,12 @@ pub struct GatewayClient {
|
||||
|
||||
impl GatewayClient {
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn new(local_secret: &PrivateKey, remote_public: PublicKey, nonce: u64) -> Self {
|
||||
pub fn new(
|
||||
local_secret: &PrivateKey,
|
||||
remote_public: PublicKey,
|
||||
private_ip: IpAddr,
|
||||
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())
|
||||
@@ -96,10 +107,12 @@ impl GatewayClient {
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(local_public.as_bytes());
|
||||
mac.update(private_ip.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
GatewayClient {
|
||||
pub_key: PeerPublicKey::new(local_public),
|
||||
private_ip,
|
||||
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
|
||||
}
|
||||
}
|
||||
@@ -121,6 +134,7 @@ impl GatewayClient {
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(self.pub_key.as_bytes());
|
||||
mac.update(self.private_ip.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
mac.verify_slice(&self.mac)
|
||||
@@ -209,6 +223,7 @@ mod tests {
|
||||
let client = GatewayClient::new(
|
||||
client_key_pair.private_key(),
|
||||
*gateway_key_pair.public_key(),
|
||||
"10.0.0.42".parse().unwrap(),
|
||||
nonce,
|
||||
);
|
||||
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
|
||||
|
||||
+1
-3
@@ -1,8 +1,6 @@
|
||||
# Community Applications
|
||||
|
||||
We love seeing our developer community create applications using Nym. If you would like to share your application with the community, please submit a pull request to the `main` branch of the `nymtech/dev-portal` [repository](https://github.com/nymtech/dev-portal).
|
||||
|
||||
|
||||
If you would like to share your application here, please submit a pull request to the `main` branch of the `nymtech/dev-portal` [repository](https://github.com/nymtech/dev-portal).
|
||||
|
||||
## <img src='../images/profile_picture/pastenym_ntv_pp.png' style="float: right; width: 75px; height: 75px;">Pastenym
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Browser only
|
||||
With the Typescript SDK you can run a Nym client in a webworker - meaning you can connect to the mixnet through the browser without having to worry about any other code than your web framework.
|
||||
|
||||
- NoTrustVerify have set up an example application using [`mixFetch`](https://sdk.nymtech.net/examples/mix-fetch) to fetch crypto prices from CoinGecko over the mixnet.
|
||||
- [NoTrustVerify](https://notrustverify.ch/) have set up an example application using [`mixFetch`](https://sdk.nymtech.net/examples/mix-fetch) to fetch crypto prices from CoinGecko over the mixnet.
|
||||
- [Website](https://notrustverify.github.io/mixfetch-examples/)
|
||||
- [Coinbase](https://github.com/notrustverify/mixfetch-examples)
|
||||
- [Codebase](https://github.com/notrustverify/mixfetch-examples)
|
||||
|
||||
- There is a coconut-scheme based Credential Library playground [here](https://coco-demo.nymtech.net/). This is a WASM implementation of our Coconut libraries which generate raw Coconut credentials. Test it to create and re-randomize your own credentials. For more information on what is happening here check out the [Coconut docs](https://nymtech.net/docs/coconut.html).
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Custom services involve two pieces of code that communicate via the mixnet: a cl
|
||||
- [Frontend codebase](https://github.com/notrustverify/pastenym)
|
||||
- [Backend codebase](https://github.com/notrustverify/pastenym-frontend)
|
||||
|
||||
- Nostr-Nym is another application written by NoTrustVerify, standing between mixnet users and a Nostr server in order to protect their metadata from being revealed when gossiping. **Useful for Go and Python developers**.
|
||||
- Nostr-Nym is another application written by [NoTrustVerify](https://notrustverify.ch/), standing between mixnet users and a Nostr server in order to protect their metadata from being revealed when gossiping. **Useful for Go and Python developers**.
|
||||
- [Codebase](https://github.com/notrustverify/nostr-nym)
|
||||
|
||||
- Spook and Nym-Ethtx are both examples of Ethereum transaction broadcasters utilising the mixnet, written in Rust. Since they were written before the release of the Rust SDK, they utilise standalone clients to communicate with the mixnet.
|
||||
|
||||
@@ -27,6 +27,7 @@ dirs = "4.0"
|
||||
dotenvy = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
humantime-serde = "1.0.1"
|
||||
ipnetwork = "0.16"
|
||||
lazy_static = "1.4.0"
|
||||
log = { workspace = true }
|
||||
once_cell = "1.7.2"
|
||||
|
||||
@@ -128,6 +128,7 @@ impl From<ConfigV1_1_31> for Config {
|
||||
enabled: value.wireguard.enabled,
|
||||
bind_address: value.wireguard.bind_address,
|
||||
announced_port: value.wireguard.announced_port,
|
||||
private_network_prefix: Default::default(),
|
||||
storage_paths: nym_node::config::persistence::WireguardPaths {
|
||||
// no fields (yet)
|
||||
},
|
||||
|
||||
@@ -134,6 +134,12 @@ pub(crate) enum GatewayError {
|
||||
// TODO: in the future this should work the other way, i.e. NymNode depending on Gateway errors
|
||||
#[error(transparent)]
|
||||
NymNodeError(#[from] nym_node::error::NymNodeError),
|
||||
|
||||
#[error("there was an issue with wireguard IP network: {source}")]
|
||||
IpNetworkError {
|
||||
#[from]
|
||||
source: ipnetwork::IpNetworkError,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ClientCoreError> for GatewayError {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::config::Config;
|
||||
use crate::error::GatewayError;
|
||||
use crate::node::helpers::load_public_key;
|
||||
use ipnetwork::IpNetwork;
|
||||
use log::warn;
|
||||
use nym_bin_common::bin_info_owned;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
@@ -16,6 +17,7 @@ use nym_node::http::router::WireguardAppState;
|
||||
use nym_node::wireguard::types::GatewayClientRegistry;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_task::TaskClient;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn load_gateway_details(
|
||||
@@ -223,12 +225,17 @@ impl<'a> HttpApiBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let wireguard_private_network = IpNetwork::new(
|
||||
IpAddr::from(Ipv4Addr::new(10, 0, 0, 0)),
|
||||
self.gateway_config.wireguard.private_network_prefix,
|
||||
)?;
|
||||
let wg_state = self.client_registry.map(|client_registry| {
|
||||
WireguardAppState::new(
|
||||
self.sphinx_keypair,
|
||||
client_registry,
|
||||
Default::default(),
|
||||
self.gateway_config.wireguard.bind_address.port(),
|
||||
wireguard_private_network,
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ license.workspace = true
|
||||
anyhow = { workspace = true }
|
||||
bytes = "1.5.0"
|
||||
colored = "2"
|
||||
ipnetwork = "0.16"
|
||||
rand = "0.7.3"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_yaml = "0.9.25"
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -12,6 +12,7 @@ pub mod persistence;
|
||||
mod serde_helpers;
|
||||
|
||||
pub const DEFAULT_WIREGUARD_PORT: u16 = WG_PORT;
|
||||
pub const DEFAULT_WIREGUARD_PREFIX: u8 = 16;
|
||||
pub const DEFAULT_HTTP_PORT: u16 = DEFAULT_NYM_NODE_HTTP_PORT;
|
||||
|
||||
// TODO: this is very much a WIP. we need proper ssl certificate support here
|
||||
@@ -75,6 +76,10 @@ pub struct Wireguard {
|
||||
/// Useful in the instances where the node is behind a proxy.
|
||||
pub announced_port: u16,
|
||||
|
||||
/// The prefix denoting the maximum number of the clients that can be connected via Wireguard.
|
||||
/// The maximum value for IPv4 is 32 and for IPv6 is 128
|
||||
pub private_network_prefix: u8,
|
||||
|
||||
/// Paths for wireguard keys, client registries, etc.
|
||||
pub storage_paths: persistence::WireguardPaths,
|
||||
}
|
||||
@@ -88,6 +93,7 @@ impl Default for Wireguard {
|
||||
DEFAULT_WIREGUARD_PORT,
|
||||
),
|
||||
announced_port: DEFAULT_WIREGUARD_PORT,
|
||||
private_network_prefix: DEFAULT_WIREGUARD_PREFIX,
|
||||
storage_paths: persistence::WireguardPaths {},
|
||||
}
|
||||
}
|
||||
|
||||
+18
-2
@@ -14,6 +14,7 @@ use nym_crypto::asymmetric::encryption::PublicKey;
|
||||
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{
|
||||
ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage, Nonce, PeerPublicKey,
|
||||
};
|
||||
use rand::{prelude::IteratorRandom, thread_rng};
|
||||
|
||||
async fn process_final_message(
|
||||
client: GatewayClient,
|
||||
@@ -91,8 +92,23 @@ pub(crate) async fn register_client(
|
||||
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 gateway_data =
|
||||
GatewayClient::new(state.dh_keypair.private_key(), remote_public, nonce);
|
||||
let mut private_ip_ref = state
|
||||
.free_private_network_ips
|
||||
.iter_mut()
|
||||
.filter(|r| **r)
|
||||
.choose(&mut thread_rng())
|
||||
.ok_or(RequestError::new(
|
||||
"No more space in the network",
|
||||
StatusCode::SERVICE_UNAVAILABLE,
|
||||
))?;
|
||||
// mark it as used, even though it's not final
|
||||
*private_ip_ref = false;
|
||||
let gateway_data = GatewayClient::new(
|
||||
state.dh_keypair.private_key(),
|
||||
remote_public,
|
||||
*private_ip_ref.key(),
|
||||
nonce,
|
||||
);
|
||||
let response = ClientRegistrationResponse::PendingRegistration {
|
||||
nonce,
|
||||
gateway_data,
|
||||
|
||||
@@ -7,8 +7,10 @@ use crate::http::api::v1::gateway::client_interfaces::wireguard::client_registry
|
||||
use crate::wireguard::types::{GatewayClientRegistry, PendingRegistrations};
|
||||
use axum::routing::{get, post};
|
||||
use axum::Router;
|
||||
use ipnetwork::IpNetwork;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
|
||||
use nym_wireguard_types::registration::PrivateIPs;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) mod client_registry;
|
||||
@@ -26,6 +28,7 @@ impl WireguardAppState {
|
||||
client_registry: Arc<GatewayClientRegistry>,
|
||||
registration_in_progress: Arc<PendingRegistrations>,
|
||||
binding_port: u16,
|
||||
private_ip_network: IpNetwork,
|
||||
) -> Self {
|
||||
WireguardAppState {
|
||||
inner: Some(WireguardAppStateInner {
|
||||
@@ -33,6 +36,9 @@ impl WireguardAppState {
|
||||
client_registry,
|
||||
registration_in_progress,
|
||||
binding_port,
|
||||
free_private_network_ips: Arc::new(
|
||||
private_ip_network.iter().map(|ip| (ip, true)).collect(),
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -77,6 +83,7 @@ pub(crate) struct WireguardAppStateInner {
|
||||
client_registry: Arc<GatewayClientRegistry>,
|
||||
registration_in_progress: Arc<PendingRegistrations>,
|
||||
binding_port: u16,
|
||||
free_private_network_ips: Arc<PrivateIPs>,
|
||||
}
|
||||
|
||||
pub(crate) fn routes<S>(initial_state: WireguardAppState) -> Router<S> {
|
||||
@@ -98,6 +105,7 @@ mod test {
|
||||
use axum::http::StatusCode;
|
||||
use dashmap::DashMap;
|
||||
use hmac::Mac;
|
||||
use ipnetwork::IpNetwork;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::{
|
||||
ClientMac, ClientMessage, ClientRegistrationResponse, GatewayClient, InitMessage,
|
||||
@@ -105,6 +113,8 @@ mod test {
|
||||
};
|
||||
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
|
||||
use nym_wireguard_types::registration::HmacSha256;
|
||||
use std::net::IpAddr;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tower::Service;
|
||||
use tower::ServiceExt;
|
||||
@@ -136,6 +146,14 @@ mod test {
|
||||
|
||||
let registration_in_progress = Arc::new(DashMap::new());
|
||||
let client_registry = Arc::new(DashMap::new());
|
||||
let free_private_network_ips = Arc::new(
|
||||
IpNetwork::from_str("10.0.0.0/24")
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|ip| (ip, true))
|
||||
.collect(),
|
||||
);
|
||||
let client_private_ip = IpAddr::from_str("10.0.0.42").unwrap();
|
||||
|
||||
let state = WireguardAppState {
|
||||
inner: Some(WireguardAppStateInner {
|
||||
@@ -143,6 +161,7 @@ mod test {
|
||||
dh_keypair: Arc::new(gateway_key_pair),
|
||||
registration_in_progress: Arc::clone(®istration_in_progress),
|
||||
binding_port: 8080,
|
||||
free_private_network_ips,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -186,11 +205,13 @@ mod test {
|
||||
|
||||
let mut mac = HmacSha256::new_from_slice(client_dh.as_bytes()).unwrap();
|
||||
mac.update(client_static_public.as_bytes());
|
||||
mac.update(client_private_ip.to_string().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),
|
||||
private_ip: client_private_ip,
|
||||
mac: ClientMac::new(mac.as_slice().to_vec()),
|
||||
});
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
"@mui/icons-material": "^5.14.9",
|
||||
"@mui/lab": "^5.0.0-alpha.145",
|
||||
"@mui/material": "^5.14.8",
|
||||
"@nymproject/contract-clients": "^1.2.2",
|
||||
"@nymproject/mix-fetch": "^1.2.2",
|
||||
"@nymproject/mix-fetch-full-fat": "^1.2.2",
|
||||
"@nymproject/sdk-full-fat": "^1.2.2",
|
||||
"@nymproject/contract-clients": ">=1.2.1-rc.0 || ^1",
|
||||
"@nymproject/mix-fetch": ">=1.2.1-rc.0 || ^1",
|
||||
"@nymproject/mix-fetch-full-fat": ">=1.2.1-rc.0 || ^1",
|
||||
"@nymproject/sdk-full-fat": ">=1.2.1-rc.0 || ^1",
|
||||
"chain-registry": "^1.19.0",
|
||||
"cosmjs-types": "^0.8.0",
|
||||
"next": "^13.4.19",
|
||||
@@ -51,4 +51,4 @@
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user