From 3fc86e2da0cae651277479482499a62be8f06b8e Mon Sep 17 00:00:00 2001 From: Aaron Kaiser Date: Wed, 8 May 2024 17:03:39 +0200 Subject: [PATCH] Integrate agent --- .envrc | 1 + Cargo.lock | 25 +++++++++ Cargo.toml | 1 + default.nix | 4 ++ keyfile | Bin 0 -> 3200 bytes src/configuration/config.rs | 14 ++--- src/configuration/uapi/get.rs | 2 +- src/wireguard/handshake/device.rs | 82 +++++++++--------------------- src/wireguard/handshake/macs.rs | 7 +-- src/wireguard/handshake/noise.rs | 20 +++++--- src/wireguard/handshake/peer.rs | 3 +- src/wireguard/wireguard.rs | 15 +++--- test.conf | 9 ++++ test2.conf | 11 ++++ 14 files changed, 112 insertions(+), 82 deletions(-) create mode 100644 .envrc create mode 100644 keyfile create mode 100644 test.conf create mode 100644 test2.conf diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/Cargo.lock b/Cargo.lock index f72e71e..11758a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "agent_lib" +version = "0.1.0" +source = "git+https://gitea.rixxc.de/rixxc/agent_lib.git#2ed3949ee12bebca2d19ec788688cc59e5378ded" +dependencies = [ + "anyhow", + "libc", + "shared_memory_heap", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -35,6 +45,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" + [[package]] name = "arraydeque" version = "0.4.5" @@ -975,6 +991,14 @@ dependencies = [ "syn 2.0.63", ] +[[package]] +name = "shared_memory_heap" +version = "0.1.0" +source = "git+https://gitea.rixxc.de/rixxc/shared_memory_heap.git#18f1a1b9a6f2d29f215cb238e470ca91f9e03bc3" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.9" @@ -1368,6 +1392,7 @@ name = "wireguard-rs" version = "0.1.4" dependencies = [ "aead", + "agent_lib", "arraydeque", "blake2", "byteorder", diff --git a/Cargo.toml b/Cargo.toml index 640d8cb..bf6d300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ rand_core = "^0.5" ring = "0.16" spin = "0.7" zerocopy = "0.3" +agent_lib = { git = "https://gitea.rixxc.de/rixxc/agent_lib.git" } [dependencies.treebitmap] package = "ip_network_table-deps-treebitmap" diff --git a/default.nix b/default.nix index 992e52c..37da577 100644 --- a/default.nix +++ b/default.nix @@ -25,6 +25,10 @@ rustPlatform.buildRustPackage cargoLock = { lockFile = ./Cargo.lock; + outputHashes = { + "agent_lib-0.1.0" = "sha256-G9PCNDLaJ18pXjoKmdSFOYFT81VJ9GxapOi7EFZMTks="; + "shared_memory_heap-0.1.0" = "sha256-AH+wfAMw5z3lND44feQ11VzoVdaqSGnkvTeIQOFlfXQ="; + }; }; doCheck = false; diff --git a/keyfile b/keyfile new file mode 100644 index 0000000000000000000000000000000000000000..217866404653c7e0a270b919dda577493182b57f GIT binary patch literal 3200 zcmX@{Gp%RpR-M9|$0Uu8{8CSuWIIE!qpPA=#D0(Zy-X z8&wDXsx5oomtOu?{pxabPcp+O7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu;sB0~TG DWk?`y literal 0 HcmV?d00001 diff --git a/src/configuration/config.rs b/src/configuration/config.rs index 636becc..f8309d2 100644 --- a/src/configuration/config.rs +++ b/src/configuration/config.rs @@ -4,6 +4,7 @@ use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex, MutexGuard}; use std::time::{Duration, SystemTime}; +use agent_lib::X25519PrivKey; use x25519_dalek::{PublicKey, StaticSecret}; use super::udp::Owner; @@ -78,7 +79,7 @@ pub trait Configuration { /// # Returns /// /// The private if set, otherwise None. - fn get_private_key(&self) -> Option; + fn get_private_key(&self) -> Option; /// Returns the protocol version of the device /// @@ -240,13 +241,14 @@ impl Configuration for WireGuardConfig { self.lock().fwmark } - fn set_private_key(&self, sk: Option) { - log::info!("configuration, set private key"); - self.lock().wireguard.set_key(sk) + fn set_private_key(&self, _sk: Option) { + // log::info!("configuration, set private key"); + // self.lock().wireguard.set_key(sk) } - fn get_private_key(&self) -> Option { - self.lock().wireguard.get_sk() + fn get_private_key(&self) -> Option { + // self.lock().wireguard.get_sk() + Some(X25519PrivKey::from(&[0; 8])) } fn get_protocol_version(&self) -> usize { diff --git a/src/configuration/uapi/get.rs b/src/configuration/uapi/get.rs index 8ca9d64..4b36cb2 100644 --- a/src/configuration/uapi/get.rs +++ b/src/configuration/uapi/get.rs @@ -16,7 +16,7 @@ pub fn serialize(writer: &mut W, config: &C) -> // serialize interface config .get_private_key() - .map(|sk| write("private_key", hex::encode(sk.to_bytes()))); + .map(|_sk| write("private_key", hex::encode([0; 32]))); config .get_listen_port() diff --git a/src/wireguard/handshake/device.rs b/src/wireguard/handshake/device.rs index 47ca401..78abe56 100644 --- a/src/wireguard/handshake/device.rs +++ b/src/wireguard/handshake/device.rs @@ -3,6 +3,9 @@ use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Mutex; +use agent_lib::x22519_pubkey; +use agent_lib::X25519PrivKey; +use agent_lib::X25519PubKey; use byteorder::{ByteOrder, LittleEndian}; use dashmap::mapref::entry::Entry; use dashmap::DashMap; @@ -14,7 +17,6 @@ use rand_core::{CryptoRng, RngCore}; use clear_on_drop::clear::Clear; use x25519_dalek::PublicKey; -use x25519_dalek::StaticSecret; use super::macs; use super::messages::{CookieReply, Initiation, Response}; @@ -27,9 +29,9 @@ use super::types::*; const MAX_PEER_PER_DEVICE: usize = 1 << 20; pub struct KeyState { - pub(super) sk: StaticSecret, // static secret key - pub(super) pk: PublicKey, // static public key - macs: macs::Validator, // validator for the mac fields + pub(super) sk: X25519PrivKey, // static secret key + pub(super) pk: X25519PubKey, // static public key + macs: macs::Validator, // validator for the mac fields } /// The device is generic over an "opaque" type @@ -95,25 +97,31 @@ impl Device { impl Device { /// Initialize a new handshake state machine pub fn new() -> Device { + let sk = X25519PrivKey::from(&[0; 8]); + let pk = x22519_pubkey(&sk); + let macs = { + let pk = PublicKey::from(*pk); + macs::Validator::new(pk) + }; Device { - keyst: None, + keyst: Some(KeyState { sk, pk, macs }), id_map: DashMap::new(), pk_map: HashMap::new(), limiter: Mutex::new(RateLimiter::new()), } } - fn update_ss(&mut self) -> (Vec, Option) { + fn update_ss(&mut self) -> (Vec, Option) { let mut same = None; let mut ids = Vec::with_capacity(self.pk_map.len()); for (pk, peer) in self.pk_map.iter_mut() { if let Some(key) = self.keyst.as_ref() { if key.pk.as_bytes() == pk { - same = Some(PublicKey::from(*pk)); + same = Some(X25519PubKey::from(pk)); peer.ss.clear() } else { - let pk = PublicKey::from(*pk); - peer.ss = *key.sk.diffie_hellman(&pk).as_bytes(); + let pk = X25519PubKey::from(pk); + peer.ss = *noise::shared_secret_agent(&key.sk, &pk); } } else { peer.ss.clear(); @@ -126,44 +134,6 @@ impl Device { (ids, same) } - /// Update the secret key of the device - /// - /// # Arguments - /// - /// * `sk` - x25519 scalar representing the local private key - pub fn set_sk(&mut self, sk: Option) -> Option { - // update secret and public key - self.keyst = sk.map(|sk| { - let pk = PublicKey::from(&sk); - let macs = macs::Validator::new(pk); - KeyState { pk, sk, macs } - }); - - // recalculate / erase the shared secrets for every peer - let (ids, same) = self.update_ss(); - - // release ids from aborted handshakes - for id in ids { - self.release(id) - } - - // if we found a peer matching the device public key - // remove it and return its value to the caller - same.map(|pk| { - self.pk_map.remove(pk.as_bytes()); - pk - }) - } - - /// Return the secret key of the device - /// - /// # Returns - /// - /// A secret key (x25519 scalar) - pub fn get_sk(&self) -> Option<&StaticSecret> { - self.keyst.as_ref().map(|key| &key.sk) - } - /// Add a new public key to the state machine /// To remove public keys, you must create a new machine instance /// @@ -172,6 +142,7 @@ impl Device { /// * `pk` - The public key to add /// * `identifier` - Associated identifier which can be used to distinguish the peers pub fn add(&mut self, pk: PublicKey, opaque: O) -> Result<(), ConfigError> { + let pk = X25519PubKey::from(pk.as_bytes()); // ensure less than 2^20 peers if self.pk_map.len() > MAX_PEER_PER_DEVICE { return Err(ConfigError::new("Too many peers for device")); @@ -185,17 +156,12 @@ impl Device { } // pre-compute shared secret and add to pk_map - self.pk_map.insert( - *pk.as_bytes(), - Peer::new( - pk, - self.keyst - .as_ref() - .map(|key| *key.sk.diffie_hellman(&pk).as_bytes()) - .unwrap_or([0u8; 32]), - opaque, - ), - ); + let ss = self + .keyst + .as_ref() + .map(|key| *noise::shared_secret_agent(&key.sk, &pk)) + .unwrap_or([0u8; 32]); + self.pk_map.insert(*pk, Peer::new(pk, ss, opaque)); Ok(()) } diff --git a/src/wireguard/handshake/macs.rs b/src/wireguard/handshake/macs.rs index f4f5586..7a98c8c 100644 --- a/src/wireguard/handshake/macs.rs +++ b/src/wireguard/handshake/macs.rs @@ -1,3 +1,4 @@ +use agent_lib::X25519PubKey; use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; use spin::RwLock; @@ -119,10 +120,10 @@ impl Generator { /// # Returns /// /// A freshly initated generator - pub fn new(pk: PublicKey) -> Generator { + pub fn new(pk: X25519PubKey) -> Generator { Generator { - mac1_key: HASH!(LABEL_MAC1, pk.as_bytes()).into(), - cookie_key: HASH!(LABEL_COOKIE, pk.as_bytes()).into(), + mac1_key: HASH!(LABEL_MAC1, *pk).into(), + cookie_key: HASH!(LABEL_COOKIE, *pk).into(), last_mac1: None, cookie: None, } diff --git a/src/wireguard/handshake/noise.rs b/src/wireguard/handshake/noise.rs index 92c8c5f..de85dc6 100644 --- a/src/wireguard/handshake/noise.rs +++ b/src/wireguard/handshake/noise.rs @@ -1,5 +1,6 @@ use std::time::Instant; +use agent_lib::{x25519, X25519PrivKey, X25519PubKey, X25519SharedKey}; // DH use x25519_dalek::{PublicKey, SharedSecret, StaticSecret}; @@ -36,7 +37,7 @@ type HMACBlake2s = Hmac; // convenient alias to pass state temporarily into device.rs and back -type TemporaryState = (u32, PublicKey, GenericArray, GenericArray); +type TemporaryState = (u32, X25519PubKey, GenericArray, GenericArray); const SIZE_CK: usize = 32; const SIZE_HS: usize = 32; @@ -227,6 +228,11 @@ fn shared_secret(sk: &StaticSecret, pk: &PublicKey) -> Result X25519SharedKey { + x25519(sk, pk) +} + pub(super) fn create_initiation( rng: &mut R, keyst: &KeyState, @@ -278,7 +284,7 @@ pub(super) fn create_initiation( SEAL!( &key, &hs, // ad - keyst.pk.as_bytes(), // pt + keyst.pk.as_slice(), // pt &mut msg.f_static // ct || tag ); @@ -328,7 +334,7 @@ pub(super) fn consume_initiation<'a, O>( let ck = INITIAL_CK; let hs = INITIAL_HS; - let hs = HASH!(&hs, keyst.pk.as_bytes()); + let hs = HASH!(&hs, *keyst.pk); // C := Kdf(C, E_pub) @@ -340,8 +346,8 @@ pub(super) fn consume_initiation<'a, O>( // (C, k) := Kdf2(C, DH(E_priv, S_pub)) - let eph_r_pk = PublicKey::from(msg.f_ephemeral); - let (ck, key) = KDF2!(&ck, shared_secret(&keyst.sk, &eph_r_pk)?.as_bytes()); + let eph_r_pk = X25519PubKey::from(&msg.f_ephemeral); + let (ck, key) = KDF2!(&ck, shared_secret_agent(&keyst.sk, &eph_r_pk).as_slice()); // msg.static := Aead(k, 0, S_pub, H) @@ -440,6 +446,7 @@ pub(super) fn create_response( // C := Kdf1(C, DH(E_priv, E_pub)) + let eph_r_pk = PublicKey::from(*eph_r_pk); let ck = KDF1!(&ck, shared_secret(&eph_sk, &eph_r_pk)?.as_bytes()); // C := Kdf1(C, DH(E_priv, S_pub)) @@ -526,7 +533,8 @@ pub(super) fn consume_response<'a, O>( // C := Kdf1(C, DH(E_priv, S_pub)) - let ck = KDF1!(&ck, shared_secret(&keyst.sk, &eph_r_pk)?.as_bytes()); + let eph_r_pk = X25519PubKey::from(&msg.f_ephemeral); + let ck = KDF1!(&ck, shared_secret_agent(&keyst.sk, &eph_r_pk).as_slice()); // (C, tau, k) := Kdf3(C, Q) diff --git a/src/wireguard/handshake/peer.rs b/src/wireguard/handshake/peer.rs index 7929256..985eb9f 100644 --- a/src/wireguard/handshake/peer.rs +++ b/src/wireguard/handshake/peer.rs @@ -1,3 +1,4 @@ +use agent_lib::X25519PubKey; use spin::Mutex; use std::mem; @@ -59,7 +60,7 @@ impl Drop for State { } impl Peer { - pub fn new(pk: PublicKey, ss: [u8; 32], opaque: O) -> Self { + pub fn new(pk: X25519PubKey, ss: [u8; 32], opaque: O) -> Self { Self { opaque, macs: Mutex::new(macs::Generator::new(pk)), diff --git a/src/wireguard/wireguard.rs b/src/wireguard/wireguard.rs index 44d698f..51a674a 100644 --- a/src/wireguard/wireguard.rs +++ b/src/wireguard/wireguard.rs @@ -183,16 +183,17 @@ impl WireGuard { } pub fn set_key(&self, sk: Option) { - let mut peers = self.peers.write(); - peers.set_sk(sk); - self.router.clear_sending_keys(); + // let mut peers = self.peers.write(); + // peers.set_sk(sk); + // self.router.clear_sending_keys(); } pub fn get_sk(&self) -> Option { - self.peers - .read() - .get_sk() - .map(|sk| StaticSecret::from(sk.to_bytes())) + // self.peers + // .read() + // .get_sk() + // .map(|sk| StaticSecret::from(sk.to_bytes())) + None } pub fn set_psk(&self, pk: PublicKey, psk: [u8; 32]) -> bool { diff --git a/test.conf b/test.conf new file mode 100644 index 0000000..8ff6fe6 --- /dev/null +++ b/test.conf @@ -0,0 +1,9 @@ +# Agent implementation + +[Interface] +ListenPort = 31337 + +[Peer] +PublicKey = hu/U5Dg+tt9z0vfJZgSjPlVXBoi0Ux4ELFhfLnzBWRc= +AllowedIPs = 100.64.100.2/32 +Endpoint = 167.235.26.58:31338 diff --git a/test2.conf b/test2.conf new file mode 100644 index 0000000..2e2707f --- /dev/null +++ b/test2.conf @@ -0,0 +1,11 @@ +# Normal implementation + +[Interface] +ListenPort = 31338 +PrivateKey = cAOHZMXEq20N8/keW4lYrkhej3oCFeMV3uJ/3f22eGQ= +Address = 100.64.100.2/24 + +[Peer] +PublicKey = 9z0xORKt5DZJeqc55/6YFljRz3Kv90yPFsIPgHjIkTs= +AllowedIPs = 100.64.100.1/32 +Endpoint = 127.0.0.1:31337