Page MenuHomePhabricator

D7540.diff
No OneTemporary

D7540.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
@@ -23,7 +23,8 @@
database::{DatabaseClient, Error as DBError, KeyPayload},
id::generate_uuid,
nonce::generate_nonce_data,
- token::AccessTokenData,
+ siwe::parse_and_verify_siwe_message,
+ token::{AccessTokenData, AuthType},
};
use aws_sdk_dynamodb::Error as DynamoDBError;
pub use client_proto::identity_client_service_server::{
@@ -178,13 +179,13 @@
let device_id = state.flattened_device_key_upload.device_id_key.clone();
let user_id = self
.client
- .add_user_to_users_table(state, password_file)
+ .add_password_user_to_users_table(state, password_file)
.await
.map_err(handle_db_error)?;
// Create access token
let token = AccessTokenData::new(
- message.session_id,
+ user_id.clone(),
device_id,
crate::token::AuthType::Password,
&mut OsRng,
@@ -324,13 +325,16 @@
self
.client
- .add_device_to_users_table(state.clone())
+ .add_password_user_device_to_users_table(
+ state.user_id.clone(),
+ state.flattened_device_key_upload.clone(),
+ )
.await
.map_err(handle_db_error)?;
// Create access token
let token = AccessTokenData::new(
- message.session_id,
+ state.user_id.clone(),
state.flattened_device_key_upload.device_id_key,
crate::token::AuthType::Password,
&mut OsRng,
@@ -356,9 +360,116 @@
async fn login_wallet_user(
&self,
- _request: tonic::Request<WalletLoginRequest>,
+ request: tonic::Request<WalletLoginRequest>,
) -> Result<tonic::Response<WalletLoginResponse>, tonic::Status> {
- unimplemented!();
+ let message = request.into_inner();
+
+ let wallet_address = parse_and_verify_siwe_message(
+ &message.siwe_message,
+ &message.siwe_signature,
+ )?;
+
+ 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),
+ }),
+ 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 key_info = KeyPayload::from_str(&payload)
+ .map_err(|_| tonic::Status::invalid_argument("malformed payload"))?;
+ (
+ 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,
+ },
+ social_proof,
+ )
+ } else {
+ return Err(tonic::Status::invalid_argument("unexpected message data"));
+ };
+
+ let user_id = match self
+ .client
+ .get_user_id_from_user_info(wallet_address.clone(), AuthType::Wallet)
+ .await
+ .map_err(handle_db_error)?
+ {
+ Some(id) => {
+ // User already exists, so we should update the DDB item
+ self
+ .client
+ .add_wallet_user_device_to_users_table(
+ id.clone(),
+ flattened_device_key_upload.clone(),
+ social_proof,
+ )
+ .await
+ .map_err(handle_db_error)?;
+ id
+ }
+ None => {
+ // User doesn't exist yet, so we should add a new user in DDB
+ self
+ .client
+ .add_wallet_user_to_users_table(
+ flattened_device_key_upload.clone(),
+ wallet_address,
+ social_proof,
+ )
+ .await
+ .map_err(handle_db_error)?
+ }
+ };
+
+ // Create access token
+ 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: user_id,
+ access_token,
+ };
+ Ok(Response::new(response))
}
async fn delete_user(
diff --git a/services/identity/src/constants.rs b/services/identity/src/constants.rs
--- a/services/identity/src/constants.rs
+++ b/services/identity/src/constants.rs
@@ -34,6 +34,7 @@
// notifPreKey: String,
// notifPreKeySignature: String,
// notifOneTimeKeys: Vec<String>,
+// socialProof: Option<String>
// }
// }
//
@@ -65,6 +66,8 @@
pub const USERS_TABLE_DEVICES_MAP_NOTIF_ONETIME_KEYS_ATTRIBUTE_NAME: &str =
"notifOneTimeKeys";
pub const USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE: &str = "walletAddress";
+pub const USERS_TABLE_DEVICES_MAP_SOCIAL_PROOF_ATTRIBUTE_NAME: &str =
+ "socialProof";
pub const USERS_TABLE_USERNAME_INDEX: &str = "username-index";
pub const USERS_TABLE_WALLET_ADDRESS_INDEX: &str = "walletAddress-index";
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
@@ -15,7 +15,7 @@
use serde::{Deserialize, Serialize};
use tracing::{debug, error, info, warn};
-use crate::client_service::{UserLoginInfo, UserRegistrationInfo};
+use crate::client_service::{FlattenedDeviceKeyUpload, UserRegistrationInfo};
use crate::config::CONFIG;
use crate::constants::{
ACCESS_TOKEN_SORT_KEY, ACCESS_TOKEN_TABLE,
@@ -33,6 +33,7 @@
USERS_TABLE_DEVICES_MAP_NOTIF_ONETIME_KEYS_ATTRIBUTE_NAME,
USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME,
USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME,
+ USERS_TABLE_DEVICES_MAP_SOCIAL_PROOF_ATTRIBUTE_NAME,
USERS_TABLE_PARTITION_KEY, USERS_TABLE_REGISTRATION_ATTRIBUTE,
USERS_TABLE_USERNAME_ATTRIBUTE, USERS_TABLE_USERNAME_INDEX,
USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE, USERS_TABLE_WALLET_ADDRESS_INDEX,
@@ -104,55 +105,75 @@
}
}
- pub async fn add_user_to_users_table(
+ pub async fn add_password_user_to_users_table(
&self,
registration_state: UserRegistrationInfo,
password_file: Vec<u8>,
+ ) -> Result<String, Error> {
+ self
+ .add_user_to_users_table(
+ registration_state.flattened_device_key_upload,
+ Some((registration_state.username, Blob::new(password_file))),
+ None,
+ None,
+ )
+ .await
+ }
+
+ pub async fn add_wallet_user_to_users_table(
+ &self,
+ flattened_device_key_upload: FlattenedDeviceKeyUpload,
+ wallet_address: String,
+ social_proof: String,
+ ) -> Result<String, Error> {
+ self
+ .add_user_to_users_table(
+ flattened_device_key_upload,
+ None,
+ Some(wallet_address),
+ Some(social_proof),
+ )
+ .await
+ }
+
+ async fn add_user_to_users_table(
+ &self,
+ flattened_device_key_upload: FlattenedDeviceKeyUpload,
+ username_and_password_file: Option<(String, Blob)>,
+ wallet_address: Option<String>,
+ social_proof: Option<String>,
) -> Result<String, Error> {
let user_id = generate_uuid();
- let device_info = HashMap::from([
+ let mut device_info = HashMap::from([
(
USERS_TABLE_DEVICES_MAP_DEVICE_TYPE_ATTRIBUTE_NAME.to_string(),
AttributeValue::S(Device::Client.to_string()),
),
(
USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_ATTRIBUTE_NAME.to_string(),
- AttributeValue::S(
- registration_state.flattened_device_key_upload.key_payload,
- ),
+ AttributeValue::S(flattened_device_key_upload.key_payload),
),
(
USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_SIGNATURE_ATTRIBUTE_NAME
.to_string(),
- AttributeValue::S(
- registration_state
- .flattened_device_key_upload
- .key_payload_signature,
- ),
+ AttributeValue::S(flattened_device_key_upload.key_payload_signature),
),
(
USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_ATTRIBUTE_NAME.to_string(),
- AttributeValue::S(
- registration_state
- .flattened_device_key_upload
- .identity_prekey,
- ),
+ AttributeValue::S(flattened_device_key_upload.identity_prekey),
),
(
USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_SIGNATURE_ATTRIBUTE_NAME
.to_string(),
AttributeValue::S(
- registration_state
- .flattened_device_key_upload
- .identity_prekey_signature,
+ flattened_device_key_upload.identity_prekey_signature,
),
),
(
USERS_TABLE_DEVICES_MAP_IDENTITY_ONETIME_KEYS_ATTRIBUTE_NAME
.to_string(),
AttributeValue::L(
- registration_state
- .flattened_device_key_upload
+ flattened_device_key_upload
.identity_onetime_keys
.into_iter()
.map(AttributeValue::S)
@@ -161,24 +182,17 @@
),
(
USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME.to_string(),
- AttributeValue::S(
- registration_state.flattened_device_key_upload.notif_prekey,
- ),
+ AttributeValue::S(flattened_device_key_upload.notif_prekey),
),
(
USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME
.to_string(),
- AttributeValue::S(
- registration_state
- .flattened_device_key_upload
- .notif_prekey_signature,
- ),
+ AttributeValue::S(flattened_device_key_upload.notif_prekey_signature),
),
(
USERS_TABLE_DEVICES_MAP_NOTIF_ONETIME_KEYS_ATTRIBUTE_NAME.to_string(),
AttributeValue::L(
- registration_state
- .flattened_device_key_upload
+ flattened_device_key_upload
.notif_onetime_keys
.into_iter()
.map(AttributeValue::S)
@@ -186,30 +200,46 @@
),
),
]);
+
+ if let Some(social_proof) = social_proof {
+ device_info.insert(
+ USERS_TABLE_DEVICES_MAP_SOCIAL_PROOF_ATTRIBUTE_NAME.to_string(),
+ AttributeValue::S(social_proof),
+ );
+ }
let devices = HashMap::from([(
- registration_state.flattened_device_key_upload.device_id_key,
+ flattened_device_key_upload.device_id_key,
AttributeValue::M(device_info),
)]);
-
- let user = HashMap::from([
+ let mut user = HashMap::from([
(
USERS_TABLE_PARTITION_KEY.to_string(),
AttributeValue::S(user_id.clone()),
),
- (
- USERS_TABLE_USERNAME_ATTRIBUTE.to_string(),
- AttributeValue::S(registration_state.username),
- ),
(
USERS_TABLE_DEVICES_ATTRIBUTE.to_string(),
AttributeValue::M(devices),
),
- (
- USERS_TABLE_REGISTRATION_ATTRIBUTE.to_string(),
- AttributeValue::B(Blob::new(password_file)),
- ),
]);
+ if let Some((username, password_file)) = username_and_password_file {
+ user.insert(
+ USERS_TABLE_USERNAME_ATTRIBUTE.to_string(),
+ AttributeValue::S(username),
+ );
+ user.insert(
+ USERS_TABLE_REGISTRATION_ATTRIBUTE.to_string(),
+ AttributeValue::B(password_file),
+ );
+ }
+
+ if let Some(address) = wallet_address {
+ user.insert(
+ USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE.to_string(),
+ AttributeValue::S(address),
+ );
+ }
+
self
.client
.put_item()
@@ -222,49 +252,67 @@
Ok(user_id)
}
- pub async fn add_device_to_users_table(
+ pub async fn add_password_user_device_to_users_table(
+ &self,
+ user_id: String,
+ flattened_device_key_upload: FlattenedDeviceKeyUpload,
+ ) -> Result<(), Error> {
+ self
+ .add_device_to_users_table(user_id, flattened_device_key_upload, None)
+ .await
+ }
+
+ pub async fn add_wallet_user_device_to_users_table(
+ &self,
+ user_id: String,
+ flattened_device_key_upload: FlattenedDeviceKeyUpload,
+ social_proof: String,
+ ) -> Result<(), Error> {
+ self
+ .add_device_to_users_table(
+ user_id,
+ flattened_device_key_upload,
+ Some(social_proof),
+ )
+ .await
+ }
+
+ async fn add_device_to_users_table(
&self,
- login_state: UserLoginInfo,
+ user_id: String,
+ flattened_device_key_upload: FlattenedDeviceKeyUpload,
+ social_proof: Option<String>,
) -> Result<(), Error> {
- let device_info = HashMap::from([
+ let mut device_info = HashMap::from([
(
USERS_TABLE_DEVICES_MAP_DEVICE_TYPE_ATTRIBUTE_NAME.to_string(),
AttributeValue::S(Device::Client.to_string()),
),
(
USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_ATTRIBUTE_NAME.to_string(),
- AttributeValue::S(login_state.flattened_device_key_upload.key_payload),
+ AttributeValue::S(flattened_device_key_upload.key_payload),
),
(
USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_SIGNATURE_ATTRIBUTE_NAME
.to_string(),
- AttributeValue::S(
- login_state
- .flattened_device_key_upload
- .key_payload_signature,
- ),
+ AttributeValue::S(flattened_device_key_upload.key_payload_signature),
),
(
USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_ATTRIBUTE_NAME.to_string(),
- AttributeValue::S(
- login_state.flattened_device_key_upload.identity_prekey,
- ),
+ AttributeValue::S(flattened_device_key_upload.identity_prekey),
),
(
USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_SIGNATURE_ATTRIBUTE_NAME
.to_string(),
AttributeValue::S(
- login_state
- .flattened_device_key_upload
- .identity_prekey_signature,
+ flattened_device_key_upload.identity_prekey_signature,
),
),
(
USERS_TABLE_DEVICES_MAP_IDENTITY_ONETIME_KEYS_ATTRIBUTE_NAME
.to_string(),
AttributeValue::L(
- login_state
- .flattened_device_key_upload
+ flattened_device_key_upload
.identity_onetime_keys
.into_iter()
.map(AttributeValue::S)
@@ -273,22 +321,17 @@
),
(
USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME.to_string(),
- AttributeValue::S(login_state.flattened_device_key_upload.notif_prekey),
+ AttributeValue::S(flattened_device_key_upload.notif_prekey),
),
(
USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME
.to_string(),
- AttributeValue::S(
- login_state
- .flattened_device_key_upload
- .notif_prekey_signature,
- ),
+ AttributeValue::S(flattened_device_key_upload.notif_prekey_signature),
),
(
USERS_TABLE_DEVICES_MAP_NOTIF_ONETIME_KEYS_ATTRIBUTE_NAME.to_string(),
AttributeValue::L(
- login_state
- .flattened_device_key_upload
+ flattened_device_key_upload
.notif_onetime_keys
.into_iter()
.map(AttributeValue::S)
@@ -297,11 +340,17 @@
),
]);
+ if let Some(social_proof) = social_proof {
+ device_info.insert(
+ USERS_TABLE_DEVICES_MAP_SOCIAL_PROOF_ATTRIBUTE_NAME.to_string(),
+ AttributeValue::S(social_proof),
+ );
+ }
let update_expression =
format!("SET {}.#{} = :v", USERS_TABLE_DEVICES_ATTRIBUTE, "deviceID",);
let expression_attribute_names = HashMap::from([(
format!("#{}", "deviceID"),
- login_state.flattened_device_key_upload.device_id_key,
+ flattened_device_key_upload.device_id_key,
)]);
let expression_attribute_values =
HashMap::from([(":v".to_string(), AttributeValue::M(device_info))]);
@@ -310,10 +359,7 @@
.client
.update_item()
.table_name(USERS_TABLE)
- .key(
- USERS_TABLE_PARTITION_KEY,
- AttributeValue::S(login_state.user_id),
- )
+ .key(USERS_TABLE_PARTITION_KEY, AttributeValue::S(user_id))
.update_expression(update_expression)
.set_expression_attribute_names(Some(expression_attribute_names))
.set_expression_attribute_values(Some(expression_attribute_values))
diff --git a/services/identity/src/main.rs b/services/identity/src/main.rs
--- a/services/identity/src/main.rs
+++ b/services/identity/src/main.rs
@@ -14,6 +14,7 @@
mod interceptor;
mod keygen;
mod nonce;
+mod siwe;
mod token;
use config::load_config;
diff --git a/services/identity/src/siwe.rs b/services/identity/src/siwe.rs
new file mode 100644
--- /dev/null
+++ b/services/identity/src/siwe.rs
@@ -0,0 +1,34 @@
+use chrono::Utc;
+use siwe::{eip55, Message};
+use tonic::Status;
+use tracing::error;
+
+pub fn parse_and_verify_siwe_message(
+ siwe_message: &str,
+ siwe_signature: &str,
+) -> Result<String, Status> {
+ let siwe_message: Message = siwe_message.parse().map_err(|e| {
+ error!("Failed to parse SIWE message: {}", e);
+ Status::invalid_argument("invalid message")
+ })?;
+
+ let decoded_signature = hex::decode(siwe_signature.trim_start_matches("0x"))
+ .map_err(|e| {
+ error!("Failed to decode SIWE signature: {}", e);
+ Status::invalid_argument("invalid signature")
+ })?;
+
+ let signature = decoded_signature.try_into().map_err(|e| {
+ error!("Conversion to SIWE signature failed: {:?}", e);
+ Status::invalid_argument("invalid message")
+ })?;
+
+ siwe_message
+ .verify(signature, None, None, Some(&Utc::now()))
+ .map_err(|e| {
+ error!("Signature verification failed: {}", e);
+ Status::unauthenticated("message not authenticated")
+ })?;
+
+ Ok(eip55(&siwe_message.address))
+}
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
@@ -82,7 +82,7 @@
string payload = 1;
// Payload signed with the signing ed25519 key
string payloadSignature = 2;
- // Signed message used for SIWE (optional)
+ // Signed message used for SIWE
// This correlates a given wallet with the identity of a device
optional string socialProof = 3;
}

File Metadata

Mime Type
text/plain
Expires
Fri, Dec 27, 8:29 AM (10 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2706825
Default Alt Text
D7540.diff (18 KB)

Event Timeline