diff --git a/Cargo.lock b/Cargo.lock index 2dd5477..7c1be12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,18 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bitflags" version = "2.11.1" @@ -26,6 +38,22 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[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" @@ -36,6 +64,16 @@ dependencies = [ "serde_core", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -46,8 +84,11 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" name = "fedichat" version = "0.1.0" dependencies = [ + "ed25519", + "rmp-serde", "serde", "serde_bytes", + "thiserror", "time", "uuid", ] @@ -82,6 +123,17 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "getrandom" version = "0.4.2" @@ -182,6 +234,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -194,6 +255,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 = "powerfmt" version = "0.2.0" @@ -234,6 +305,34 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[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 = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -299,12 +398,31 @@ dependencies = [ "zmij", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + [[package]] name = "slab" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "syn" version = "2.0.117" @@ -316,6 +434,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.47" @@ -364,12 +502,18 @@ version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ - "getrandom", + "getrandom 0.4.2", "js-sys", "serde_core", "wasm-bindgen", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.3+wasi-0.2.9" @@ -561,6 +705,12 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index a9cf7f0..aca91e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,12 @@ version = "0.1.0" edition = "2024" [dependencies] +# This is kinda an old version but the dalek side of v3 is still in prerelease +ed25519 = "2.2.3" +rmp-serde = "1.3.1" serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11.19" +thiserror = "2.0.18" time = {version = "0.3.47", features = ["serde"]} [dependencies.uuid] diff --git a/src/client.rs b/src/client.rs index 2e8e21b..a49f8f9 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,9 +1,13 @@ use uuid::Uuid; -use crate::message::{MessageId,TaggedMessage,VerificationError,Relevance}; +use crate::message::{MessageId,TaggedMessage,Relevance}; use crate::state::{self,StateValue,StatePath,StatePermission,StatePermissionKey}; use crate::{RoomId,Group,Role,User,GroupPower,ServerAddr}; +use ed25519::signature::{Signer,Verifier}; +use ed25519::Signature; +use rmp_serde::encode::Serializer; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; +use thiserror::Error; //StatePath [String] //octal is fine for now @@ -61,9 +65,38 @@ impl SignedClientMessage { target: self.target.unwrap_or(servername) } } - pub fn verify(&self) -> Result { - unimplemented!() + // Canonical way to sign messages. Glue the message, target, and timestamp together. Serialize + // them, then sign those bytes. It is slightly ambiguous how target=None messages should + // by signed vs target=Some(local_server) especially if rewriting of the target happens. + // This makes signature verification harder as you would have to check both + // cases to see if either correctly verifies. + pub fn sign>(&mut self, signer: S) -> Result<(),SignatureError> { + + let mut bytes = Vec::new(); + (&self.message,&self.target,&self.timestamp).serialize(&mut Serializer::new(&mut bytes).with_struct_map())?; + let result = signer.try_sign(&bytes)?; + + + self.signature = Box::new(result.to_bytes()); + Ok(()) } + pub fn verify>(&self, verifier: V) -> Result<(),SignatureError> { + + let mut bytes = Vec::new(); + (&self.message,&self.target,&self.timestamp).serialize(&mut Serializer::new(&mut bytes).with_struct_map())?; + + + let sig = Signature::from_slice(&self.signature)?; + Ok(verifier.verify(&bytes,&sig)?) + } +} + +#[derive(Debug,Error)] +pub enum SignatureError { + #[error("Problem while making eliptic curve {0}")] + Ed25519(#[from] ed25519::Error), + #[error("Serialization error: {0}")] + Serialization(#[from] rmp_serde::encode::Error), } #[derive(Serialize,Deserialize,Clone,Debug)] pub enum AuthMethod {