Compare commits

4 Commits

Author SHA1 Message Date
34dcb870de remove build dependency 2024-10-15 13:45:33 +02:00
9073513e27 Update shared_memory_heap 2024-05-15 13:17:41 +02:00
3fed591fd6 update shared_memory_heap 2024-05-15 09:08:43 +02:00
3fc86e2da0 Integrate agent 2024-05-15 09:08:43 +02:00
15 changed files with 121 additions and 96 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use nix

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@
proptest-regressions/ proptest-regressions/
.idea/ .idea/
result result
.direnv/

33
Cargo.lock generated
View File

@@ -26,6 +26,16 @@ dependencies = [
"generic-array", "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]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.3" version = "1.1.3"
@@ -35,6 +45,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "anyhow"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
[[package]] [[package]]
name = "arraydeque" name = "arraydeque"
version = "0.4.5" version = "0.4.5"
@@ -957,24 +973,32 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.201" version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.201" version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.63", "syn 2.0.63",
] ]
[[package]]
name = "shared_memory_heap"
version = "0.1.0"
source = "git+https://gitea.rixxc.de/rixxc/shared_memory_heap.git#9ec0b728c2936c57ef8b0e6b015cdcc0474060d4"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@@ -1368,6 +1392,7 @@ name = "wireguard-rs"
version = "0.1.4" version = "0.1.4"
dependencies = [ dependencies = [
"aead", "aead",
"agent_lib",
"arraydeque", "arraydeque",
"blake2", "blake2",
"byteorder", "byteorder",

View File

@@ -29,6 +29,7 @@ rand_core = "^0.5"
ring = "0.16" ring = "0.16"
spin = "0.7" spin = "0.7"
zerocopy = "0.3" zerocopy = "0.3"
agent_lib = { git = "https://gitea.rixxc.de/rixxc/agent_lib.git" }
[dependencies.treebitmap] [dependencies.treebitmap]
package = "ip_network_table-deps-treebitmap" package = "ip_network_table-deps-treebitmap"

View File

@@ -1,9 +1,4 @@
{ pkgs ? import <nixpkgs> { { pkgs ? import <nixpkgs> { } }:
overlays = [
(import (fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz"))
];
}
}:
with pkgs; with pkgs;
let let
agent = callPackage "${fetchgit { agent = callPackage "${fetchgit {
@@ -18,17 +13,20 @@ rustPlatform.buildRustPackage
name = "wireguard-agent"; name = "wireguard-agent";
src = nix-gitignore.gitignoreSource [ ] ./.; src = nix-gitignore.gitignoreSource [ ] ./.;
nativeBuildInputs = [ nativeBuildInputs = [ agent ];
(rust-bin.fromRustupToolchainFile ./rust-toolchain.toml)
agent
];
cargoLock = { cargoLock = {
lockFile = ./Cargo.lock; lockFile = ./Cargo.lock;
outputHashes = {
"agent_lib-0.1.0" = "sha256-G9PCNDLaJ18pXjoKmdSFOYFT81VJ9GxapOi7EFZMTks=";
"shared_memory_heap-0.1.0" = "sha256-pWwmMGofgXrF7U4Tj/S6pRWXrcy6OPj9zyMii+vrWNo=";
};
}; };
doCheck = false; doCheck = false;
RUSTC_BOOTSTRAP = true;
AGENT_PATH = "${agent}/bin/agent_harness"; AGENT_PATH = "${agent}/bin/agent_harness";
KEY_FILE = "keyfile"; KEY_FILE = "keyfile";
RUST_LOG = "debug"; RUST_LOG = "debug";

BIN
keyfile Normal file

Binary file not shown.

View File

@@ -4,6 +4,7 @@ use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use agent_lib::X25519PrivKey;
use x25519_dalek::{PublicKey, StaticSecret}; use x25519_dalek::{PublicKey, StaticSecret};
use super::udp::Owner; use super::udp::Owner;
@@ -78,7 +79,7 @@ pub trait Configuration {
/// # Returns /// # Returns
/// ///
/// The private if set, otherwise None. /// The private if set, otherwise None.
fn get_private_key(&self) -> Option<StaticSecret>; fn get_private_key(&self) -> Option<X25519PrivKey>;
/// Returns the protocol version of the device /// Returns the protocol version of the device
/// ///
@@ -240,13 +241,14 @@ impl<T: tun::Tun, B: udp::PlatformUDP> Configuration for WireGuardConfig<T, B> {
self.lock().fwmark self.lock().fwmark
} }
fn set_private_key(&self, sk: Option<StaticSecret>) { fn set_private_key(&self, _sk: Option<StaticSecret>) {
log::info!("configuration, set private key"); // log::info!("configuration, set private key");
self.lock().wireguard.set_key(sk) // self.lock().wireguard.set_key(sk)
} }
fn get_private_key(&self) -> Option<StaticSecret> { fn get_private_key(&self) -> Option<X25519PrivKey> {
self.lock().wireguard.get_sk() // self.lock().wireguard.get_sk()
Some(X25519PrivKey::from(&[0; 8]))
} }
fn get_protocol_version(&self) -> usize { fn get_protocol_version(&self) -> usize {

View File

@@ -16,7 +16,7 @@ pub fn serialize<C: Configuration, W: io::Write>(writer: &mut W, config: &C) ->
// serialize interface // serialize interface
config config
.get_private_key() .get_private_key()
.map(|sk| write("private_key", hex::encode(sk.to_bytes()))); .map(|_sk| write("private_key", hex::encode([0; 32])));
config config
.get_listen_port() .get_listen_port()

View File

@@ -3,6 +3,9 @@ use std::collections::HashMap;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Mutex; use std::sync::Mutex;
use agent_lib::x22519_pubkey;
use agent_lib::X25519PrivKey;
use agent_lib::X25519PubKey;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use dashmap::mapref::entry::Entry; use dashmap::mapref::entry::Entry;
use dashmap::DashMap; use dashmap::DashMap;
@@ -14,7 +17,6 @@ use rand_core::{CryptoRng, RngCore};
use clear_on_drop::clear::Clear; use clear_on_drop::clear::Clear;
use x25519_dalek::PublicKey; use x25519_dalek::PublicKey;
use x25519_dalek::StaticSecret;
use super::macs; use super::macs;
use super::messages::{CookieReply, Initiation, Response}; use super::messages::{CookieReply, Initiation, Response};
@@ -27,9 +29,9 @@ use super::types::*;
const MAX_PEER_PER_DEVICE: usize = 1 << 20; const MAX_PEER_PER_DEVICE: usize = 1 << 20;
pub struct KeyState { pub struct KeyState {
pub(super) sk: StaticSecret, // static secret key pub(super) sk: X25519PrivKey, // static secret key
pub(super) pk: PublicKey, // static public key pub(super) pk: X25519PubKey, // static public key
macs: macs::Validator, // validator for the mac fields macs: macs::Validator, // validator for the mac fields
} }
/// The device is generic over an "opaque" type /// The device is generic over an "opaque" type
@@ -95,25 +97,31 @@ impl<O> Device<O> {
impl<O> Device<O> { impl<O> Device<O> {
/// Initialize a new handshake state machine /// Initialize a new handshake state machine
pub fn new() -> Device<O> { pub fn new() -> Device<O> {
let sk = X25519PrivKey::from(&[0; 8]);
let pk = x22519_pubkey(&sk);
let macs = {
let pk = PublicKey::from(*pk);
macs::Validator::new(pk)
};
Device { Device {
keyst: None, keyst: Some(KeyState { sk, pk, macs }),
id_map: DashMap::new(), id_map: DashMap::new(),
pk_map: HashMap::new(), pk_map: HashMap::new(),
limiter: Mutex::new(RateLimiter::new()), limiter: Mutex::new(RateLimiter::new()),
} }
} }
fn update_ss(&mut self) -> (Vec<u32>, Option<PublicKey>) { fn update_ss(&mut self) -> (Vec<u32>, Option<X25519PubKey>) {
let mut same = None; let mut same = None;
let mut ids = Vec::with_capacity(self.pk_map.len()); let mut ids = Vec::with_capacity(self.pk_map.len());
for (pk, peer) in self.pk_map.iter_mut() { for (pk, peer) in self.pk_map.iter_mut() {
if let Some(key) = self.keyst.as_ref() { if let Some(key) = self.keyst.as_ref() {
if key.pk.as_bytes() == pk { if key.pk.as_bytes() == pk {
same = Some(PublicKey::from(*pk)); same = Some(X25519PubKey::from(pk));
peer.ss.clear() peer.ss.clear()
} else { } else {
let pk = PublicKey::from(*pk); let pk = X25519PubKey::from(pk);
peer.ss = *key.sk.diffie_hellman(&pk).as_bytes(); peer.ss = *noise::shared_secret_agent(&key.sk, &pk);
} }
} else { } else {
peer.ss.clear(); peer.ss.clear();
@@ -126,44 +134,6 @@ impl<O> Device<O> {
(ids, same) (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<StaticSecret>) -> Option<PublicKey> {
// 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 /// Add a new public key to the state machine
/// To remove public keys, you must create a new machine instance /// To remove public keys, you must create a new machine instance
/// ///
@@ -172,6 +142,7 @@ impl<O> Device<O> {
/// * `pk` - The public key to add /// * `pk` - The public key to add
/// * `identifier` - Associated identifier which can be used to distinguish the peers /// * `identifier` - Associated identifier which can be used to distinguish the peers
pub fn add(&mut self, pk: PublicKey, opaque: O) -> Result<(), ConfigError> { pub fn add(&mut self, pk: PublicKey, opaque: O) -> Result<(), ConfigError> {
let pk = X25519PubKey::from(pk.as_bytes());
// ensure less than 2^20 peers // ensure less than 2^20 peers
if self.pk_map.len() > MAX_PEER_PER_DEVICE { if self.pk_map.len() > MAX_PEER_PER_DEVICE {
return Err(ConfigError::new("Too many peers for device")); return Err(ConfigError::new("Too many peers for device"));
@@ -185,17 +156,12 @@ impl<O> Device<O> {
} }
// pre-compute shared secret and add to pk_map // pre-compute shared secret and add to pk_map
self.pk_map.insert( let ss = self
*pk.as_bytes(), .keyst
Peer::new( .as_ref()
pk, .map(|key| *noise::shared_secret_agent(&key.sk, &pk))
self.keyst .unwrap_or([0u8; 32]);
.as_ref() self.pk_map.insert(*pk, Peer::new(pk, ss, opaque));
.map(|key| *key.sk.diffie_hellman(&pk).as_bytes())
.unwrap_or([0u8; 32]),
opaque,
),
);
Ok(()) Ok(())
} }

View File

@@ -1,3 +1,4 @@
use agent_lib::X25519PubKey;
use generic_array::GenericArray; use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore}; use rand_core::{CryptoRng, RngCore};
use spin::RwLock; use spin::RwLock;
@@ -119,10 +120,10 @@ impl Generator {
/// # Returns /// # Returns
/// ///
/// A freshly initated generator /// A freshly initated generator
pub fn new(pk: PublicKey) -> Generator { pub fn new(pk: X25519PubKey) -> Generator {
Generator { Generator {
mac1_key: HASH!(LABEL_MAC1, pk.as_bytes()).into(), mac1_key: HASH!(LABEL_MAC1, *pk).into(),
cookie_key: HASH!(LABEL_COOKIE, pk.as_bytes()).into(), cookie_key: HASH!(LABEL_COOKIE, *pk).into(),
last_mac1: None, last_mac1: None,
cookie: None, cookie: None,
} }

View File

@@ -1,5 +1,6 @@
use std::time::Instant; use std::time::Instant;
use agent_lib::{x25519, X25519PrivKey, X25519PubKey, X25519SharedKey};
// DH // DH
use x25519_dalek::{PublicKey, SharedSecret, StaticSecret}; use x25519_dalek::{PublicKey, SharedSecret, StaticSecret};
@@ -36,7 +37,7 @@ type HMACBlake2s = Hmac<Blake2s>;
// convenient alias to pass state temporarily into device.rs and back // convenient alias to pass state temporarily into device.rs and back
type TemporaryState = (u32, PublicKey, GenericArray<u8, U32>, GenericArray<u8, U32>); type TemporaryState = (u32, X25519PubKey, GenericArray<u8, U32>, GenericArray<u8, U32>);
const SIZE_CK: usize = 32; const SIZE_CK: usize = 32;
const SIZE_HS: usize = 32; const SIZE_HS: usize = 32;
@@ -227,6 +228,11 @@ fn shared_secret(sk: &StaticSecret, pk: &PublicKey) -> Result<SharedSecret, Hand
} }
} }
#[inline(always)]
pub fn shared_secret_agent(sk: &X25519PrivKey, pk: &X25519PubKey) -> X25519SharedKey {
x25519(sk, pk)
}
pub(super) fn create_initiation<R: RngCore + CryptoRng, O>( pub(super) fn create_initiation<R: RngCore + CryptoRng, O>(
rng: &mut R, rng: &mut R,
keyst: &KeyState, keyst: &KeyState,
@@ -278,7 +284,7 @@ pub(super) fn create_initiation<R: RngCore + CryptoRng, O>(
SEAL!( SEAL!(
&key, &key,
&hs, // ad &hs, // ad
keyst.pk.as_bytes(), // pt keyst.pk.as_slice(), // pt
&mut msg.f_static // ct || tag &mut msg.f_static // ct || tag
); );
@@ -328,7 +334,7 @@ pub(super) fn consume_initiation<'a, O>(
let ck = INITIAL_CK; let ck = INITIAL_CK;
let hs = INITIAL_HS; let hs = INITIAL_HS;
let hs = HASH!(&hs, keyst.pk.as_bytes()); let hs = HASH!(&hs, *keyst.pk);
// C := Kdf(C, E_pub) // 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)) // (C, k) := Kdf2(C, DH(E_priv, S_pub))
let eph_r_pk = PublicKey::from(msg.f_ephemeral); let eph_r_pk = X25519PubKey::from(&msg.f_ephemeral);
let (ck, key) = KDF2!(&ck, shared_secret(&keyst.sk, &eph_r_pk)?.as_bytes()); let (ck, key) = KDF2!(&ck, shared_secret_agent(&keyst.sk, &eph_r_pk).as_slice());
// msg.static := Aead(k, 0, S_pub, H) // msg.static := Aead(k, 0, S_pub, H)
@@ -440,6 +446,7 @@ pub(super) fn create_response<R: RngCore + CryptoRng, O>(
// C := Kdf1(C, DH(E_priv, E_pub)) // 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()); let ck = KDF1!(&ck, shared_secret(&eph_sk, &eph_r_pk)?.as_bytes());
// C := Kdf1(C, DH(E_priv, S_pub)) // 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)) // 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) // (C, tau, k) := Kdf3(C, Q)

View File

@@ -1,3 +1,4 @@
use agent_lib::X25519PubKey;
use spin::Mutex; use spin::Mutex;
use std::mem; use std::mem;
@@ -59,7 +60,7 @@ impl Drop for State {
} }
impl<O> Peer<O> { impl<O> Peer<O> {
pub fn new(pk: PublicKey, ss: [u8; 32], opaque: O) -> Self { pub fn new(pk: X25519PubKey, ss: [u8; 32], opaque: O) -> Self {
Self { Self {
opaque, opaque,
macs: Mutex::new(macs::Generator::new(pk)), macs: Mutex::new(macs::Generator::new(pk)),

View File

@@ -183,16 +183,17 @@ impl<T: Tun, B: UDP> WireGuard<T, B> {
} }
pub fn set_key(&self, sk: Option<StaticSecret>) { pub fn set_key(&self, sk: Option<StaticSecret>) {
let mut peers = self.peers.write(); // let mut peers = self.peers.write();
peers.set_sk(sk); // peers.set_sk(sk);
self.router.clear_sending_keys(); // self.router.clear_sending_keys();
} }
pub fn get_sk(&self) -> Option<StaticSecret> { pub fn get_sk(&self) -> Option<StaticSecret> {
self.peers // self.peers
.read() // .read()
.get_sk() // .get_sk()
.map(|sk| StaticSecret::from(sk.to_bytes())) // .map(|sk| StaticSecret::from(sk.to_bytes()))
None
} }
pub fn set_psk(&self, pk: PublicKey, psk: [u8; 32]) -> bool { pub fn set_psk(&self, pk: PublicKey, psk: [u8; 32]) -> bool {

9
test.conf Normal file
View File

@@ -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

11
test2.conf Normal file
View File

@@ -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