diff --git a/Cargo.lock b/Cargo.lock index c78746f..78e96cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,6 +93,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bitflags" version = "1.3.2" @@ -116,6 +122,15 @@ dependencies = [ "constant_time_eq", ] +[[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 = "bumpalo" version = "3.20.3" @@ -163,7 +178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.3.0", "rand_core 0.10.1", ] @@ -223,6 +238,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -255,6 +276,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "cpufeatures" version = "0.3.0" @@ -294,6 +324,16 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "ctrlc-async" version = "3.2.2" @@ -305,12 +345,49 @@ dependencies = [ "winapi", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "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 = "data-encoding" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "deranged" version = "0.5.8" @@ -321,6 +398,16 @@ dependencies = [ "serde_core", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dirs" version = "1.0.5" @@ -349,6 +436,32 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "serde", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.16.0" @@ -397,10 +510,13 @@ dependencies = [ [[package]] name = "fedichat" version = "0.1.0" -source = "git+https://git.firechicken.net/fedichat/fedichat-lib#49cbd905ceecb7bf7be463f81836742e3a7ddc24" +source = "git+https://git.firechicken.net/fedichat/fedichat-lib#3edf74d890232d09737ac9c343ebb24331fe7c68" dependencies = [ + "ed25519", + "rmp-serde", "serde", "serde_bytes", + "thiserror 2.0.18", "time", "uuid", ] @@ -412,10 +528,12 @@ dependencies = [ "clap", "ctrlc-async", "dotenv", + "ed25519-dalek", "expanduser", "fedichat", "hickory-resolver", "quinn", + "rand 0.8.6", "rmp-serde", "serde", "thiserror 2.0.18", @@ -427,6 +545,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -499,6 +623,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1077,6 +1211,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -1225,13 +1369,24 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -1246,6 +1401,16 @@ dependencies = [ "rand_core 0.10.1", ] +[[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 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -1256,6 +1421,15 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + [[package]] name = "rand_core" version = "0.9.5" @@ -1558,6 +1732,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1583,6 +1768,15 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "simd_cesu8" version = "1.1.1" @@ -1627,6 +1821,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -1923,6 +2127,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1983,6 +2193,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "walkdir" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index e58cddf..1fcdebf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,5 @@ time = { version = "0.3.47", features = ["serde"] } hickory-resolver = "0.26.1" expanduser = "1.2.2" uuid = { version = "1.23.2", features = ["v4"] } +ed25519-dalek = { version = "2.2.0", features = ["rand_core", "serde"] } +rand = "0.8" diff --git a/src/cli.rs b/src/cli.rs index 48bd66c..d4b30ce 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,5 @@ use clap::{Parser,Subcommand,ArgAction}; +use ed25519_dalek::SigningKey; use fedichat::client::{ClientMessage,SignedClientMessage,AuthMethod}; use fedichat::ServerAddr; use fedichat::state::StatePath; @@ -181,7 +182,7 @@ impl Command { } // If a command needs multiple messages, like an auth message first // then call this - pub fn generate_messages(self,username: String,token: Option) + pub fn generate_messages(self,username: String,token: Option, key: SigningKey) -> Result,MessageError> { let mut messages = Vec::with_capacity(2); @@ -211,6 +212,11 @@ impl Command { signature: Box::new([0])}); } + // sign all the messages + for message in messages.iter_mut() { + message.sign(key.clone())?; + } + Ok(messages) } } @@ -223,6 +229,8 @@ pub enum MessageError { UuidError(#[from] uuid::Error), #[error("Error during file IO: {0}")] IoError(#[from] std::io::Error), + #[error("Error while processing signature: {0}")] + Signature(#[from] fedichat::client::SignatureError) } diff --git a/src/config.rs b/src/config.rs index 9d3bb3d..bc692a9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,6 @@ use expanduser::expanduser; +use rand::rngs::OsRng; +use ed25519_dalek::SigningKey; use serde::{Serialize,Deserialize}; use std::collections::HashMap; use std::fs::File; @@ -62,14 +64,41 @@ impl Config { pub fn insert_token(&mut self,server: &String,username: String, token: String) -> Result<(),ConfigError> { let _ = self.servers.get_mut(server).ok_or(ConfigError::ServerNotFound(server.clone()))? - .users.insert(username,token); + .users.get_mut(&username).ok_or(ConfigError::UserNotFound(username.clone()))?.token = Some(token); Ok(()) } pub fn get_token(&self,server: &String, username: &String) -> Result,ConfigError> { + // TODO: Dont really have to clone the return value probably. Should refactor at some point Ok(self.servers.get(server).ok_or(ConfigError::ServerNotFound(server.clone()))? - .users.get(username).cloned()) + .users.get(username).ok_or(ConfigError::UserNotFound(username.clone()))?.token.clone()) + } + + // Set up key if user does not exist + // + // If user does exist they are guaranteed to already have a key + // + // Maybe at some point there should be a command to load/save/regenerate keys + pub fn maybe_gen_key(&mut self, server: &String, username: &String) -> Result<(),ConfigError>{ + if !self.servers.contains_key(server) { + self.servers.insert(server.to_string(),Server{users: HashMap::new()}); + } + let server = self.servers.get_mut(server).ok_or(ConfigError::ServerNotFound(server.clone()))?; + + if !server.users.contains_key(username) { + let mut rng = OsRng; + let key = SigningKey::generate(&mut rng); + server.users.insert(username.to_string(),User{token: None, key}); + } + + Ok(()) + } + + pub fn get_key(&self,server: &String, username: &String) -> Result{ + + Ok(self.servers.get(server).ok_or(ConfigError::ServerNotFound(server.clone()))? + .users.get(username).ok_or(ConfigError::UserNotFound(username.clone()))?.key.clone()) } } @@ -78,7 +107,14 @@ impl Config { #[derive(Serialize,Deserialize)] pub struct Server { // user to token mapping - users: HashMap + users: HashMap +} + +// NOTE: Storing keys in the config file means we need to lock down read access +#[derive(Serialize,Deserialize,Clone)] +pub struct User { + pub token: Option, + pub key: ed25519_dalek::SigningKey } #[derive(Error,Debug)] @@ -91,8 +127,8 @@ pub enum ConfigError { #[error("Error while serializing config file: {0}")] SerError(#[from] toml::ser::Error), - //#[error("User `{0}` not found in config")] - //UserNotFound(String), + #[error("User `{0}` not found in config")] + UserNotFound(String), #[error("Server `{0}` not found in config")] ServerNotFound(String), diff --git a/src/main.rs b/src/main.rs index feee87f..d1a19ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,10 +57,13 @@ async fn main() -> Result<(), Box> { Err(e) => return Err(Box::new(e).into()) }; + // set homeserver let target_server = match cli.server { Some(s) => s, None => config.default_server.clone() }; + // Generate a key for the specified user if they do not already exist + config.maybe_gen_key(&target_server,&cli.username)?; let token = config.get_token(&target_server,&cli.username)?; debug!("Target server is {target_server}"); @@ -83,9 +86,11 @@ async fn main() -> Result<(), Box> { if let Command::Fetch{file, ..} = &cli.command { file_to_write = Some(file.clone()); } + + let key = config.get_key(&target_server,&cli.username)?; // send messages, each time waiting for a response - let messages = cli.command.generate_messages(cli.username.clone(),token)?; + let messages = cli.command.generate_messages(cli.username.clone(),token,key)?; debug!("Sending commands");