From 323da003f9a5cf6c264c00d65179b96eab34bc20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Wed, 11 Dec 2024 11:15:48 +0100 Subject: [PATCH] Fix serde for Key and add Key generation methods --- Cargo.lock | 124 +++++++++++++++++++++++++---------------- Cargo.toml | 7 ++- src/bsd/nvlist.rs | 4 +- src/dependencies.rs | 7 +-- src/key.rs | 92 ++++++++++++++++-------------- src/utils.rs | 13 ++--- src/wgapi_freebsd.rs | 5 +- src/wgapi_linux.rs | 2 +- src/wgapi_userspace.rs | 4 +- 9 files changed, 149 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d8502f..e881823 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -28,33 +28,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", "windows-sys", @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "autocfg" @@ -92,9 +92,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cfg-if" @@ -110,15 +110,15 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -151,7 +151,7 @@ dependencies = [ [[package]] name = "defguard_wireguard_rs" -version = "0.5.5" +version = "0.6.0" dependencies = [ "base64", "env_logger", @@ -165,7 +165,8 @@ dependencies = [ "netlink-sys", "nix", "serde", - "thiserror", + "serde_test", + "thiserror 2.0.6", "x25519-dalek", ] @@ -223,9 +224,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "libc" -version = "0.2.159" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "log" @@ -295,7 +296,7 @@ dependencies = [ "anyhow", "byteorder", "paste", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -314,9 +315,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" +checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" dependencies = [ "bytes", "libc", @@ -344,9 +345,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -371,9 +372,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -383,9 +384,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -415,24 +416,33 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_test" +version = "1.0.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed" +dependencies = [ + "serde", +] + [[package]] name = "subtle" version = "2.6.1" @@ -441,9 +451,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -452,18 +462,38 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" dependencies = [ "proc-macro2", "quote", @@ -472,9 +502,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "utf8parse" @@ -490,9 +520,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] diff --git a/Cargo.toml b/Cargo.toml index 77866dd..bd6b8aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "defguard_wireguard_rs" -version = "0.5.5" +version = "0.6.0" edition = "2021" description = "A unified multi-platform high-level API for managing WireGuard interfaces" license = "Apache-2.0" @@ -14,11 +14,12 @@ categories = ["network-programming"] base64 = "0.22" log = "0.4" serde = { version = "1.0", features = ["derive"] } -thiserror = "1.0" +thiserror = "2.0" +x25519-dalek = { version = "2.0", features = ["getrandom", "static_secrets"] } [dev-dependencies] env_logger = "0.11" -x25519-dalek = { version = "2.0", features = ["getrandom", "static_secrets"] } +serde_test = "1.0" [target.'cfg(target_os = "freebsd")'.dependencies] libc = { version = "0.2", default-features = false } diff --git a/src/bsd/nvlist.rs b/src/bsd/nvlist.rs index a064903..ef2deb0 100644 --- a/src/bsd/nvlist.rs +++ b/src/bsd/nvlist.rs @@ -78,7 +78,7 @@ pub enum NvValue<'a> { // NvListAUp, } -impl<'a> NvValue<'a> { +impl NvValue<'_> { /// Return number of bytes this value occupies when packed. #[must_use] pub fn byte_size(&self) -> usize { @@ -162,7 +162,7 @@ pub struct NvList<'a> { is_big_endian: bool, } -impl<'a> Default for NvList<'a> { +impl Default for NvList<'_> { fn default() -> Self { Self::new() } diff --git a/src/dependencies.rs b/src/dependencies.rs index 40308f3..6da2f78 100644 --- a/src/dependencies.rs +++ b/src/dependencies.rs @@ -44,10 +44,9 @@ pub(crate) fn check_external_dependencies() -> Result<(), WireguardInterfaceErro }); if let Some(cmd) = missing { - return Err(WireguardInterfaceError::MissingDependency(format!( - "Command `{}` required by wireguard-rs couldn't be found. The following directories were checked: {paths:?}", - cmd - ))); + Err(WireguardInterfaceError::MissingDependency(format!( + "Command `{cmd}` required by wireguard-rs couldn't be found. The following directories were checked: {paths:?}" + ))) } else { debug!("All commands required by wireguard-rs are available"); Ok(()) diff --git a/src/key.rs b/src/key.rs index ef5b8ca..420eb8c 100644 --- a/src/key.rs +++ b/src/key.rs @@ -7,7 +7,8 @@ use std::{ }; use base64::{prelude::BASE64_STANDARD, DecodeError, Engine}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, Serializer}; +use x25519_dalek::{PublicKey, StaticSecret}; const KEY_LENGTH: usize = 32; @@ -22,7 +23,7 @@ fn hex_value(char: u8) -> Option { } /// WireGuard key representation in binary form. -#[derive(Clone, Default, Serialize, Deserialize)] +#[derive(Clone, Default, Deserialize)] #[serde(try_from = "&str")] pub struct Key([u8; KEY_LENGTH]); @@ -84,6 +85,18 @@ impl Key { } Ok(Self(key)) } + + /// Generate WireGuard private key. + #[must_use] + pub fn generate() -> Self { + Self(StaticSecret::random().to_bytes()) + } + + /// Make WireGuard public key from a private key. + #[must_use] + pub fn public_key(private_key: &Self) -> Self { + Self(PublicKey::from(private_key.0).to_bytes()) + } } impl TryFrom<&str> for Key { @@ -96,16 +109,7 @@ impl TryFrom<&str> for Key { Key::decode(value) } else { // Try base64 - let v = BASE64_STANDARD.decode(value)?; - let length = v.len(); - if length == KEY_LENGTH { - let buf = v - .try_into() - .map_err(|_| Self::Error::InvalidLength(length))?; - Ok(Self::new(buf)) - } else { - Err(Self::Error::InvalidLength(length)) - } + Self::from_str(value) } } } @@ -166,45 +170,49 @@ impl fmt::Display for Key { } } +impl Serialize for Key { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&BASE64_STANDARD.encode(self.0)) + } +} + #[cfg(test)] mod tests { use super::*; + use serde_test::{assert_tokens, Token}; + + // Same `Key` in different representations. + static KEY_B64: &str = "AAECAwQFBgcICQoLDA0OD/Dh0sO0pZaHeGlaSzwtHg8="; + static KEY_HEX: &str = "000102030405060708090a0b0c0d0e0ff0e1d2c3b4a5968778695a4b3c2d1e0f"; + static KEY_BUF: [u8; KEY_LENGTH] = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, + 0x1e, 0x0f, + ]; + #[test] fn decode_key() { - let key_str = "000102030405060708090a0b0c0d0e0ff0e1d2c3b4a5968778695a4b3c2d1e0f"; - let key = Key::decode(key_str).unwrap(); - assert_eq!( - key.0, - [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, - 0x0e, 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, - 0x3c, 0x2d, 0x1e, 0x0f - ] - ); - assert_eq!(key.to_lower_hex(), key_str); - assert_eq!( - format!("{key}"), - "AAECAwQFBgcICQoLDA0OD/Dh0sO0pZaHeGlaSzwtHg8=" - ); + let key = Key::decode(KEY_HEX).unwrap(); + assert_eq!(key.0, KEY_BUF); + assert_eq!(key.to_lower_hex(), KEY_HEX); + assert_eq!(key.to_string(), KEY_B64); } #[test] fn parse_key() { - let key_str = "AAECAwQFBgcICQoLDA0OD/Dh0sO0pZaHeGlaSzwtHg8="; - let key: Key = key_str.try_into().unwrap(); - assert_eq!( - key.0, - [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, - 0x0e, 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, - 0x3c, 0x2d, 0x1e, 0x0f - ] - ); - assert_eq!( - key.to_lower_hex(), - "000102030405060708090a0b0c0d0e0ff0e1d2c3b4a5968778695a4b3c2d1e0f" - ); - assert_eq!(format!("{key}"), key_str); + let key: Key = KEY_B64.try_into().unwrap(); + assert_eq!(key.0, KEY_BUF); + assert_eq!(key.to_lower_hex(), KEY_HEX); + assert_eq!(key.to_string(), KEY_B64); + } + + #[test] + fn serialize_key() { + let key = Key(KEY_BUF); + assert_tokens(&key, &[Token::BorrowedStr(KEY_B64)]); } } diff --git a/src/utils.rs b/src/utils.rs index f588c42..474c4bb 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,16 +1,15 @@ -#[cfg(target_os = "linux")] -use std::collections::HashSet; #[cfg(target_os = "macos")] use std::io::{BufRead, BufReader, Cursor, Error as IoError}; #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] use std::net::{Ipv4Addr, Ipv6Addr}; +#[cfg(target_os = "linux")] +use std::{collections::HashSet, fs::OpenOptions}; +#[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "netbsd"))] +use std::{io::Write, process::Stdio}; use std::{ - fs::OpenOptions, net::{IpAddr, SocketAddr, ToSocketAddrs}, process::Command, }; -#[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "netbsd"))] -use std::{io::Write, process::Stdio}; #[cfg(target_os = "freebsd")] use crate::check_command_output_status; @@ -296,7 +295,7 @@ pub(crate) fn add_peer_routing( Ok(()) => debug!("Route to {default1} has been added for interface {ifname}"), Err(err) => { match err { - IoError::WriteIo(errno) if errno == Errno::ENETUNREACH => { + IoError::WriteIo(Errno::ENETUNREACH) => { warn!("Failed to add default route {default1} for interface {ifname}: Network is unreachable. \ This may happen if your interface's IP address is not the same IP version as the default gateway ({default1}) that was tried to be set, in this case this warning can be ignored. \ Otherwise, there may be some other issues with your network configuration."); @@ -311,7 +310,7 @@ pub(crate) fn add_peer_routing( Ok(()) => debug!("Route to {default2} has been added for interface {ifname}"), Err(err) => { match err { - IoError::WriteIo(errno) if errno == Errno::ENETUNREACH => { + IoError::WriteIo(Errno::ENETUNREACH) => { warn!("Failed to add default route {default2} for interface {ifname}: Network is unreachable. \ This may happen if your interface's IP address is not the same IP version as the default gateway ({default2}) that was tried to be set, in this case this warning can be ignored. \ Otherwise, there may be some other issues with your network configuration."); diff --git a/src/wgapi_freebsd.rs b/src/wgapi_freebsd.rs index c5aeb70..fde760e 100644 --- a/src/wgapi_freebsd.rs +++ b/src/wgapi_freebsd.rs @@ -91,7 +91,10 @@ impl WireguardInterfaceApi for WGApi { if let Some(mtu) = config.mtu { debug!("Setting MTU of {mtu} for interface {}", self.ifname); bsd::set_mtu(&self.ifname, mtu)?; - debug!("MTU of {mtu} set for interface {}, value: {mtu}"); + debug!( + "MTU of {mtu} set for interface {}, value: {mtu}", + self.ifname + ); } info!( diff --git a/src/wgapi_linux.rs b/src/wgapi_linux.rs index 2d4b8f6..b2025b7 100644 --- a/src/wgapi_linux.rs +++ b/src/wgapi_linux.rs @@ -1,4 +1,4 @@ -use std::{marker::PhantomData, net::IpAddr, process::Command, str::FromStr}; +use std::{net::IpAddr, str::FromStr}; use crate::{ netlink, diff --git a/src/wgapi_userspace.rs b/src/wgapi_userspace.rs index 37b13fd..ffb9881 100644 --- a/src/wgapi_userspace.rs +++ b/src/wgapi_userspace.rs @@ -303,7 +303,7 @@ impl WireguardInterfaceApi for WGApi { return Err(WireguardInterfaceError::UnixSockerError(format!( "Failed to remove socket for interface {}: {err}", self.ifname - ))) + ))); } } @@ -383,7 +383,7 @@ impl WireguardInterfaceApi for WGApi { } Err(err) => match err { err if err.kind() == ErrorKind::NotFound => { - return Err(WireguardInterfaceError::SocketClosed(format!( + Err(WireguardInterfaceError::SocketClosed(format!( "Failed to read network information for interface {} data, the socket may have been closed before we've attempted to read. If the socket has been closed intentionally, this message can be ignored. Error details: {err}", self.ifname )))