diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0ee0666 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.linkedProjects": ["./Cargo.toml"] +} diff --git a/Cargo.lock b/Cargo.lock index 6e977b4..53cefe1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,18 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "4.4.7" @@ -96,12 +108,83 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f69037fe1b785e84986b4f2cbcf647381876a00671d25ceef715d7812dd7e1dd" + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "platforms" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.69" @@ -120,12 +203,83 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "2.0.39" @@ -149,11 +303,20 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wg-gen" version = "0.1.0" dependencies = [ + "base64", "clap", + "rand", + "x25519-dalek", ] [[package]] @@ -221,3 +384,35 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "x25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index b9b6ecd..54d73d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +base64 = "0.21.5" clap = { version = "4.4.7", features = ["derive"] } +rand = "0.8.5" +x25519-dalek = "2.0.0" diff --git a/src/key.rs b/src/key.rs new file mode 100644 index 0000000..812235a --- /dev/null +++ b/src/key.rs @@ -0,0 +1,132 @@ +use std::fmt::Display; + +use base64::Engine; +use rand::{rngs::StdRng, RngCore, SeedableRng}; + +pub trait Key { + fn get_data(&self) -> [u8; 32]; +} + +pub struct PrivKey { + data: [u8; 32], +} + +pub struct PubKey { + data: [u8; 32], +} + +impl Key for PrivKey { + fn get_data(&self) -> [u8; 32] { + self.data.to_owned() + } +} + +impl Key for PubKey { + fn get_data(&self) -> [u8; 32] { + self.data.to_owned() + } +} + +impl Display for dyn Key { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + base64::engine::GeneralPurpose::new( + &base64::alphabet::STANDARD, + base64::engine::general_purpose::GeneralPurposeConfig::new() + ) + .encode(self.get_data()) + ) + } +} + +impl Display for PrivKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + base64::engine::GeneralPurpose::new( + &base64::alphabet::STANDARD, + base64::engine::general_purpose::GeneralPurposeConfig::new() + ) + .encode(self.get_data()) + ) + } +} +impl Display for PubKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + base64::engine::GeneralPurpose::new( + &base64::alphabet::STANDARD, + base64::engine::general_purpose::GeneralPurposeConfig::new() + ) + .encode(self.get_data()) + ) + } +} + +impl PrivKey { + pub fn generate() -> Self { + let mut buf: [u8; 32] = [0; 32]; + StdRng::from_entropy().fill_bytes(&mut buf); + PrivKey { data: buf } + } + + pub fn derive_pubkey(&self) -> PubKey { + let key: [u8; 32] = self.data.clone().try_into().unwrap(); + let pubkey = x25519_dalek::x25519(key, x25519_dalek::X25519_BASEPOINT_BYTES); + + PubKey { data: pubkey } + } +} + +impl From<[u8; 32]> for PrivKey { + fn from(value: [u8; 32]) -> Self { + PrivKey { data: value } + } +} + +impl From<&[u8; 32]> for PrivKey { + fn from(value: &[u8; 32]) -> Self { + PrivKey { + data: value.clone(), + } + } +} + +pub fn generate_key_pair() -> (PrivKey, PubKey) { + let privkey = PrivKey::generate(); + let pubkey = privkey.derive_pubkey(); + (privkey, pubkey) +} + +#[cfg(test)] +mod tests { + use base64::Engine; + + use crate::key::PrivKey; + + #[test] + fn test_derive() { + const KEY: &str = "8GXvDsTm8DOC/qizByqVEAlKgA/2avVmkofR6gI+QVg="; + let decoded: [u8; 32] = base64::engine::GeneralPurpose::new( + &base64::alphabet::STANDARD, + base64::engine::general_purpose::GeneralPurposeConfig::new(), + ) + .decode(KEY) + .unwrap() + .try_into() + .unwrap(); + let privkey = PrivKey::from(decoded); + + let pubkey = privkey.derive_pubkey(); + + assert_eq!( + "9zuC6zGZcrz7Aa9uboEp8N8QT+IUiO3RaemhSbhvQTA=", + format!("{}", pubkey) + ); + } +} diff --git a/src/main.rs b/src/main.rs index b6931c9..dbfef7e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,31 @@ -use clap::Parser; +use clap::{Arg, Command}; -#[derive(Parser)] -#[command(name = "wg-genconf")] -struct Args { - peers: u32 -} +mod key; + +const C_GENERATE: &str = "generate"; +const C_GENERATE_FULL: &str = "fullgen"; +const S_KEYS: &str = "keys"; +const S_PEERS: &str = "peers"; +const A_PEERCOUNT: &str = "peercount"; fn main() { - let args = Args::parse(); - println!("{}", args.peers); + let matches = Command::new("wg-conf") + .about("wireguard configuration utility") + .version("1.0.0e") + .author("femsci") + .subcommand_required(true) + .arg_required_else_help(true) + .subcommand( + Command::new(C_GENERATE) + .short_flag('g') + .long_flag(C_GENERATE) + .about("generate wg configuration") + .arg( + Arg::new(A_PEERCOUNT) + .short('n') + .long("peercount") + .help("number of peers"), + ), + ) + .get_matches(); }