diff --git a/services/identity/src/constants.rs b/services/identity/src/constants.rs index b2a3d2653..988332618 100644 --- a/services/identity/src/constants.rs +++ b/services/identity/src/constants.rs @@ -1,48 +1,106 @@ // Secrets pub const SECRETS_DIRECTORY: &str = "secrets"; pub const SECRETS_FILE_NAME: &str = "secret_key"; pub const SECRETS_FILE_EXTENSION: &str = "txt"; // DynamoDB +// User table information, supporting opaque_ke 2.0 and X3DH information +pub mod opaque2 { + // Users can sign in either through username+password or Eth wallet. + // + // This structure should be aligned with the messages defined in + // shared/protos/identity_client.proto + // + // Structure for a user should be: + // { + // userID: String, + // opaqueRegistrationData: Option, + // username: Option, + // walletAddress: Option, + // devices: HashMap + // } + // + // A device is defined as: + // { + // deviceType: String, # client or keyserver + // keyPayload: String, + // identityPreKey: String, + // identityPreKeySignature: String, + // identityOneTimeKeys: Vec, + // notifPreKey: String, + // notifPreKeySignature: String, + // notifOneTimeKeys: Vec, + // } + // } + // + // Additional context: + // "devices" uses the signing public identity key of the device as a key for the devices map + // "keyPayload" is a JSON encoded string containing identity and notif keys (both signature and verification) + // if "deviceType" == "keyserver", then the device will not have any notif key information + + pub const USERS_TABLE: &str = "identity-users-opaque2"; + pub const USERS_TABLE_PARTITION_KEY: &str = "userID"; + pub const USERS_TABLE_REGISTRATION_ATTRIBUTE: &str = "opaqueRegistrationData"; + pub const USERS_TABLE_USERNAME_ATTRIBUTE: &str = "username"; + pub const USERS_TABLE_DEVICES_ATTRIBUTE: &str = "devices"; + pub const USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_ATTRIBUTE_NAME: &str = + "keyPayload"; + pub const USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_ATTRIBUTE_NAME: &str = + "identityPreKey"; + pub const USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_SIGNATURE_ATTRIBUTE_NAME: + &str = "identityPreKeySignature"; + pub const USERS_TABLE_DEVICES_MAP_IDENTITY_ONETIME_KEYS_ATTRIBUTE_NAME: &str = + "identityOneTimeKeys"; + pub const USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME: &str = + "preKey"; + pub const USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME: + &str = "preKeySignature"; + 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_USERNAME_INDEX: &str = "username-index"; + pub const USERS_TABLE_WALLET_ADDRESS_INDEX: &str = "walletAddress-index"; +} + pub const USERS_TABLE: &str = "identity-users"; pub const USERS_TABLE_PARTITION_KEY: &str = "userID"; pub const USERS_TABLE_REGISTRATION_ATTRIBUTE: &str = "pakeRegistrationData"; pub const USERS_TABLE_USERNAME_ATTRIBUTE: &str = "username"; pub const USERS_TABLE_DEVICES_ATTRIBUTE: &str = "devices"; pub const USERS_TABLE_DEVICE_ATTRIBUTE_NAME: &str = "device"; pub const USERS_TABLE_DEVICES_MAP_ATTRIBUTE_NAME: &str = "signingPublicKey"; pub const USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE: &str = "walletAddress"; pub const USERS_TABLE_USERNAME_INDEX: &str = "username-index"; pub const USERS_TABLE_WALLET_ADDRESS_INDEX: &str = "walletAddress-index"; pub const USERS_TABLE_INITIALIZATION_INFO: &str = "initializationInfo"; pub const ACCESS_TOKEN_TABLE: &str = "identity-tokens"; pub const ACCESS_TOKEN_TABLE_PARTITION_KEY: &str = "userID"; pub const ACCESS_TOKEN_SORT_KEY: &str = "signingPublicKey"; pub const ACCESS_TOKEN_TABLE_CREATED_ATTRIBUTE: &str = "created"; pub const ACCESS_TOKEN_TABLE_AUTH_TYPE_ATTRIBUTE: &str = "authType"; pub const ACCESS_TOKEN_TABLE_VALID_ATTRIBUTE: &str = "valid"; pub const ACCESS_TOKEN_TABLE_TOKEN_ATTRIBUTE: &str = "token"; pub const NONCE_TABLE: &str = "identity-nonces"; pub const NONCE_TABLE_PARTITION_KEY: &str = "nonce"; pub const NONCE_TABLE_CREATED_ATTRIBUTE: &str = "created"; // Tokio pub const MPSC_CHANNEL_BUFFER_CAPACITY: usize = 1; pub const IDENTITY_SERVICE_SOCKET_ADDR: &str = "[::]:50054"; // Token pub const ACCESS_TOKEN_LENGTH: usize = 512; // Temporary config pub const AUTH_TOKEN: &str = "COMM_IDENTITY_SERVICE_AUTH_TOKEN"; // Nonce pub const NONCE_LENGTH: usize = 17; diff --git a/services/identity/src/main.rs b/services/identity/src/main.rs index 11e4edc1c..1e702e576 100644 --- a/services/identity/src/main.rs +++ b/services/identity/src/main.rs @@ -1,81 +1,81 @@ use clap::{Parser, Subcommand}; use database::DatabaseClient; use interceptor::check_auth; use tonic::transport::Server; use tonic_web::GrpcWebLayer; use tracing_subscriber::FmtSubscriber; mod client_service; mod config; -mod constants; +pub mod constants; mod database; mod interceptor; mod keygen; mod nonce; mod pake_grpc; mod service; mod token; use config::load_config; use constants::{IDENTITY_SERVICE_SOCKET_ADDR, SECRETS_DIRECTORY}; use keygen::generate_and_persist_keypair; use service::{IdentityKeyserverServiceServer, MyIdentityService}; use tracing::info; use client_service::{ClientService, IdentityClientServiceServer}; #[derive(Parser)] #[clap(author, version, about, long_about = None)] #[clap(propagate_version = true)] struct Cli { #[clap(subcommand)] command: Commands, } #[derive(Subcommand)] enum Commands { /// Runs the server Server, /// Generates and persists a keypair to use for PAKE registration and login Keygen { #[clap(short, long)] #[clap(default_value_t = String::from(SECRETS_DIRECTORY))] dir: String, }, /// Populates the `identity-users` table in DynamoDB from MySQL PopulateDB, } #[tokio::main] async fn main() -> Result<(), Box> { let subscriber = FmtSubscriber::new(); tracing::subscriber::set_global_default(subscriber)?; let cli = Cli::parse(); match &cli.command { Commands::Keygen { dir } => { generate_and_persist_keypair(dir)?; } Commands::Server => { load_config(); let addr = IDENTITY_SERVICE_SOCKET_ADDR.parse()?; let aws_config = aws_config::from_env().region("us-east-2").load().await; let database_client = DatabaseClient::new(&aws_config); let server = MyIdentityService::new(database_client.clone()); let keyserver_service = IdentityKeyserverServiceServer::with_interceptor(server, check_auth); let client_service = IdentityClientServiceServer::new(ClientService::new(database_client)); info!("Listening to gRPC traffic on {}", addr); Server::builder() .accept_http1(true) .layer(GrpcWebLayer::new()) .add_service(keyserver_service) .add_service(client_service) .serve(addr) .await?; } Commands::PopulateDB => unimplemented!(), } Ok(()) } diff --git a/services/terraform/dynamodb.tf b/services/terraform/dynamodb.tf index 6aa13537a..63a3a8931 100644 --- a/services/terraform/dynamodb.tf +++ b/services/terraform/dynamodb.tf @@ -1,241 +1,280 @@ resource "aws_dynamodb_table" "backup-service-backup" { name = "backup-service-backup" hash_key = "userID" range_key = "backupID" write_capacity = 10 read_capacity = 10 attribute { name = "userID" type = "S" } attribute { name = "backupID" type = "S" } attribute { name = "created" type = "S" } global_secondary_index { name = "userID-created-index" hash_key = "userID" range_key = "created" write_capacity = 10 read_capacity = 10 projection_type = "INCLUDE" non_key_attributes = ["recoveryData"] } } resource "aws_dynamodb_table" "backup-service-log" { name = "backup-service-log" hash_key = "backupID" range_key = "logID" write_capacity = 10 read_capacity = 10 attribute { name = "backupID" type = "S" } attribute { name = "logID" type = "S" } } resource "aws_dynamodb_table" "blob-service-blob" { name = "blob-service-blob" hash_key = "blobHash" write_capacity = 10 read_capacity = 10 attribute { name = "blobHash" type = "S" } } resource "aws_dynamodb_table" "blob-service-reverse-index" { name = "blob-service-reverse-index" hash_key = "holder" write_capacity = 10 read_capacity = 10 attribute { name = "holder" type = "S" } attribute { name = "blobHash" type = "S" } global_secondary_index { name = "blobHash-index" hash_key = "blobHash" write_capacity = 10 read_capacity = 10 projection_type = "ALL" } } resource "aws_dynamodb_table" "tunnelbroker-device-sessions" { name = "tunnelbroker-device-sessions" hash_key = "SessionID" write_capacity = 10 read_capacity = 10 attribute { name = "SessionID" type = "S" } ttl { attribute_name = "Expire" enabled = true } } resource "aws_dynamodb_table" "tunnelbroker-verification-messages" { name = "tunnelbroker-verification-messages" hash_key = "DeviceID" write_capacity = 10 read_capacity = 10 attribute { name = "DeviceID" type = "S" } ttl { attribute_name = "Expire" enabled = true } } resource "aws_dynamodb_table" "tunnelbroker-public-keys" { name = "tunnelbroker-public-keys" hash_key = "DeviceID" write_capacity = 10 read_capacity = 10 attribute { name = "DeviceID" type = "S" } } resource "aws_dynamodb_table" "tunnelbroker-messages" { name = "tunnelbroker-messages" hash_key = "ToDeviceID" range_key = "MessageID" write_capacity = 10 read_capacity = 10 attribute { name = "MessageID" type = "S" } attribute { name = "ToDeviceID" type = "S" } ttl { attribute_name = "Expire" enabled = true } } resource "aws_dynamodb_table" "identity-users" { name = "identity-users" hash_key = "userID" write_capacity = 10 read_capacity = 10 attribute { name = "userID" type = "S" } attribute { name = "username" type = "S" } attribute { name = "walletAddress" type = "S" } global_secondary_index { name = "username-index" hash_key = "username" write_capacity = 10 read_capacity = 10 projection_type = "KEYS_ONLY" } global_secondary_index { name = "walletAddress-index" hash_key = "walletAddress" write_capacity = 10 read_capacity = 10 projection_type = "KEYS_ONLY" } } +# Identity users with opaque_ke 2.0 credentials +resource "aws_dynamodb_table" "identity-users-opaque2" { + name = "identity-users-opaque2" + hash_key = "userID" + write_capacity = 10 + read_capacity = 10 + + attribute { + name = "userID" + type = "S" + } + + attribute { + name = "username" + type = "S" + } + + attribute { + name = "walletAddress" + type = "S" + } + + global_secondary_index { + name = "username-index" + hash_key = "username" + write_capacity = 10 + read_capacity = 10 + projection_type = "KEYS_ONLY" + } + + global_secondary_index { + name = "walletAddress-index" + hash_key = "walletAddress" + write_capacity = 10 + read_capacity = 10 + projection_type = "KEYS_ONLY" + } +} + resource "aws_dynamodb_table" "identity-tokens" { name = "identity-tokens" hash_key = "userID" range_key = "signingPublicKey" write_capacity = 10 read_capacity = 10 attribute { name = "userID" type = "S" } attribute { name = "signingPublicKey" type = "S" } } resource "aws_dynamodb_table" "identity-nonces" { name = "identity-nonces" hash_key = "nonce" write_capacity = 10 read_capacity = 10 attribute { name = "nonce" type = "S" } } resource "aws_dynamodb_table" "feature-flags" { name = "feature-flags" hash_key = "platform" range_key = "feature" billing_mode = "PAY_PER_REQUEST" attribute { name = "platform" type = "S" } attribute { name = "feature" type = "S" } }