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 @@ -22,6 +22,7 @@ database::{DatabaseClient, Error as DBError, KeyPayload}, id::generate_uuid, nonce::generate_nonce_data, + reserved_users::validate_signed_keyserver_message, siwe::parse_and_verify_siwe_message, token::{AccessTokenData, AuthType}, }; @@ -36,6 +37,8 @@ use tonic::Response; use tracing::error; +use self::client_proto::ReservedRegistrationStartRequest; + #[derive(Clone)] pub enum WorkflowInProgress { Registration(UserRegistrationInfo), @@ -166,6 +169,106 @@ } } + async fn register_reserved_password_user_start( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let message = request.into_inner(); + let username_taken = self + .client + .username_taken(message.username.clone()) + .await + .map_err(handle_db_error)?; + + if username_taken { + return Err(tonic::Status::already_exists("username already exists")); + } + + if CONFIG.reserved_usernames.contains(&message.username) { + return Err(tonic::Status::invalid_argument("username reserved")); + } + + let username_in_reserved_usernames_table = self + .client + .username_in_reserved_usernames_table(&message.username) + .await + .map_err(handle_db_error)?; + if username_in_reserved_usernames_table { + validate_signed_keyserver_message( + &message.username, + &message.keyserver_message, + &message.keyserver_signature, + )?; + } else { + return Err(tonic::Status::permission_denied("username not reserved")); + } + + if let client_proto::ReservedRegistrationStartRequest { + opaque_registration_request: register_message, + username, + device_key_upload: + Some(client_proto::DeviceKeyUpload { + device_key_info: + Some(client_proto::IdentityKeyInfo { + payload, + payload_signature, + social_proof: _social_proof, + }), + content_upload: + Some(client_proto::PreKey { + pre_key: content_prekey, + pre_key_signature: content_prekey_signature, + }), + notif_upload: + Some(client_proto::PreKey { + pre_key: notif_prekey, + pre_key_signature: notif_prekey_signature, + }), + onetime_content_prekeys, + onetime_notif_prekeys, + }), + .. + } = message + { + let server_registration = comm_opaque2::server::Registration::new(); + let server_message = server_registration + .start(&CONFIG.server_setup, ®ister_message, username.as_bytes()) + .map_err(protocol_error_to_grpc_status)?; + let key_info = KeyPayload::from_str(&payload) + .map_err(|_| tonic::Status::invalid_argument("malformed payload"))?; + let registration_state = UserRegistrationInfo { + username, + flattened_device_key_upload: FlattenedDeviceKeyUpload { + device_id_key: key_info.primary_identity_public_keys.ed25519, + key_payload: payload, + key_payload_signature: payload_signature, + content_prekey, + content_prekey_signature, + content_onetime_keys: onetime_content_prekeys, + notif_prekey, + notif_prekey_signature, + notif_onetime_keys: onetime_notif_prekeys, + }, + }; + let session_id = generate_uuid(); + self + .cache + .insert( + session_id.clone(), + WorkflowInProgress::Registration(registration_state), + ) + .await; + + let response = RegistrationStartResponse { + session_id, + opaque_registration_response: server_message, + }; + Ok(Response::new(response)) + } else { + Err(tonic::Status::invalid_argument("unexpected message data")) + } + } + async fn register_password_user_finish( &self, request: tonic::Request, diff --git a/shared/protos/identity_client.proto b/shared/protos/identity_client.proto --- a/shared/protos/identity_client.proto +++ b/shared/protos/identity_client.proto @@ -13,6 +13,8 @@ // Start/Finish is used here to align with opaque protocol rpc RegisterPasswordUserStart(RegistrationStartRequest) returns ( RegistrationStartResponse) {} + rpc RegisterReservedPasswordUserStart(ReservedRegistrationStartRequest) + returns (RegistrationStartResponse) {} rpc RegisterPasswordUserFinish(RegistrationFinishRequest) returns ( RegistrationFinishResponse) {} // Called by user to update password and receive new access token @@ -115,6 +117,19 @@ DeviceKeyUpload deviceKeyUpload = 3; } +message ReservedRegistrationStartRequest { + // Message sent to initiate PAKE registration (step 1) + bytes opaqueRegistrationRequest = 1; + string username = 2; + // Information needed to open a new channel to current user's device + DeviceKeyUpload deviceKeyUpload = 3; + // Message from Ashoat's keyserver attesting that a given user has ownership + // of a given username + string keyserverMessage = 4; + // Above message signed with Ashoat's keyserver's signing ed25519 key + string keyserverSignature = 5; +} + // Messages sent from a client to Identity Service message RegistrationFinishRequest { // Identifier to correlate RegisterStart session