aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2023-03-09 14:23:40 +0100
committerAlex Auvolat <alex@adnab.me>2023-03-09 14:23:40 +0100
commit52926d6152686ccf864fe281c03482b62fb6a749 (patch)
treeba60cb639554dc3d002b99b39bf61c80ab128d33
parent7c9839f900171d88a4fd4cb6d7c5dc556817aace (diff)
downloadwgautomesh-52926d6152686ccf864fe281c03482b62fb6a749.tar.gz
wgautomesh-52926d6152686ccf864fe281c03482b62fb6a749.zip
gossip encryption (fix #4)
-rw-r--r--Cargo.lock193
-rw-r--r--Cargo.toml2
-rw-r--r--src/main.rs64
3 files changed, 251 insertions, 8 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c2a75ae..eb28c58 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,16 @@
version = 3
[[package]]
+name = "aead"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8"
+dependencies = [
+ "crypto-common",
+ "generic-array",
+]
+
+[[package]]
name = "aho-corasick"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -18,6 +28,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]]
+name = "arrayref"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
+
+[[package]]
name = "attohttpc"
version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -56,6 +78,29 @@ dependencies = [
]
[[package]]
+name = "blake3"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "cc",
+ "cfg-if",
+ "constant_time_eq",
+ "digest",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "bytes"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -68,12 +113,66 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b"
[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+ "zeroize",
+]
+
+[[package]]
+name = "constant_time_eq"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "rand_core",
+ "typenum",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -108,6 +207,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
[[package]]
+name = "generic-array"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
name = "get_if_addrs"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -209,6 +318,15 @@ dependencies = [
]
[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -236,12 +354,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
+name = "poly1305"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
+dependencies = [
+ "cpufeatures",
+ "opaque-debug",
+ "universal-hash",
+]
+
+[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -329,6 +464,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
+name = "salsa20"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
name = "serde"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -358,6 +502,12 @@ dependencies = [
]
[[package]]
+name = "subtle"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+
+[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -427,6 +577,12 @@ dependencies = [
]
[[package]]
+name = "typenum"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+
+[[package]]
name = "unicode-bidi"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -448,6 +604,16 @@ dependencies = [
]
[[package]]
+name = "universal-hash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5"
+dependencies = [
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
name = "url"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -459,6 +625,12 @@ dependencies = [
]
[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -470,12 +642,14 @@ version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
+ "blake3",
"get_if_addrs",
"igd",
"log",
"pretty_env_logger",
"serde",
"toml",
+ "xsalsa20poly1305",
"xxhash-rust",
]
@@ -547,7 +721,26 @@ dependencies = [
]
[[package]]
+name = "xsalsa20poly1305"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "472c385ee974833d7e59979eeb74175d56774be3768b5bcc581337e21396bda3"
+dependencies = [
+ "aead",
+ "poly1305",
+ "salsa20",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
name = "xxhash-rust"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "735a71d46c4d68d71d4b24d03fdc2b98e38cea81730595801db779c04fe80d70"
+
+[[package]]
+name = "zeroize"
+version = "1.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
diff --git a/Cargo.toml b/Cargo.toml
index f722bf3..7a04067 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,8 @@ pretty_env_logger = "0.4.0"
serde = { version = "1.0", features = ["derive"] }
bincode = "1.3"
toml = { version = "0.7", default-features = false, features = ["parse"] }
+xsalsa20poly1305 = "0.9"
+blake3 = "1.3"
igd = { version = "0.12", default-features = false }
get_if_addrs = "0.5"
diff --git a/src/main.rs b/src/main.rs
index b913068..1730edc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -34,6 +34,9 @@ struct Config {
interface: Pubkey,
/// The port to use for gossip inside the Wireguard mesh (must be the same on all nodes)
gossip_port: u16,
+ /// The secret to use to authenticate nodes between them
+ gossip_secret: Option<String>,
+ gossip_secret_file: Option<String>,
/// Enable LAN discovery
#[serde(default)]
@@ -71,11 +74,18 @@ fn main() -> Result<()> {
),
};
- let config: Config = {
+ let mut config: Config = {
let config_str = std::fs::read_to_string(config_path)?;
toml::from_str(&config_str)?
};
+ if let Some(f) = &config.gossip_secret_file {
+ if config.gossip_secret.is_some() {
+ bail!("both gossip_secret and gossip_secret_file are given in config file");
+ }
+ config.gossip_secret = Some(std::fs::read_to_string(f)?);
+ }
+
Daemon::new(config)?.run()
}
@@ -96,6 +106,11 @@ fn fasthash(data: &[u8]) -> u64 {
h.digest()
}
+fn kdf(secret: &str) -> xsalsa20poly1305::Key {
+ let hash = blake3::hash(format!("wgautomesh: {}", secret).as_bytes());
+ hash.as_bytes().clone().into()
+}
+
fn wg_dump(config: &Config) -> Result<(Pubkey, u16, Vec<(Pubkey, Option<SocketAddr>, u64)>)> {
let output = Command::new("wg")
.args(["show", &config.interface, "dump"])
@@ -134,6 +149,7 @@ fn wg_dump(config: &Config) -> Result<(Pubkey, u16, Vec<(Pubkey, Option<SocketAd
struct Daemon {
config: Config,
+ gossip_key: xsalsa20poly1305::Key,
our_pubkey: Pubkey,
listen_port: u16,
socket: UdpSocket,
@@ -166,11 +182,14 @@ enum Gossip {
impl Daemon {
fn new(config: Config) -> Result<Self> {
+ let gossip_key = kdf(config.gossip_secret.as_deref().unwrap_or_default());
+
let (our_pubkey, listen_port, _peers) = wg_dump(&config)?;
let socket = UdpSocket::bind(SocketAddr::new("0.0.0.0".parse()?, config.gossip_port))?;
socket.set_broadcast(true)?;
Ok(Daemon {
config,
+ gossip_key,
our_pubkey,
listen_port,
socket,
@@ -186,7 +205,7 @@ impl Daemon {
error!("Error initializing wireguard peers: {}", e);
}
- let request = bincode::serialize(&Gossip::Request)?;
+ let request = self.make_packet(&Gossip::Request)?;
for peer in self.config.peers.iter() {
let addr = SocketAddr::new(peer.address, self.config.gossip_port);
if let Err(e) = self.socket.send_to(&request, addr) {
@@ -264,7 +283,7 @@ impl Daemon {
}
Gossip::Request => {
for (pubkey, endpoints) in state.gossip.iter() {
- let packet = bincode::serialize(&Gossip::Announce {
+ let packet = self.make_packet(&Gossip::Announce {
pubkey: pubkey.clone(),
endpoints: endpoints.clone(),
})?;
@@ -287,12 +306,24 @@ impl Daemon {
}
fn recv_gossip(&self) -> Result<(SocketAddr, Gossip)> {
+ use xsalsa20poly1305::{
+ aead::{Aead, KeyInit},
+ XSalsa20Poly1305, NONCE_SIZE,
+ };
+
let mut buf = vec![0u8; 1500];
let (amt, src) = self.socket.recv_from(&mut buf)?;
- if !self.config.peers.iter().any(|x| x.address == src.ip()) {
- bail!("Received message from unexpected peer: {}", src);
+
+ if amt < NONCE_SIZE {
+ bail!("invalid packet");
}
- let gossip = bincode::deserialize(&buf[..amt])?;
+
+ let cipher = XSalsa20Poly1305::new(&self.gossip_key);
+ let plaintext = cipher
+ .decrypt(buf[..NONCE_SIZE].try_into().unwrap(), &buf[NONCE_SIZE..amt])
+ .map_err(|e| anyhow!("decrypt error: {}", e))?;
+
+ let gossip = bincode::deserialize(&plaintext)?;
debug!("RECV {}\t{:?}", src, gossip);
Ok((src, gossip))
}
@@ -309,7 +340,7 @@ impl Daemon {
}
fn lan_broadcast_iter(&self) -> Result<()> {
- let packet = bincode::serialize(&Gossip::LanBroadcast {
+ let packet = self.make_packet(&Gossip::LanBroadcast {
pubkey: self.our_pubkey.clone(),
listen_port: self.listen_port,
})?;
@@ -365,6 +396,23 @@ impl Daemon {
Ok(())
}
+
+ fn make_packet(&self, gossip: &Gossip) -> Result<Vec<u8>> {
+ use xsalsa20poly1305::{
+ aead::{Aead, KeyInit, OsRng},
+ XSalsa20Poly1305,
+ };
+
+ let plaintext = bincode::serialize(&gossip)?;
+
+ let cipher = XSalsa20Poly1305::new(&self.gossip_key);
+ let nonce = XSalsa20Poly1305::generate_nonce(&mut OsRng);
+ let ciphertext = cipher
+ .encrypt(&nonce, &plaintext[..])
+ .map_err(|e| anyhow!("encrypt error: {}", e))?;
+
+ Ok([&nonce[..], &ciphertext[..]].concat())
+ }
}
struct State {
@@ -374,7 +422,7 @@ struct State {
impl State {
fn send_gossip(&self, daemon: &Daemon, gossip: Gossip) -> Result<()> {
- let packet = bincode::serialize(&gossip)?;
+ let packet = daemon.make_packet(&gossip)?;
let now = time();