diff --git a/services/identity/Cargo.lock b/services/identity/Cargo.lock --- a/services/identity/Cargo.lock +++ b/services/identity/Cargo.lock @@ -4,9 +4,9 @@ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -680,7 +680,7 @@ "argon2", "log", "opaque-ke 2.0.0", - "rand", + "rand 0.8.5", "tonic", "wasm-bindgen", ] @@ -971,6 +971,29 @@ "signature", ] +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + [[package]] name = "either" version = "1.8.1" @@ -1461,13 +1484,14 @@ "constant_time_eq 0.2.5", "curve25519-dalek 3.2.0", "derive_more", + "ed25519-dalek", "futures-core", "hex", "moka", "once_cell", "opaque-ke 1.2.0", "prost", - "rand", + "rand 0.8.5", "serde", "serde_json", "siwe", @@ -1772,7 +1796,7 @@ "getrandom 0.2.9", "hkdf 0.11.0", "hmac 0.11.0", - "rand", + "rand 0.8.5", "subtle", "zeroize", ] @@ -1792,7 +1816,7 @@ "generic-array", "hkdf 0.12.3", "hmac 0.12.1", - "rand", + "rand 0.8.5", "serde", "subtle", "voprf", @@ -2066,6 +2090,19 @@ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -2073,10 +2110,20 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -2105,6 +2152,15 @@ "getrandom 0.2.9", ] +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + [[package]] name = "raw-cpuid" version = "10.7.0" @@ -2134,9 +2190,9 @@ [[package]] name = "regex" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" dependencies = [ "aho-corasick", "memchr", @@ -2145,9 +2201,9 @@ [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "ring" @@ -2423,7 +2479,7 @@ "http", "iri-string", "k256", - "rand", + "rand 0.8.5", "sha3", "thiserror", ] @@ -2761,7 +2817,7 @@ "indexmap", "pin-project", "pin-project-lite", - "rand", + "rand 0.8.5", "slab", "tokio", "tokio-util", diff --git a/services/identity/Cargo.toml b/services/identity/Cargo.toml --- a/services/identity/Cargo.toml +++ b/services/identity/Cargo.toml @@ -12,6 +12,7 @@ tokio-stream = "0.1.9" opaque-ke = { version = "1.2.0", features = ["std"] } curve25519-dalek = "3" +ed25519-dalek = "1" clap = { version = "3.1.12", features = ["derive"] } derive_more = "0.99" aws-config = "0.54.0" diff --git a/services/identity/src/main.rs b/services/identity/src/main.rs --- a/services/identity/src/main.rs +++ b/services/identity/src/main.rs @@ -14,6 +14,7 @@ mod interceptor; mod keygen; mod nonce; +mod reserved_users; mod siwe; mod token; diff --git a/services/identity/src/reserved_users.rs b/services/identity/src/reserved_users.rs new file mode 100644 --- /dev/null +++ b/services/identity/src/reserved_users.rs @@ -0,0 +1,56 @@ +use std::str::FromStr; + +use chrono::{DateTime, Utc}; +use constant_time_eq::constant_time_eq; +use ed25519_dalek::Signature; +use serde::Deserialize; +use tonic::Status; + +#[derive(Deserialize)] +struct AccountOwnershipMessage { + statement: String, + username: String, + issued_at: String, +} + +pub fn validate_signed_keyserver_message( + username: &str, + keyserver_message: &str, + keyserver_signature: &str, +) -> Result<(), Status> { + // Deserialize the keyserver message + let deserialized_message: AccountOwnershipMessage = + serde_json::from_str(keyserver_message) + .map_err(|_| Status::invalid_argument("message format invalid"))?; + + let message_statement = deserialized_message.statement; + if !constant_time_eq( + message_statement.as_bytes(), + b"This user is the owner of the following username", + ) { + return Err(Status::invalid_argument("message invalid")); + } + let message_username = deserialized_message.username; + let issued_at: DateTime = deserialized_message + .issued_at + .parse() + .map_err(|_| Status::invalid_argument("message format invalid"))?; + + // Check that the username in the gRPC message matches the username in the keyserver_message string + if message_username != username { + return Err(Status::invalid_argument("message invalid")); + } + + // Check that no more than 5 seconds have passed since the timestamp in the keyserver_message string + let now = Utc::now(); + if (now - issued_at).num_seconds() > 5 { + return Err(Status::invalid_argument("message invalid")); + } + + let _signature = Signature::from_str(keyserver_signature) + .map_err(|_| Status::invalid_argument("message invalid"))?; + + // TODO: verify the signature once ashoat's keyserver is registered + + Ok(()) +}