gossip encryption (fix #4)
This commit is contained in:
parent
7c9839f900
commit
52926d6152
3 changed files with 251 additions and 8 deletions
193
Cargo.lock
generated
193
Cargo.lock
generated
|
@ -2,6 +2,16 @@
|
|||
# It is not intended for manual editing.
|
||||
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"
|
||||
|
@ -17,6 +27,18 @@ version = "1.0.69"
|
|||
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"
|
||||
|
@ -55,6 +77,29 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -67,12 +112,66 @@ version = "1.1.1"
|
|||
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"
|
||||
|
@ -107,6 +206,16 @@ version = "0.3.55"
|
|||
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"
|
||||
|
@ -208,6 +317,15 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -235,12 +353,29 @@ version = "2.5.0"
|
|||
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"
|
||||
|
@ -328,6 +463,15 @@ version = "0.6.28"
|
|||
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"
|
||||
|
@ -357,6 +501,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -426,6 +576,12 @@ dependencies = [
|
|||
"winnow",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -447,6 +603,16 @@ dependencies = [
|
|||
"tinyvec",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -458,6 +624,12 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -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",
|
||||
]
|
||||
|
||||
|
@ -546,8 +720,27 @@ dependencies = [
|
|||
"xml-rs",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
|
@ -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"
|
||||
|
|
64
src/main.rs
64
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();
|
||||
|
||||
|
|
Loading…
Reference in a new issue