Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3399969
D9678.id32656.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
D9678.id32656.diff
View Options
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,28 @@
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 key_info = KeyPayload::from_str(&message.payload()?)
+ .map_err(|_| tonic::Status::invalid_argument("malformed payload"))?;
+
+ 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)?,
+ };
+
+ let social_proof = message
+ .social_proof()?
+ .ok_or_else(|| tonic::Status::invalid_argument("malformed payload"))?;
let user_id = match self
.client
@@ -578,6 +512,7 @@
flattened_device_key_upload.clone(),
wallet_address,
social_proof,
+ None,
)
.await
.map_err(handle_db_error)?
@@ -607,6 +542,109 @@
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 key_info = KeyPayload::from_str(&message.payload()?)
+ .map_err(|_| tonic::Status::invalid_argument("malformed payload"))?;
+
+ 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)?,
+ };
+
+ 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 +967,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,12 +1010,13 @@
fn construct_user_registration_info(
message: &impl DeviceKeyUploadActions,
user_id: Option<String>,
+ username: String,
) -> Result<UserRegistrationInfo, tonic::Status> {
let key_info = KeyPayload::from_str(&message.payload()?)
.map_err(|_| tonic::Status::invalid_argument("malformed payload"))?;
Ok(UserRegistrationInfo {
- username: message.username(),
+ username,
flattened_device_key_upload: FlattenedDeviceKeyUpload {
device_id_key: key_info.primary_identity_public_keys.ed25519,
key_payload: message.payload()?,
@@ -979,3 +1035,32 @@
user_id,
})
}
+
+fn construct_user_login_info(
+ message: &impl DeviceKeyUploadActions,
+ user_id: String,
+ opaque_server_login: comm_opaque2::server::Login,
+) -> Result<UserLoginInfo, tonic::Status> {
+ let key_info = KeyPayload::from_str(&message.payload()?)
+ .map_err(|_| tonic::Status::invalid_argument("malformed payload"))?;
+
+ Ok(UserLoginInfo {
+ user_id,
+ 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)?,
+ },
+ opaque_server_login,
+ })
+}
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
Details
Attached
Mime Type
text/plain
Expires
Tue, Dec 3, 5:36 AM (20 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2610417
Default Alt Text
D9678.id32656.diff (19 KB)
Attached To
Mode
D9678: [identity] add RPC for logging in users with reserved wallet addresses
Attached
Detach File
Event Timeline
Log In to Comment