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 @@ -29,6 +29,7 @@ pub use client_proto::identity_client_service_server::{ IdentityClientService, IdentityClientServiceServer, }; +use comm_opaque2::grpc::protocol_error_to_grpc_status; use moka::future::Cache; use rand::rngs::OsRng; use tonic::Response; @@ -47,7 +48,10 @@ } #[derive(Clone)] -pub struct UserLoginInfo(FlattenedDeviceKeyUpload); +pub struct UserLoginInfo { + pub flattened_device_key_upload: FlattenedDeviceKeyUpload, + pub opaque_server_login: comm_opaque2::server::Login, +} #[derive(Clone)] pub struct FlattenedDeviceKeyUpload { @@ -118,7 +122,7 @@ let server_registration = comm_opaque2::server::Registration::new(); let server_message = server_registration .start(&CONFIG.server_setup, ®ister_message, username.as_bytes()) - .map_err(comm_opaque2::grpc::protocol_error_to_grpc_status)?; + .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 { @@ -168,7 +172,7 @@ let server_registration = comm_opaque2::server::Registration::new(); let password_file = server_registration .finish(&message.opaque_registration_upload) - .map_err(comm_opaque2::grpc::protocol_error_to_grpc_status)?; + .map_err(protocol_error_to_grpc_status)?; let device_id = state.flattened_device_key_upload.device_id_key.clone(); let user_id = self @@ -221,9 +225,83 @@ async fn login_password_user_start( &self, - _request: tonic::Request, + request: tonic::Request, ) -> Result, tonic::Status> { - unimplemented!(); + let message = request.into_inner(); + + let password_file_bytes = self + .client + .get_password_file_from_username(&message.username) + .await + .map_err(handle_db_error)? + .ok_or(tonic::Status::not_found("user not found"))?; + + if let client_proto::OpaqueLoginStartRequest { + opaque_login_request: login_message, + username, + device_key_upload: + Some(client_proto::DeviceKeyUpload { + device_key_info: + Some(client_proto::IdentityKeyInfo { + payload, + payload_signature, + social_proof: _social_proof, + }), + identity_upload: + Some(client_proto::PreKey { + pre_key: identity_prekey, + pre_key_signature: identity_prekey_signature, + }), + notif_upload: + Some(client_proto::PreKey { + pre_key: notif_prekey, + pre_key_signature: notif_prekey_signature, + }), + onetime_identity_prekeys, + onetime_notif_prekeys, + }), + } = message + { + let mut server_login = comm_opaque2::server::Login::new(); + let server_response = server_login + .start( + &CONFIG.server_setup, + &password_file_bytes, + &login_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 login_state = UserLoginInfo { + opaque_server_login: server_login, + flattened_device_key_upload: FlattenedDeviceKeyUpload { + device_id_key: key_info.primary_identity_public_keys.curve25519, + key_payload: payload, + key_payload_signature: payload_signature, + identity_prekey, + identity_prekey_signature, + identity_onetime_keys: onetime_identity_prekeys, + notif_prekey, + notif_prekey_signature, + notif_onetime_keys: onetime_notif_prekeys, + }, + }; + let session_id = generate_uuid(); + self + .cache + .insert(session_id.clone(), WorkflowInProgress::Login(login_state)) + .await; + + let response = Response::new(OpaqueLoginStartResponse { + session_id, + opaque_login_response: server_response, + }); + Ok(response) + } else { + Err(tonic::Status::invalid_argument("unexpected message data")) + } } async fn login_password_user_finish( 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 @@ -105,36 +105,6 @@ } } - pub async fn get_pake_registration( - &self, - user_id: String, - ) -> Result>, Error> { - match self.get_item_from_users_table(&user_id).await { - Ok(GetItemOutput { - item: Some(mut item), - .. - }) => parse_registration_data_attribute( - item.remove(USERS_TABLE_REGISTRATION_ATTRIBUTE), - ) - .map(Some) - .map_err(Error::Attribute), - Ok(_) => { - info!( - "No item found for user {} in PAKE registration table", - user_id - ); - Ok(None) - } - Err(e) => { - error!( - "DynamoDB client failed to get registration data for user {}: {}", - user_id, e - ); - Err(e) - } - } - } - pub async fn add_user_to_users_table( &self, registration_state: UserRegistrationInfo, @@ -397,11 +367,11 @@ Ok(result.is_some()) } - pub async fn get_user_id_from_user_info( + async fn get_user_from_user_info( &self, user_info: String, auth_type: AuthType, - ) -> Result, Error> { + ) -> Result>, Error> { let (index, attribute_name) = match auth_type { AuthType::Password => { (USERS_TABLE_USERNAME_INDEX, USERS_TABLE_USERNAME_ATTRIBUTE) @@ -422,8 +392,7 @@ .await { Ok(QueryOutput { - items: Some(mut items), - .. + items: Some(items), .. }) => { let num_items = items.len(); if num_items == 0 { @@ -435,12 +404,8 @@ num_items, attribute_name, user_info, items ); } - parse_string_attribute( - USERS_TABLE_PARTITION_KEY, - items[0].remove(USERS_TABLE_PARTITION_KEY), - ) - .map(Some) - .map_err(Error::Attribute) + let first_item = items[0].clone(); + Ok(Some(first_item)) } Ok(_) => { info!( @@ -451,7 +416,7 @@ } Err(e) => { error!( - "DynamoDB client failed to get user ID from {} {}: {}", + "DynamoDB client failed to get user from {} {}: {}", attribute_name, user_info, e ); Err(Error::AwsSdk(e.into())) @@ -459,6 +424,56 @@ } } + pub async fn get_user_id_from_user_info( + &self, + user_info: String, + auth_type: AuthType, + ) -> Result, Error> { + match self + .get_user_from_user_info(user_info.clone(), auth_type) + .await + { + Ok(Some(mut user)) => parse_string_attribute( + USERS_TABLE_PARTITION_KEY, + user.remove(USERS_TABLE_PARTITION_KEY), + ) + .map(Some) + .map_err(Error::Attribute), + Ok(_) => Ok(None), + Err(e) => Err(e), + } + } + + pub async fn get_password_file_from_username( + &self, + username: &str, + ) -> Result>, Error> { + match self + .get_user_from_user_info(username.to_string(), AuthType::Password) + .await + { + Ok(Some(mut user)) => parse_registration_data_attribute( + user.remove(USERS_TABLE_REGISTRATION_ATTRIBUTE), + ) + .map(Some) + .map_err(Error::Attribute), + Ok(_) => { + info!( + "No item found for user {} in PAKE registration table", + username + ); + Ok(None) + } + Err(e) => { + error!( + "DynamoDB client failed to get registration data for user {}: {}", + username, e + ); + Err(e) + } + } + } + pub async fn get_item_from_users_table( &self, user_id: &str, @@ -673,19 +688,10 @@ fn parse_registration_data_attribute( attribute: Option, -) -> Result, DBItemError> { - match &attribute { +) -> Result, DBItemError> { + match attribute { Some(AttributeValue::B(server_registration_bytes)) => { - match ServerRegistration::::deserialize( - server_registration_bytes.as_ref(), - ) { - Ok(server_registration) => Ok(server_registration), - Err(e) => Err(DBItemError::new( - USERS_TABLE_REGISTRATION_ATTRIBUTE, - attribute, - DBItemAttributeError::Pake(e), - )), - } + Ok(server_registration_bytes.into_inner()) } Some(_) => Err(DBItemError::new( USERS_TABLE_REGISTRATION_ATTRIBUTE, diff --git a/shared/comm-opaque2/src/server/login.rs b/shared/comm-opaque2/src/server/login.rs --- a/shared/comm-opaque2/src/server/login.rs +++ b/shared/comm-opaque2/src/server/login.rs @@ -7,6 +7,7 @@ use crate::Cipher; +#[derive(Clone)] pub struct Login { state: Option>, rng: OsRng,