diff --git a/Cargo.lock b/Cargo.lock index e0f32dda3d..9f22cb4b4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -542,6 +542,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "boringtun" +version = "0.5.2" +source = "git+https://github.com/durch/boringtun.git#a65d78339157dcedca1dd97de23a4b111f4da0b1" +dependencies = [ + "aead", + "base64 0.13.1", + "blake2 0.10.6", + "chacha20poly1305", + "hex", + "hmac 0.12.1", + "ip_network", + "ip_network_table", + "libc", + "nix", + "parking_lot 0.12.1", + "rand_core 0.6.4", + "ring", + "tracing", + "untrusted 0.9.0", + "x25519-dalek 2.0.0-rc.3", +] + [[package]] name = "bs58" version = "0.4.0" @@ -1308,6 +1331,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "platforms", + "rustc_version 0.4.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.16", +] + [[package]] name = "cw-controllers" version = "0.13.4" @@ -1603,7 +1653,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "ed25519", "rand 0.7.3", "serde", @@ -1618,7 +1668,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "hashbrown 0.12.3", "hex", "rand_core 0.6.4", @@ -1832,6 +1882,12 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + [[package]] name = "figment" version = "0.10.8" @@ -2763,6 +2819,28 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ip_network" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" + +[[package]] +name = "ip_network_table" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4099b7cfc5c5e2fe8c5edf3f6f7adf7a714c9cc697534f63a5a5da30397cb2c0" +dependencies = [ + "ip_network", + "ip_network_table-deps-treebitmap", +] + +[[package]] +name = "ip_network_table-deps-treebitmap" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e537132deb99c0eb4b752f0346b6a836200eaaa3516dd7e5514b63930a09e5d" + [[package]] name = "ipnet" version = "2.7.2" @@ -3226,6 +3304,18 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg 1.1.0", + "bitflags", + "cfg-if", + "libc", +] + [[package]] name = "no-std-compat" version = "0.4.1" @@ -3762,7 +3852,7 @@ dependencies = [ "serde_bytes", "subtle-encoding", "thiserror", - "x25519-dalek", + "x25519-dalek 1.1.1", "zeroize", ] @@ -3804,7 +3894,9 @@ dependencies = [ "anyhow", "async-trait", "atty", + "base64 0.21.2", "bip39", + "boringtun", "bs58", "clap 4.2.7", "colored", @@ -3844,6 +3936,7 @@ dependencies = [ "tokio-tungstenite 0.14.0", "tokio-util", "url", + "x25519-dalek 2.0.0-rc.3", "zeroize", ] @@ -4184,7 +4277,7 @@ dependencies = [ "chacha20", "chacha20poly1305", "criterion", - "curve25519-dalek", + "curve25519-dalek 3.2.0", "fastrand", "getrandom 0.2.10", "log", @@ -5164,6 +5257,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + [[package]] name = "plotters" version = "0.3.4" @@ -5795,7 +5894,7 @@ dependencies = [ "libc", "once_cell", "spin 0.5.2", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi", ] @@ -6119,7 +6218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ "ring", - "untrusted", + "untrusted 0.7.1", ] [[package]] @@ -6129,7 +6228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ "ring", - "untrusted", + "untrusted 0.7.1", ] [[package]] @@ -6500,7 +6599,7 @@ dependencies = [ "bs58", "byteorder", "chacha", - "curve25519-dalek", + "curve25519-dalek 3.2.0", "digest 0.9.0", "hkdf 0.11.0", "hmac 0.11.0", @@ -7691,6 +7790,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "unwind_safe" version = "0.1.0" @@ -7937,7 +8042,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ "ring", - "untrusted", + "untrusted 0.7.1", ] [[package]] @@ -7947,7 +8052,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" dependencies = [ "ring", - "untrusted", + "untrusted 0.7.1", ] [[package]] @@ -8208,12 +8313,24 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "rand_core 0.5.1", "serde", "zeroize", ] +[[package]] +name = "x25519-dalek" +version = "2.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a" +dependencies = [ + "curve25519-dalek 4.0.0-rc.3", + "rand_core 0.6.4", + "serde", + "zeroize", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index 76f78fc189..4f425ad990 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -34,16 +34,35 @@ pretty_env_logger = "0.4" rand = "0.7" serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "sqlite", "macros", "migrate", ] } +sqlx = { version = "0.5", features = [ + "runtime-tokio-rustls", + "sqlite", + "macros", + "migrate", +] } subtle-encoding = { version = "0.5", features = ["bech32-preview"] } thiserror = "1" -tokio = { version = "1.24.1", features = [ "rt-multi-thread", "net", "signal", "fs", ] } +tokio = { version = "1.24.1", features = [ + "rt-multi-thread", + "net", + "signal", + "fs", +] } tokio-stream = { version = "0.1.11", features = ["fs"] } tokio-tungstenite = "0.14" tokio-util = { version = "0.7.4", features = ["codec"] } url = { version = "2.2", features = ["serde"] } zeroize = { workspace = true } +# wireguard +# Forked it to be able to bump x25519-dalek to rc.3 +boringtun = { git = "https://github.com/durch/boringtun.git" } +base64 = "0.21" +x25519-dalek = { version = "=2.0.0-rc.3", features = [ + "reusable_secrets", + "static_secrets", +] } + # internal nym-api-requests = { path = "../nym-api/nym-api-requests" } nym-bin-common = { path = "../common/bin-common", features = ["output_format"] } @@ -60,7 +79,9 @@ nym-sphinx = { path = "../common/nymsphinx" } nym-statistics-common = { path = "../common/statistics" } nym-task = { path = "../common/task" } nym-types = { path = "../common/types" } -nym-validator-client = { path = "../common/client-libs/validator-client", features = [ "nyxd-client" ] } +nym-validator-client = { path = "../common/client-libs/validator-client", features = [ + "nyxd-client", +] } [build-dependencies] tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] } diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index 8487e6c99f..a59a1398ca 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -10,6 +10,7 @@ use crate::node::client_handling::websocket::connection_handler::coconut::Coconu use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler; use crate::node::statistics::collector::GatewayStatisticsCollector; use crate::node::storage::Storage; +use crate::node::wireguard::wireguard; use log::*; use nym_bin_common::output_format::OutputFormat; use nym_crypto::asymmetric::{encryption, identity}; @@ -28,6 +29,7 @@ pub(crate) mod client_handling; pub(crate) mod mixnet_handling; pub(crate) mod statistics; pub(crate) mod storage; +pub (crate) mod wireguard; /// Wire up and create Gateway instance pub(crate) async fn create_gateway(config: Config) -> Gateway { @@ -297,6 +299,8 @@ impl Gateway { Arc::new(coconut_verifier), ); + tokio::spawn(wireguard()); + info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!"); self.wait_for_interrupt(shutdown).await diff --git a/gateway/src/node/wireguard.rs b/gateway/src/node/wireguard.rs new file mode 100644 index 0000000000..31f8cc9387 --- /dev/null +++ b/gateway/src/node/wireguard.rs @@ -0,0 +1,100 @@ +use std::sync::Arc; + +use base64::engine::general_purpose; +use base64::Engine as _; +use boringtun::noise::{rate_limiter::RateLimiter, Tunn, TunnResult}; +use log::{debug, error, info}; +use tokio::net::UdpSocket; +use x25519_dalek::{PublicKey, StaticSecret}; + +pub async fn wireguard() { + let wg_address = "127.0.0.1:51820"; + let sock = UdpSocket::bind(wg_address).await.unwrap(); + info!("wg listening on {wg_address}"); + + // Secret key ofthe gateway, we'll need a way to generate this from the IdentityKey, might be enough to do some base58 -> base64 conversion + let secret_bytes: [u8; 32] = general_purpose::STANDARD + .decode("MBbPChSpmC/FXwIWNROltjd6cOywC81GNEgH9jMOOFk=") + .unwrap() + .try_into() + .unwrap(); + + // Hardcoded peer public key, we'll need a way to register those, private key for that one is `aMUcuAgTiFCHQ/fHqEQRvpLWBxh8sKA7f7lSyWymrGE=` + // Wireguard configuration that works with this setup is below, this needs to be put into the wireguard client of choice. + // Working in this case means that they go through the handshake, and client + // starts sending data packets to the gateway. + // + // [Interface] + // PrivateKey = aMUcuAgTiFCHQ/fHqEQRvpLWBxh8sKA7f7lSyWymrGE= + // Address = 10.8.0.0/24 + // DNS = 1.1.1.1 + // + // [Peer] + // PublicKey = y6/iGYraJjON6pw9fcBa5vLRbGsQqprFLfWKyJQnlWs= + // AllowedIPs = 0.0.0.0/0 + // Endpoint = 127.0.0.1:51820 + let peer_public_bytes: [u8; 32] = general_purpose::STANDARD + .decode("JpJzoO1DY6HZbn2h33GQJg0GLnxfdpOeV9C/rvdZ5Cs=") + .unwrap() + .try_into() + .unwrap(); + let peer_public = PublicKey::from(peer_public_bytes); + let secret = StaticSecret::try_from(secret_bytes).unwrap(); + let public = PublicKey::from(&secret); + info!( + "wg public key: {}", + general_purpose::STANDARD.encode(public) + ); + // Rate limiter is global for the gateway + let rate_limiter = Arc::new(RateLimiter::new(&public, 1024)); + + let mut tun = Tunn::new(secret, peer_public, None, None, 0, Some(rate_limiter)).unwrap(); + // Here we have a pretty suboptimal implementation of the UDP communication, for one client + loop { + let mut buf = [0; 1024]; + let mut dst = vec![0; 1024]; + let (len, addr) = sock.recv_from(&mut buf).await.unwrap(); + let packet = Tunn::parse_incoming_packet(&buf[..len]).unwrap(); + debug!("packet: {:?}", packet); + let dst_addr = Tunn::dst_address(&buf[..len]); + debug!("dst_addr: {:?}", dst_addr); + let result = tun.decapsulate(dst_addr, &buf[..len], &mut dst); + loop { + debug!("result: {:?}", result); + match result { + TunnResult::Done => break, + // We'll get here during the handshake process, if the reponse is WriteToNetwork we should call decapsulate again with an + // empty datagram until we get a Done response + TunnResult::WriteToNetwork(p) => { + let len = sock.send_to(p, addr).await.unwrap(); + debug!("{} bytes sent to {}", len, addr); + tun.decapsulate(dst_addr, &[], p); + break; + } + TunnResult::Err(e) => { + error!("error: {:?}", e); + break; + } + // We've recieved some DataPackets we need to forward and send response back to the initiating client + // if no data packets are available we should send an empty packet as an ack. + // For now this just logs that it received the packet, and send and ack back to the client. + TunnResult::WriteToTunnelV4(ref _r, _addy) => { + // These are very spammy + debug!("WriteToTunnelV4"); + sock.send_to(&empty_packet(&mut tun), addr).await.unwrap(); + } + TunnResult::WriteToTunnelV6(ref _r, _addy) => { + // These are very spammy + debug!("WriteToTunnelV6"); + sock.send_to(&empty_packet(&mut tun), addr).await.unwrap(); + } + } + } + } +} + +fn empty_packet(tun: &mut Tunn) -> [u8; 128] { + let mut dst = [0; 128]; + tun.encapsulate(&[], &mut dst); + dst +}