Changeset View
Changeset View
Standalone View
Standalone View
services/identity/src/service.rs
use aws_sdk_dynamodb::output::GetItemOutput; | use aws_sdk_dynamodb::output::GetItemOutput; | ||||
use aws_sdk_dynamodb::Error as DynamoDBError; | use aws_sdk_dynamodb::Error as DynamoDBError; | ||||
use chrono::Utc; | use chrono::Utc; | ||||
use comm_opaque::Cipher; | use comm_opaque::Cipher; | ||||
use constant_time_eq::constant_time_eq; | use constant_time_eq::constant_time_eq; | ||||
use futures_core::Stream; | use futures_core::Stream; | ||||
use opaque_ke::{ | use opaque_ke::{ServerLogin, ServerRegistration}; | ||||
CredentialFinalization, CredentialRequest, | |||||
RegistrationRequest as PakeRegistrationRequest, ServerLogin, | |||||
ServerLoginStartParameters, | |||||
}; | |||||
use opaque_ke::{RegistrationUpload, ServerRegistration}; | |||||
use rand::rngs::OsRng; | use rand::rngs::OsRng; | ||||
use rand::{CryptoRng, Rng}; | use rand::{CryptoRng, Rng}; | ||||
use siwe::Message; | use siwe::Message; | ||||
use std::collections::{HashMap, HashSet}; | use std::collections::{HashMap, HashSet}; | ||||
use std::pin::Pin; | use std::pin::Pin; | ||||
use tokio::sync::mpsc; | use tokio::sync::mpsc; | ||||
use tokio_stream::{wrappers::ReceiverStream, StreamExt}; | use tokio_stream::{wrappers::ReceiverStream, StreamExt}; | ||||
use tonic::{Request, Response, Status}; | use tonic::{Request, Response, Status}; | ||||
use tracing::{error, info, instrument}; | use tracing::{error, info, instrument}; | ||||
use crate::config::CONFIG; | |||||
use crate::constants::MPSC_CHANNEL_BUFFER_CAPACITY; | use crate::constants::MPSC_CHANNEL_BUFFER_CAPACITY; | ||||
use crate::database::{DatabaseClient, Error as DBError}; | use crate::database::{DatabaseClient, Error as DBError}; | ||||
use crate::nonce::generate_nonce_data; | use crate::nonce::generate_nonce_data; | ||||
use crate::pake_grpc; | |||||
use crate::token::{AccessTokenData, AuthType}; | use crate::token::{AccessTokenData, AuthType}; | ||||
pub use proto::identity_service_server::IdentityServiceServer; | pub use proto::identity_service_server::IdentityServiceServer; | ||||
use proto::{ | use proto::{ | ||||
get_user_id_request::AuthType as ProtoAuthType, | get_user_id_request::AuthType as ProtoAuthType, | ||||
identity_service_server::IdentityService, | identity_service_server::IdentityService, | ||||
login_request::Data::PakeLoginRequest, | login_request::Data::PakeLoginRequest, | ||||
login_request::Data::WalletLoginRequest, | login_request::Data::WalletLoginRequest, | ||||
▲ Show 20 Lines • Show All 411 Lines • ▼ Show 20 Lines | ) -> Result<LoginResponseAndPakeState, Status> { | ||||
let server_registration = | let server_registration = | ||||
match client.get_pake_registration(user_id.to_string()).await { | match client.get_pake_registration(user_id.to_string()).await { | ||||
Ok(Some(r)) => r, | Ok(Some(r)) => r, | ||||
Ok(None) => { | Ok(None) => { | ||||
return Err(Status::not_found("user not found")); | return Err(Status::not_found("user not found")); | ||||
} | } | ||||
Err(e) => return Err(handle_db_error(e)), | Err(e) => return Err(handle_db_error(e)), | ||||
}; | }; | ||||
let credential_request = | let server_login_start_result = pake_grpc::server_login_start( | ||||
CredentialRequest::deserialize(pake_credential_request).map_err(|e| { | |||||
error!("Failed to deserialize credential request: {}", e); | |||||
Status::invalid_argument("invalid message") | |||||
})?; | |||||
match ServerLogin::start( | |||||
&mut OsRng, | &mut OsRng, | ||||
server_registration, | server_registration, | ||||
CONFIG.server_keypair.private(), | pake_credential_request, | ||||
credential_request, | )?; | ||||
ServerLoginStartParameters::default(), | let credential_response = | ||||
) { | |||||
Ok(server_login_start_result) => Ok(LoginResponseAndPakeState { | |||||
response: PakeLoginResponseStruct { | |||||
data: Some(PakeCredentialResponse( | |||||
server_login_start_result.message.serialize().map_err(|e| { | server_login_start_result.message.serialize().map_err(|e| { | ||||
error!("Failed to serialize PAKE message: {}", e); | error!("Failed to serialize PAKE message: {}", e); | ||||
Status::failed_precondition("internal error") | Status::failed_precondition("internal error") | ||||
})?, | })?; | ||||
)), | Ok(LoginResponseAndPakeState { | ||||
response: PakeLoginResponseStruct { | |||||
data: Some(PakeCredentialResponse(credential_response)), | |||||
}, | }, | ||||
pake_state: server_login_start_result.state, | pake_state: server_login_start_result.state, | ||||
}), | }) | ||||
Err(e) => { | |||||
error!( | |||||
"Encountered a PAKE protocol error when starting login: {}", | |||||
e | |||||
); | |||||
Err(Status::aborted("server error")) | |||||
} | |||||
} | |||||
} | } | ||||
async fn pake_login_finish( | async fn pake_login_finish( | ||||
user_id: &str, | user_id: &str, | ||||
signing_public_key: &str, | signing_public_key: &str, | ||||
client: &DatabaseClient, | client: &DatabaseClient, | ||||
server_login: ServerLogin<Cipher>, | server_login: ServerLogin<Cipher>, | ||||
pake_credential_finalization: &[u8], | pake_credential_finalization: &Vec<u8>, | ||||
rng: &mut (impl Rng + CryptoRng), | rng: &mut (impl Rng + CryptoRng), | ||||
pake_workflow: PakeWorkflow, | pake_workflow: PakeWorkflow, | ||||
session_initialization_info: &HashMap<String, String>, | session_initialization_info: &HashMap<String, String>, | ||||
) -> Result<PakeLoginResponseStruct, Status> { | ) -> Result<PakeLoginResponseStruct, Status> { | ||||
if user_id.is_empty() || signing_public_key.is_empty() { | if user_id.is_empty() || signing_public_key.is_empty() { | ||||
error!( | error!( | ||||
"Incomplete data: user ID {}, signing public key {}", | "Incomplete data: user ID {}, signing public key {}", | ||||
user_id, signing_public_key | user_id, signing_public_key | ||||
); | ); | ||||
return Err(Status::aborted("user not found")); | return Err(Status::aborted("user not found")); | ||||
} | } | ||||
server_login | |||||
.finish( | pake_grpc::server_login_finish(server_login, pake_credential_finalization)?; | ||||
CredentialFinalization::deserialize(pake_credential_finalization) | |||||
.map_err(|e| { | |||||
error!("Failed to deserialize credential finalization bytes: {}", e); | |||||
Status::aborted("login failed") | |||||
})?, | |||||
) | |||||
.map_err(|e| { | |||||
error!( | |||||
"Encountered a PAKE protocol error when finishing login: {}", | |||||
e | |||||
); | |||||
Status::aborted("server error") | |||||
})?; | |||||
if matches!(pake_workflow, PakeWorkflow::Login) { | if matches!(pake_workflow, PakeWorkflow::Login) { | ||||
client | client | ||||
.update_users_table( | .update_users_table( | ||||
user_id.to_string(), | user_id.to_string(), | ||||
Some(signing_public_key.to_string()), | Some(signing_public_key.to_string()), | ||||
None, | None, | ||||
None, | None, | ||||
Some(session_initialization_info), | Some(session_initialization_info), | ||||
Show All 10 Lines | data: Some(AccessToken( | ||||
signing_public_key, | signing_public_key, | ||||
rng, | rng, | ||||
) | ) | ||||
.await?, | .await?, | ||||
)), | )), | ||||
}) | }) | ||||
} | } | ||||
async fn pake_registration_start( | async fn server_register_response( | ||||
rng: &mut (impl Rng + CryptoRng), | registration_request_bytes: &Vec<u8>, | ||||
registration_request_bytes: &[u8], | |||||
) -> Result<RegistrationResponseAndPakeState, Status> { | ) -> Result<RegistrationResponseAndPakeState, Status> { | ||||
match ServerRegistration::<Cipher>::start( | let server_registration_start_result = pake_grpc::server_registration_start( | ||||
rng, | &mut OsRng, | ||||
PakeRegistrationRequest::deserialize(registration_request_bytes).map_err( | registration_request_bytes, | ||||
|e| { | )?; | ||||
error!("Failed to deserialize registration request bytes: {}", e); | |||||
Status::aborted("registration failed") | |||||
}, | |||||
)?, | |||||
CONFIG.server_keypair.public(), | |||||
) { | |||||
Ok(server_registration_start_result) => { | |||||
Ok(RegistrationResponseAndPakeState { | Ok(RegistrationResponseAndPakeState { | ||||
response: RegistrationResponse { | response: RegistrationResponse { | ||||
data: Some(PakeRegistrationResponse( | data: Some(PakeRegistrationResponse( | ||||
server_registration_start_result.message.serialize(), | server_registration_start_result.message.serialize(), | ||||
)), | )), | ||||
}, | }, | ||||
pake_state: server_registration_start_result.state, | pake_state: server_registration_start_result.state, | ||||
}) | }) | ||||
} | } | ||||
Err(e) => { | |||||
error!( | |||||
"Encountered a PAKE protocol error when starting registration: {}", | |||||
e | |||||
); | |||||
Err(Status::aborted("server error")) | |||||
} | |||||
} | |||||
} | |||||
async fn pake_registration_finish( | async fn pake_registration_finish( | ||||
user_id: &str, | user_id: &str, | ||||
client: &DatabaseClient, | client: &DatabaseClient, | ||||
registration_upload_bytes: &[u8], | registration_upload_bytes: &Vec<u8>, | ||||
server_registration: Option<ServerRegistration<Cipher>>, | server_registration: ServerRegistration<Cipher>, | ||||
username: &str, | username: &str, | ||||
signing_public_key: &str, | signing_public_key: &str, | ||||
session_initialization_info: &HashMap<String, String>, | session_initialization_info: &HashMap<String, String>, | ||||
) -> Result<(), Status> { | ) -> Result<(), Status> { | ||||
if user_id.is_empty() { | if user_id.is_empty() { | ||||
error!("Incomplete data: user ID not provided"); | error!("Incomplete data: user ID not provided"); | ||||
return Err(Status::aborted("user not found")); | return Err(Status::aborted("user not found")); | ||||
} | } | ||||
let server_registration_finish_result = server_registration | let server_registration_finish_result = | ||||
.ok_or_else(|| Status::aborted("registration failed"))? | pake_grpc::server_registration_finish( | ||||
.finish( | server_registration, | ||||
RegistrationUpload::deserialize(registration_upload_bytes).map_err( | registration_upload_bytes, | ||||
|e| { | )?; | ||||
error!("Failed to deserialize registration upload bytes: {}", e); | |||||
Status::aborted("registration failed") | |||||
}, | |||||
)?, | |||||
) | |||||
.map_err(|e| { | |||||
error!( | |||||
"Encountered a PAKE protocol error when finishing registration: {}", | |||||
e | |||||
); | |||||
Status::aborted("server error") | |||||
})?; | |||||
match client | match client | ||||
.add_user_to_users_table( | .add_user_to_users_table( | ||||
user_id.to_string(), | user_id.to_string(), | ||||
server_registration_finish_result, | server_registration_finish_result, | ||||
username.to_string(), | username.to_string(), | ||||
signing_public_key.to_string(), | signing_public_key.to_string(), | ||||
session_initialization_info, | session_initialization_info, | ||||
Show All 33 Lines |