diff --git a/services/identity/src/config.rs b/services/identity/src/config.rs index df15e8768..fed405c6f 100644 --- a/services/identity/src/config.rs +++ b/services/identity/src/config.rs @@ -1,73 +1,89 @@ use curve25519_dalek::ristretto::RistrettoPoint; use once_cell::sync::Lazy; use opaque_ke::{errors::PakeError, keypair::KeyPair}; -use std::{env, fmt, fs, io, path::Path}; +use std::{env, fmt, fs, io, path}; use crate::constants::{ AUTH_TOKEN, LOCALSTACK_ENDPOINT, SECRETS_DIRECTORY, SECRETS_FILE_EXTENSION, - SECRETS_FILE_NAME, + SECRETS_FILE_NAME, SECRETS_SETUP_FILE, }; pub static CONFIG: Lazy = Lazy::new(|| Config::load().expect("failed to load config")); pub(super) fn load_config() { Lazy::force(&CONFIG); } #[derive(Clone)] pub struct Config { + // Opaque 1.2 server secrets pub server_keypair: KeyPair, // this is temporary, while the only authorized caller is ashoat's keyserver pub keyserver_auth_token: String, pub localstack_endpoint: Option, + // Opaque 2.0 server secrets + pub server_setup: comm_opaque2::ServerSetup, } impl Config { fn load() -> Result { - let mut path = env::current_dir()?; + let mut path = path::PathBuf::new(); path.push(SECRETS_DIRECTORY); path.push(SECRETS_FILE_NAME); path.set_extension(SECRETS_FILE_EXTENSION); let server_keypair = get_keypair_from_file(path)?; let keyserver_auth_token = env::var(AUTH_TOKEN).unwrap_or_else(|_| String::from("test")); let localstack_endpoint = env::var(LOCALSTACK_ENDPOINT).ok(); + let mut path = path::PathBuf::new(); + path.push(SECRETS_DIRECTORY); + path.push(SECRETS_SETUP_FILE); + let server_setup = get_server_setup_from_file(&path)?; + Ok(Self { server_keypair, keyserver_auth_token, localstack_endpoint, + server_setup, }) } } impl fmt::Debug for Config { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Config") .field("server_keypair", &"** redacted **") .field("keyserver_auth_token", &"** redacted **") .field("localstack_endpoint", &self.localstack_endpoint) .finish() } } -#[derive( - Debug, derive_more::Display, derive_more::From, derive_more::Error, -)] +#[derive(Debug, derive_more::Display, derive_more::From)] pub enum Error { #[display(...)] Pake(PakeError), #[display(...)] + Opaque(comm_opaque2::ProtocolError), + #[display(...)] IO(io::Error), #[display(...)] Env(env::VarError), } -fn get_keypair_from_file>( +fn get_keypair_from_file>( path: P, ) -> Result, Error> { let bytes = fs::read(path)?; KeyPair::from_private_key_slice(&bytes) .map_err(|e| Error::Pake(PakeError::CryptoError(e))) } + +fn get_server_setup_from_file>( + path: &P, +) -> Result, Error> { + let bytes = fs::read(path)?; + comm_opaque2::ServerSetup::deserialize(&bytes).map_err(Error::Opaque) +} diff --git a/services/identity/src/constants.rs b/services/identity/src/constants.rs index 52bb027db..1991bfa97 100644 --- a/services/identity/src/constants.rs +++ b/services/identity/src/constants.rs @@ -1,110 +1,111 @@ // Secrets pub const SECRETS_DIRECTORY: &str = "secrets"; pub const SECRETS_FILE_NAME: &str = "secret_key"; pub const SECRETS_FILE_EXTENSION: &str = "txt"; +pub const SECRETS_SETUP_FILE: &str = "server_setup.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; // LocalStack pub const LOCALSTACK_ENDPOINT: &str = "LOCALSTACK_ENDPOINT"; diff --git a/services/identity/src/keygen.rs b/services/identity/src/keygen.rs index c24c22b73..a27aa9a70 100644 --- a/services/identity/src/keygen.rs +++ b/services/identity/src/keygen.rs @@ -1,24 +1,41 @@ -use crate::constants::{SECRETS_FILE_EXTENSION, SECRETS_FILE_NAME}; +use crate::constants::{ + SECRETS_FILE_EXTENSION, SECRETS_FILE_NAME, SECRETS_SETUP_FILE, +}; use comm_opaque::Cipher; use opaque_ke::{ciphersuite::CipherSuite, rand::rngs::OsRng}; -use std::{env, fs, io}; +use std::{fs, io, path}; pub fn generate_and_persist_keypair(dir: &str) -> Result<(), io::Error> { + let mut secrets_dir = path::PathBuf::new(); + secrets_dir.push(dir); + if !secrets_dir.exists() { + println!("Creating secrets directory {:?}", secrets_dir); + fs::create_dir(&secrets_dir)?; + } + + // Opaque_ke 1.2.0 setup let mut rng = OsRng; let server_kp = Cipher::generate_random_keypair(&mut rng); - let mut path = env::current_dir()?; - path.push(dir); - if !path.exists() { - println!("Creating secrets directory {:?}", path); - fs::create_dir(&path)?; - } + let mut path = secrets_dir.clone(); path.push(SECRETS_FILE_NAME); path.set_extension(SECRETS_FILE_EXTENSION); + if !path.exists() { + println!("Writing secret key to {:?}", path); + fs::write(&path, server_kp.private().to_arr())?; + } else { + println!("{:?} already exists, skipping", path); + } + + // Opaque 2.0 setup + let server_setup = comm_opaque2::server::generate_server_setup(); + let mut path = secrets_dir.clone(); + path.push(SECRETS_SETUP_FILE); if path.exists() { - println!("{:?} already exists", path); - return Ok(()); + eprintln!("{:?} already exists, skipping", path); + } else { + println!("Writing setup file to {:?}", path); + fs::write(&path, server_setup.serialize())?; } - println!("Writing secret key to {:?}", path); - fs::write(&path, server_kp.private().to_arr())?; + Ok(()) } diff --git a/shared/comm-opaque2/src/lib.rs b/shared/comm-opaque2/src/lib.rs index 41c4a027e..35fc6a345 100644 --- a/shared/comm-opaque2/src/lib.rs +++ b/shared/comm-opaque2/src/lib.rs @@ -1,55 +1,58 @@ pub mod client; pub mod error; pub mod grpc; pub mod opaque; pub mod server; pub use crate::opaque::Cipher; +pub use opaque_ke; +pub use opaque_ke::errors::ProtocolError; +pub use opaque_ke::ServerSetup; #[test] pub fn test_register_and_login() { use rand::rngs::OsRng; let pass = "test"; let username = "alice"; let server_setup = opaque_ke::ServerSetup::::new(&mut OsRng); // Register user let mut client_register = client::Registration::new(); let client_message = client_register.start(pass).unwrap(); let mut server_register = server::Registration::new(); let server_response = server_register .start(&server_setup, &client_message, username.as_bytes()) .unwrap(); let client_upload = client_register.finish(pass, &server_response).unwrap(); // These bytes are the used to validate future login sessions, normally it // would saved to a database or other data store let password_file_bytes = server_register.finish(&client_upload).unwrap(); // Login user let mut client_login = client::Login::new(); let client_request = client_login.start(pass).unwrap(); let mut server_login = server::Login::new(); let server_response = server_login .start( &server_setup, &password_file_bytes, &client_request, username.as_bytes(), ) .unwrap(); let client_upload = client_login.finish(&server_response).unwrap(); server_login.finish(&client_upload).unwrap(); assert_eq!( client_login.session_key().unwrap(), server_login.session_key.unwrap() ); } diff --git a/shared/comm-opaque2/src/server/mod.rs b/shared/comm-opaque2/src/server/mod.rs index 8b01db132..b5d11e1f9 100644 --- a/shared/comm-opaque2/src/server/mod.rs +++ b/shared/comm-opaque2/src/server/mod.rs @@ -1,5 +1,14 @@ pub mod login; pub mod register; pub use login::Login; pub use register::Registration; + +use opaque_ke::rand::rngs::OsRng; +use opaque_ke::ServerSetup; + +use crate::Cipher; + +pub fn generate_server_setup() -> ServerSetup { + ServerSetup::::new(&mut OsRng) +}