diff --git a/services/commtest/Cargo.lock b/services/commtest/Cargo.lock --- a/services/commtest/Cargo.lock +++ b/services/commtest/Cargo.lock @@ -8,6 +8,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +[[package]] +name = "argon2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73" +dependencies = [ + "base64ct", + "blake2", + "password-hash", +] + [[package]] name = "async-stream" version = "0.3.3" @@ -92,6 +103,12 @@ "tower-service", ] +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base64" version = "0.13.1" @@ -104,12 +121,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "block-buffer" version = "0.10.3" @@ -119,6 +157,12 @@ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + [[package]] name = "byteorder" version = "1.4.3" @@ -149,6 +193,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "comm-opaque2" +version = "0.2.0" +dependencies = [ + "argon2", + "log", + "opaque-ke", + "rand", + "tonic 0.9.2", + "wasm-bindgen", +] + [[package]] name = "commtest" version = "0.1.0" @@ -157,6 +213,7 @@ "async-stream", "base64 0.20.0", "bytesize", + "comm-opaque2", "derive_more", "futures", "futures-util", @@ -165,16 +222,23 @@ "num_cpus", "openssl", "prost", + "rand", "serde_json", "sha2", "tokio", "tokio-tungstenite", - "tonic", + "tonic 0.8.3", "tonic-build", "tunnelbroker_messages", "url", ] +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + [[package]] name = "convert_case" version = "0.4.0" @@ -190,6 +254,18 @@ "libc", ] +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -200,6 +276,39 @@ "typenum", ] +[[package]] +name = "curve25519-dalek" +version = "4.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4033478fbf70d6acf2655ac70da91ee65852d69daf7a67bf7a2f518fb47aafcf" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", +] + +[[package]] +name = "derive-where" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d322f2907b2abad3117790c1a54d8f2d64574ba0fbea54cb6c6e66a0e50d99a4" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.107", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -215,12 +324,33 @@ [[package]] name = "digest" -version = "0.10.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", ] [[package]] @@ -229,6 +359,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "fastrand" version = "1.8.0" @@ -238,6 +387,16 @@ "instant", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -339,6 +498,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ + "serde", "typenum", "version_check", ] @@ -354,6 +514,17 @@ "wasi", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "h2" version = "0.3.17" @@ -400,6 +571,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "http" version = "0.2.8" @@ -593,6 +782,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +[[package]] +name = "opaque-ke" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d410412d23781909d90c3900c5783e830586765f2277bccc78167da8af81a5" +dependencies = [ + "argon2", + "curve25519-dalek", + "derive-where", + "digest 0.10.7", + "displaydoc", + "elliptic-curve", + "generic-array", + "hkdf", + "hmac", + "rand", + "serde", + "subtle", + "voprf", + "zeroize", +] + [[package]] name = "openssl" version = "0.10.48" @@ -632,6 +843,17 @@ "vcpkg", ] +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -859,6 +1081,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.16" @@ -904,7 +1139,7 @@ dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -915,7 +1150,7 @@ dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -937,6 +1172,12 @@ "winapi", ] +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.107" @@ -1122,6 +1363,26 @@ "tracing-futures", ] +[[package]] +name = "tonic" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +dependencies = [ + "base64 0.21.2", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "percent-encoding", + "pin-project", + "tokio-stream", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tonic-build" version = "0.8.4" @@ -1318,6 +1579,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "voprf" +version = "0.4.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081acbe8fcf05d5e8e2aad8ef3d40e02eddeaec07c75a9770d862a0fc0874322" +dependencies = [ + "curve25519-dalek", + "derive-where", + "digest 0.10.7", + "displaydoc", + "elliptic-curve", + "generic-array", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "want" version = "0.3.0" @@ -1334,6 +1614,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.15", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + [[package]] name = "which" version = "4.3.0" @@ -1423,3 +1757,23 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[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 2.0.15", +] diff --git a/services/commtest/Cargo.toml b/services/commtest/Cargo.toml --- a/services/commtest/Cargo.toml +++ b/services/commtest/Cargo.toml @@ -5,6 +5,7 @@ license = "BSD-3-Clause" [dependencies] +comm-opaque2 = { path = "../../shared/comm-opaque2" } tonic = "0.8" tokio = { version = "1.24", features = ["macros", "rt-multi-thread"] } prost = "0.11" @@ -24,6 +25,7 @@ url = "2.3.1" futures-util = "0.3.28" serde_json = "1.0.96" +rand = "0.8.5" [build-dependencies] tonic-build = "0.8" diff --git a/services/commtest/tests/identity_access_tokens_tests.rs b/services/commtest/tests/identity_access_tokens_tests.rs new file mode 100644 --- /dev/null +++ b/services/commtest/tests/identity_access_tokens_tests.rs @@ -0,0 +1,115 @@ +mod proto { + tonic::include_proto!("identity.client"); +} +use comm_opaque2::client::Registration; +use proto::identity_client_service_client::IdentityClientServiceClient; +use proto::{ + DeviceKeyUpload, IdentityKeyInfo, PreKey, RegistrationFinishRequest, + RegistrationStartRequest, VerifyUserAccessTokenRequest, +}; +use rand::{distributions::Alphanumeric, Rng}; + +struct DeviceInfo { + pub username: String, + pub user_id: String, + pub device_id: String, + pub access_token: String, +} + +async fn create_device() -> DeviceInfo { + let password = "pass"; + let username: String = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(7) + .map(char::from) + .collect(); + + // TODO: Generate dynamic valid olm account info + let example_payload = r#"{\"notificationIdentityPublicKeys\":{\"curve25519\":\"DYmV8VdkjwG/VtC8C53morogNJhpTPT/4jzW0/cxzQo\",\"ed25519\":\"D0BV2Y7Qm36VUtjwyQTJJWYAycN7aMSJmhEsRJpW2mk\"},\"primaryIdentityPublicKeys\":{\"curve25519\":\"Y4ZIqzpE1nv83kKGfvFP6rifya0itRg2hifqYtsISnk\",\"ed25519\":\"cSlL+VLLJDgtKSPlIwoCZg0h0EmHlQoJC08uV/O+jvg\"}}"#; + // The ed25519 value from the olm payload + let device_id = r#"cSlL+VLLJDgtKSPlIwoCZg0h0EmHlQoJC08uV/O+jvg"#; + + let mut client_registration = Registration::new(); + let opaque_registration_request = + client_registration.start(&password).unwrap(); + let registration_start_request = RegistrationStartRequest { + opaque_registration_request, + username: username.to_string(), + device_key_upload: Some(DeviceKeyUpload { + device_key_info: Some(IdentityKeyInfo { + payload: example_payload.to_string(), + payload_signature: "foo".to_string(), + social_proof: None, + }), + content_upload: Some(PreKey { + pre_key: "content_prekey".to_string(), + pre_key_signature: "content_prekey_sig".to_string(), + }), + notif_upload: Some(PreKey { + pre_key: "notif_prekey".to_string(), + pre_key_signature: "notif_prekey_sig".to_string(), + }), + onetime_content_prekeys: Vec::new(), + onetime_notif_prekeys: Vec::new(), + }), + }; + + // TODO: allow endpoint to be configured + let mut identity_client = + IdentityClientServiceClient::connect("http://127.0.0.1:50054") + .await + .expect("Couldn't connect to identitiy service"); + + let registration_start_response = identity_client + .register_password_user_start(registration_start_request) + .await + .unwrap() + .into_inner(); + + let opaque_registration_upload = client_registration + .finish( + &password, + ®istration_start_response.opaque_registration_response, + ) + .unwrap(); + let registration_finish_request = RegistrationFinishRequest { + session_id: registration_start_response.session_id, + opaque_registration_upload, + }; + + let registration_finish_response = identity_client + .register_password_user_finish(registration_finish_request) + .await + .unwrap() + .into_inner(); + + return DeviceInfo { + username: username.to_string(), + device_id: device_id.to_string(), + user_id: registration_finish_response.user_id, + access_token: registration_finish_response.access_token, + }; +} + +#[tokio::test] +async fn verify_access_token() { + let device_info = create_device().await; + + let mut identity_client = + IdentityClientServiceClient::connect("http://127.0.0.1:50054") + .await + .expect("Couldn't connect to identitiy service"); + + let verify_request = VerifyUserAccessTokenRequest { + user_id: device_info.user_id, + signing_public_key: device_info.device_id, + access_token: device_info.access_token, + }; + + let response = identity_client + .verify_user_access_token(verify_request) + .await + .unwrap(); + + assert_eq!(response.into_inner().token_valid, true); +} diff --git a/services/identity/src/client_service.rs b/services/identity/src/client_service.rs --- a/services/identity/src/client_service.rs +++ b/services/identity/src/client_service.rs @@ -766,20 +766,16 @@ request: tonic::Request, ) -> Result, tonic::Status> { let message = request.into_inner(); - let token_valid = match self + let token_valid = self .client - .get_access_token_data(message.user_id, message.signing_public_key) + .verify_access_token( + message.user_id, + message.signing_public_key, + message.access_token, + ) .await - { - Ok(Some(access_token_data)) => { - constant_time_eq( - access_token_data.access_token.as_bytes(), - message.access_token.as_bytes(), - ) && access_token_data.is_valid() - } - Ok(None) => false, - Err(e) => return Err(handle_db_error(e)), - }; + .map_err(handle_db_error)?; + let response = Response::new(VerifyUserAccessTokenResponse { token_valid }); Ok(response) } diff --git a/services/identity/src/database.rs b/services/identity/src/database.rs --- a/services/identity/src/database.rs +++ b/services/identity/src/database.rs @@ -1,3 +1,4 @@ +use constant_time_eq::constant_time_eq; use std::collections::HashMap; use std::fmt::{Display, Formatter, Result as FmtResult}; use std::str::FromStr; @@ -369,6 +370,26 @@ } } + pub async fn verify_access_token( + &self, + user_id: String, + signing_public_key: String, + access_token_to_verify: String, + ) -> Result { + let is_valid = self + .get_access_token_data(user_id, signing_public_key) + .await? + .map(|access_token_data| { + constant_time_eq( + access_token_data.access_token.as_bytes(), + access_token_to_verify.as_bytes(), + ) && access_token_data.is_valid() + }) + .unwrap_or(false); + + Ok(is_valid) + } + pub async fn put_access_token_data( &self, access_token_data: AccessTokenData,