Page MenuHomePhabricator

D9678.diff
No OneTemporary

D9678.diff

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,10 +22,10 @@
RegistrationFinishRequest, RegistrationFinishResponse,
RegistrationStartRequest, RegistrationStartResponse,
RemoveReservedUsernameRequest, ReservedRegistrationStartRequest,
- UpdateUserPasswordFinishRequest, UpdateUserPasswordStartRequest,
- UpdateUserPasswordStartResponse, UploadOneTimeKeysRequest,
- VerifyUserAccessTokenRequest, VerifyUserAccessTokenResponse,
- WalletLoginRequest, WalletLoginResponse,
+ ReservedWalletLoginRequest, UpdateUserPasswordFinishRequest,
+ UpdateUserPasswordStartRequest, UpdateUserPasswordStartResponse,
+ UploadOneTimeKeysRequest, VerifyUserAccessTokenRequest,
+ VerifyUserAccessTokenResponse, WalletLoginRequest, WalletLoginResponse,
};
use crate::config::CONFIG;
use crate::database::{
@@ -122,7 +122,11 @@
return Err(tonic::Status::invalid_argument("username reserved"));
}
- let registration_state = construct_user_registration_info(&message, None)?;
+ let registration_state = construct_user_registration_info(
+ &message,
+ None,
+ message.username.clone(),
+ )?;
let server_registration = comm_opaque2::server::Registration::new();
let server_message = server_registration
.start(
@@ -170,8 +174,11 @@
&message.keyserver_signature,
)?;
- let registration_state =
- construct_user_registration_info(&message, Some(user_id))?;
+ let registration_state = construct_user_registration_info(
+ &message,
+ Some(user_id),
+ message.username.clone(),
+ )?;
let server_registration = comm_opaque2::server::Registration::new();
let server_message = server_registration
.start(
@@ -353,74 +360,28 @@
return Err(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,
- }),
- 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,
- }),
- one_time_content_prekeys,
- one_time_notif_prekeys,
- device_type,
- }),
- } = 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 mut server_login = comm_opaque2::server::Login::new();
+ let server_response = server_login
+ .start(
+ &CONFIG.server_setup,
+ &password_file_bytes,
+ &message.opaque_login_request,
+ 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 {
- user_id,
- opaque_server_login: server_login,
- 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_one_time_keys: one_time_content_prekeys,
- notif_prekey,
- notif_prekey_signature,
- notif_one_time_keys: one_time_notif_prekeys,
- device_type: DeviceType::try_from(DBDeviceTypeInt(device_type))
- .map_err(handle_db_error)?,
- },
- };
- let session_id = self
- .insert_into_cache(WorkflowInProgress::Login(Box::new(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"))
- }
+ let login_state =
+ construct_user_login_info(&message, user_id, server_login)?;
+
+ let session_id = self
+ .insert_into_cache(WorkflowInProgress::Login(Box::new(login_state)))
+ .await;
+
+ let response = Response::new(OpaqueLoginStartResponse {
+ session_id,
+ opaque_login_response: server_response,
+ });
+ Ok(response)
}
async fn login_password_user_finish(
@@ -501,55 +462,12 @@
let wallet_address = eip55(&parsed_message.address);
- let (flattened_device_key_upload, social_proof) =
- if let client_proto::WalletLoginRequest {
- siwe_message: _,
- siwe_signature: _,
- device_key_upload:
- Some(client_proto::DeviceKeyUpload {
- device_key_info:
- Some(client_proto::IdentityKeyInfo {
- payload,
- payload_signature,
- social_proof: Some(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,
- }),
- one_time_content_prekeys,
- one_time_notif_prekeys,
- device_type,
- }),
- } = message
- {
- let key_info = KeyPayload::from_str(&payload)
- .map_err(|_| tonic::Status::invalid_argument("malformed payload"))?;
- (
- 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_one_time_keys: one_time_content_prekeys,
- notif_prekey,
- notif_prekey_signature,
- notif_one_time_keys: one_time_notif_prekeys,
- device_type: DeviceType::try_from(DBDeviceTypeInt(device_type))
- .map_err(handle_db_error)?,
- },
- social_proof,
- )
- } else {
- return Err(tonic::Status::invalid_argument("unexpected message data"));
- };
+ let flattened_device_key_upload =
+ construct_flattened_device_key_upload(&message)?;
+
+ let social_proof = message
+ .social_proof()?
+ .ok_or_else(|| tonic::Status::invalid_argument("malformed payload"))?;
let user_id = match self
.client
@@ -578,6 +496,7 @@
flattened_device_key_upload.clone(),
wallet_address,
social_proof,
+ None,
)
.await
.map_err(handle_db_error)?
@@ -607,6 +526,93 @@
Ok(Response::new(response))
}
+ async fn login_reserved_wallet_user(
+ &self,
+ request: tonic::Request<ReservedWalletLoginRequest>,
+ ) -> Result<tonic::Response<WalletLoginResponse>, tonic::Status> {
+ let message = request.into_inner();
+
+ let parsed_message = parse_and_verify_siwe_message(
+ &message.siwe_message,
+ &message.siwe_signature,
+ )?;
+
+ match self
+ .client
+ .get_nonce_from_nonces_table(&parsed_message.nonce)
+ .await
+ .map_err(handle_db_error)?
+ {
+ None => return Err(tonic::Status::invalid_argument("invalid nonce")),
+ Some(_) => self
+ .client
+ .remove_nonce_from_nonces_table(&parsed_message.nonce)
+ .await
+ .map_err(handle_db_error)?,
+ };
+
+ let wallet_address = eip55(&parsed_message.address);
+
+ self.check_wallet_address_taken(&wallet_address).await?;
+
+ let wallet_address_in_reserved_usernames_table = self
+ .client
+ .username_in_reserved_usernames_table(&wallet_address)
+ .await
+ .map_err(handle_db_error)?;
+ if !wallet_address_in_reserved_usernames_table {
+ return Err(tonic::Status::permission_denied(
+ "wallet address not reserved",
+ ));
+ }
+
+ let user_id = validate_account_ownership_message_and_get_user_id(
+ &wallet_address,
+ &message.keyserver_message,
+ &message.keyserver_signature,
+ )?;
+
+ let flattened_device_key_upload =
+ construct_flattened_device_key_upload(&message)?;
+
+ let social_proof = message
+ .social_proof()?
+ .ok_or_else(|| tonic::Status::invalid_argument("malformed payload"))?;
+
+ self
+ .client
+ .add_wallet_user_to_users_table(
+ flattened_device_key_upload.clone(),
+ wallet_address,
+ social_proof,
+ Some(user_id.clone()),
+ )
+ .await
+ .map_err(handle_db_error)?;
+
+ let token = AccessTokenData::new(
+ user_id.clone(),
+ flattened_device_key_upload.device_id_key,
+ crate::token::AuthType::Password,
+ &mut OsRng,
+ );
+
+ let access_token = token.access_token.clone();
+
+ self
+ .client
+ .put_access_token_data(token)
+ .await
+ .map_err(handle_db_error)?;
+
+ let response = WalletLoginResponse {
+ user_id,
+ access_token,
+ };
+
+ Ok(Response::new(response))
+ }
+
async fn log_out_user(
&self,
request: tonic::Request<LogoutRequest>,
@@ -929,6 +935,23 @@
Ok(())
}
+ async fn check_wallet_address_taken(
+ &self,
+ wallet_address: &str,
+ ) -> Result<(), tonic::Status> {
+ let wallet_address_taken = self
+ .client
+ .wallet_address_taken(wallet_address.to_string())
+ .await
+ .map_err(handle_db_error)?;
+ if wallet_address_taken {
+ return Err(tonic::Status::already_exists(
+ "wallet address already exists",
+ ));
+ }
+ Ok(())
+ }
+
async fn insert_into_cache(&self, workflow: WorkflowInProgress) -> String {
let session_id = generate_uuid();
self.cache.insert(session_id.clone(), workflow).await;
@@ -955,27 +978,50 @@
fn construct_user_registration_info(
message: &impl DeviceKeyUploadActions,
user_id: Option<String>,
+ username: String,
) -> Result<UserRegistrationInfo, tonic::Status> {
+ Ok(UserRegistrationInfo {
+ username,
+ flattened_device_key_upload: construct_flattened_device_key_upload(
+ message,
+ )?,
+ user_id,
+ })
+}
+
+fn construct_user_login_info(
+ message: &impl DeviceKeyUploadActions,
+ user_id: String,
+ opaque_server_login: comm_opaque2::server::Login,
+) -> Result<UserLoginInfo, tonic::Status> {
+ Ok(UserLoginInfo {
+ user_id,
+ flattened_device_key_upload: construct_flattened_device_key_upload(
+ message,
+ )?,
+ opaque_server_login,
+ })
+}
+
+fn construct_flattened_device_key_upload(
+ message: &impl DeviceKeyUploadActions,
+) -> Result<FlattenedDeviceKeyUpload, tonic::Status> {
let key_info = KeyPayload::from_str(&message.payload()?)
.map_err(|_| tonic::Status::invalid_argument("malformed payload"))?;
- Ok(UserRegistrationInfo {
- username: message.username(),
- flattened_device_key_upload: FlattenedDeviceKeyUpload {
- device_id_key: key_info.primary_identity_public_keys.ed25519,
- key_payload: message.payload()?,
- key_payload_signature: message.payload_signature()?,
- content_prekey: message.content_prekey()?,
- content_prekey_signature: message.content_prekey_signature()?,
- content_one_time_keys: message.one_time_content_prekeys()?,
- notif_prekey: message.notif_prekey()?,
- notif_prekey_signature: message.notif_prekey_signature()?,
- notif_one_time_keys: message.one_time_notif_prekeys()?,
- device_type: DeviceType::try_from(DBDeviceTypeInt(
- message.device_type()?,
- ))
+ let flattened_device_key_upload = FlattenedDeviceKeyUpload {
+ device_id_key: key_info.primary_identity_public_keys.ed25519,
+ key_payload: message.payload()?,
+ key_payload_signature: message.payload_signature()?,
+ content_prekey: message.content_prekey()?,
+ content_prekey_signature: message.content_prekey_signature()?,
+ content_one_time_keys: message.one_time_content_prekeys()?,
+ notif_prekey: message.notif_prekey()?,
+ notif_prekey_signature: message.notif_prekey_signature()?,
+ notif_one_time_keys: message.one_time_notif_prekeys()?,
+ device_type: DeviceType::try_from(DBDeviceTypeInt(message.device_type()?))
.map_err(handle_db_error)?,
- },
- user_id,
- })
+ };
+
+ Ok(flattened_device_key_upload)
}
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
@@ -154,6 +154,7 @@
flattened_device_key_upload: FlattenedDeviceKeyUpload,
wallet_address: String,
social_proof: String,
+ user_id: Option<String>,
) -> Result<String, Error> {
self
.add_user_to_users_table(
@@ -161,7 +162,7 @@
None,
Some(wallet_address),
Some(social_proof),
- None,
+ user_id,
)
.await
}
@@ -843,6 +844,17 @@
Ok(())
}
+ pub async fn wallet_address_taken(
+ &self,
+ wallet_address: String,
+ ) -> Result<bool, Error> {
+ let result = self
+ .get_user_id_from_user_info(wallet_address, &AuthType::Wallet)
+ .await?;
+
+ Ok(result.is_some())
+ }
+
pub async fn username_taken(&self, username: String) -> Result<bool, Error> {
let result = self
.get_user_id_from_user_info(username, &AuthType::Password)
diff --git a/services/identity/src/grpc_utils.rs b/services/identity/src/grpc_utils.rs
--- a/services/identity/src/grpc_utils.rs
+++ b/services/identity/src/grpc_utils.rs
@@ -5,8 +5,10 @@
use crate::{
client_service::client_proto::{
- DeviceKeyUpload, IdentityKeyInfo, InboundKeyInfo, OutboundKeyInfo, PreKey,
- RegistrationStartRequest, ReservedRegistrationStartRequest,
+ DeviceKeyUpload, IdentityKeyInfo, InboundKeyInfo, OpaqueLoginStartRequest,
+ OutboundKeyInfo, PreKey, RegistrationStartRequest,
+ ReservedRegistrationStartRequest, ReservedWalletLoginRequest,
+ WalletLoginRequest,
},
constants::{
CONTENT_ONE_TIME_KEY, NOTIF_ONE_TIME_KEY,
@@ -131,29 +133,39 @@
pub trait DeviceKeyUploadData {
fn device_key_upload(&self) -> Option<&DeviceKeyUpload>;
- fn username(&self) -> &str;
}
impl DeviceKeyUploadData for RegistrationStartRequest {
fn device_key_upload(&self) -> Option<&DeviceKeyUpload> {
self.device_key_upload.as_ref()
}
- fn username(&self) -> &str {
- &self.username
- }
}
impl DeviceKeyUploadData for ReservedRegistrationStartRequest {
fn device_key_upload(&self) -> Option<&DeviceKeyUpload> {
self.device_key_upload.as_ref()
}
- fn username(&self) -> &str {
- &self.username
+}
+
+impl DeviceKeyUploadData for OpaqueLoginStartRequest {
+ fn device_key_upload(&self) -> Option<&DeviceKeyUpload> {
+ self.device_key_upload.as_ref()
+ }
+}
+
+impl DeviceKeyUploadData for WalletLoginRequest {
+ fn device_key_upload(&self) -> Option<&DeviceKeyUpload> {
+ self.device_key_upload.as_ref()
+ }
+}
+
+impl DeviceKeyUploadData for ReservedWalletLoginRequest {
+ fn device_key_upload(&self) -> Option<&DeviceKeyUpload> {
+ self.device_key_upload.as_ref()
}
}
pub trait DeviceKeyUploadActions {
- fn username(&self) -> String;
fn payload(&self) -> Result<String, Status>;
fn payload_signature(&self) -> Result<String, Status>;
fn content_prekey(&self) -> Result<String, Status>;
@@ -163,13 +175,10 @@
fn one_time_content_prekeys(&self) -> Result<Vec<String>, Status>;
fn one_time_notif_prekeys(&self) -> Result<Vec<String>, Status>;
fn device_type(&self) -> Result<i32, Status>;
+ fn social_proof(&self) -> Result<Option<String>, Status>;
}
impl<T: DeviceKeyUploadData> DeviceKeyUploadActions for T {
- fn username(&self) -> String {
- self.username().to_string()
- }
-
fn payload(&self) -> Result<String, Status> {
self
.device_key_upload()
@@ -237,4 +246,11 @@
.map(|upload| upload.device_type)
.ok_or_else(|| Status::invalid_argument("unexpected message data"))
}
+ fn social_proof(&self) -> Result<Option<String>, Status> {
+ self
+ .device_key_upload()
+ .and_then(|upload| upload.device_key_info.as_ref())
+ .map(|info| info.social_proof.clone())
+ .ok_or_else(|| Status::invalid_argument("unexpected message data"))
+ }
}
diff --git a/services/identity/src/reserved_users.rs b/services/identity/src/reserved_users.rs
--- a/services/identity/src/reserved_users.rs
+++ b/services/identity/src/reserved_users.rs
@@ -84,6 +84,7 @@
keyserver_message: &str,
keyserver_signature: &str,
) -> Result<String, Status> {
+ // Note that username in this context includes wallet addresses, too.
const EXPECTED_STATEMENT: &[u8; 60] =
b"This user is the owner of the following username and user ID";
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
@@ -28,6 +28,8 @@
rpc LoginPasswordUserFinish(OpaqueLoginFinishRequest) returns
(OpaqueLoginFinishResponse) {}
rpc LoginWalletUser(WalletLoginRequest) returns (WalletLoginResponse) {}
+ rpc LoginReservedWalletUser(ReservedWalletLoginRequest) returns
+ (WalletLoginResponse) {}
// Called by user to log out (clears device's keys and access token)
rpc LogOutUser(LogoutRequest) returns (Empty) {}
// Called by a user to delete their own account
@@ -245,6 +247,19 @@
DeviceKeyUpload deviceKeyUpload = 3;
}
+message ReservedWalletLoginRequest {
+ string siweMessage = 1;
+ string siweSignature = 2;
+ // Information specific to a user's device needed to open a new channel of
+ // communication with this user
+ DeviceKeyUpload deviceKeyUpload = 3;
+ // Message from Ashoat's keyserver attesting that a given user has ownership
+ // of a given wallet address
+ string keyserverMessage = 4;
+ // Above message signed with Ashoat's keyserver's signing ed25519 key
+ string keyserverSignature = 5;
+}
+
message WalletLoginResponse {
string userID = 1;
string accessToken = 2;

File Metadata

Mime Type
text/plain
Expires
Tue, Dec 3, 3:54 AM (21 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2610115
Default Alt Text
D9678.diff (18 KB)

Event Timeline