diff --git a/shared/comm-opaque/src/client.rs b/shared/comm-opaque/src/client.rs new file mode 100644 --- /dev/null +++ b/shared/comm-opaque/src/client.rs @@ -0,0 +1,52 @@ +use opaque_ke::{ + errors::ProtocolError, ClientLogin, ClientLoginFinishParameters, + ClientLoginFinishResult, ClientLoginStartParameters, ClientRegistration, + ClientRegistrationFinishParameters, CredentialResponse, RegistrationResponse, +}; +use rand::rngs::OsRng; + +use crate::Cipher; + +// These methods are used in other parts of the code base +#[allow(dead_code)] +pub fn register_start( + password: &[u8], +) -> Result<(ClientRegistration, Vec), ProtocolError> { + let res = ClientRegistration::::start(&mut OsRng, password)?; + Ok((res.state, res.message.serialize())) +} + +#[allow(dead_code)] +pub fn register_finish( + registration_result: ClientRegistration, + response_payload: &[u8], +) -> Result, ProtocolError> { + let response = RegistrationResponse::deserialize(response_payload)?; + let result = registration_result.finish( + &mut OsRng, + response, + ClientRegistrationFinishParameters::default(), + )?; + Ok(result.message.serialize()) +} + +#[allow(dead_code)] +pub fn login_start( + password: &[u8], +) -> Result<(ClientLogin, Vec), ProtocolError> { + let start_result = ClientLogin::::start( + &mut OsRng, + password, + ClientLoginStartParameters::default(), + )?; + Ok((start_result.state, start_result.message.serialize()?)) +} + +#[allow(dead_code)] +pub fn login_finish( + login_result: ClientLogin, + response_payload: &[u8], +) -> Result, ProtocolError> { + let response = CredentialResponse::deserialize(response_payload)?; + login_result.finish(response, ClientLoginFinishParameters::default()) +} diff --git a/shared/comm-opaque/src/config.rs b/shared/comm-opaque/src/config.rs new file mode 100644 --- /dev/null +++ b/shared/comm-opaque/src/config.rs @@ -0,0 +1,52 @@ +use curve25519_dalek::ristretto::RistrettoPoint; +use log::error; +use once_cell::sync::Lazy; +use opaque_ke::{ + ciphersuite::CipherSuite, errors::InternalPakeError, keypair::KeyPair, +}; +use rand::rngs::OsRng; +use std::{env, fs, path::Path}; + +use crate::{ + constants::{SECRETS_DIRECTORY, SECRETS_FILE_EXTENSION, SECRETS_FILE_NAME}, + Cipher, +}; + +pub static CONFIG: Lazy = + Lazy::new(|| Config::load().expect("failed to load config")); + +#[allow(dead_code)] +pub(super) fn load_config() { + Lazy::force(&CONFIG); +} + +#[derive(Clone)] +pub struct Config { + pub server_keypair: KeyPair, +} + +impl Config { + fn load() -> Result { + let mut path = env::current_dir().expect("Failed to determine CWD"); + path.push(SECRETS_DIRECTORY); + path.push(SECRETS_FILE_NAME); + path.set_extension(SECRETS_FILE_EXTENSION); + let keypair = get_keypair_from_file(path)?; + Ok(Self { + server_keypair: keypair, + }) + } +} + +fn get_keypair_from_file>( + path: P, +) -> Result, InternalPakeError> { + let maybe_bytes = fs::read(path); + match maybe_bytes { + Ok(bytes) => KeyPair::from_private_key_slice(&bytes), + Err(_) => { + error!("Unable to load secrets file"); + return Ok(Cipher::generate_random_keypair(&mut OsRng)); + } + } +} diff --git a/shared/comm-opaque/src/constants.rs b/shared/comm-opaque/src/constants.rs new file mode 100644 --- /dev/null +++ b/shared/comm-opaque/src/constants.rs @@ -0,0 +1,5 @@ +// Secrets + +pub const SECRETS_DIRECTORY: &str = "secrets"; +pub const SECRETS_FILE_NAME: &str = "secret_key"; +pub const SECRETS_FILE_EXTENSION: &str = "txt"; diff --git a/shared/comm-opaque/src/grpc.rs b/shared/comm-opaque/src/grpc.rs new file mode 100644 --- /dev/null +++ b/shared/comm-opaque/src/grpc.rs @@ -0,0 +1,29 @@ +use log::info; +use opaque_ke::errors::ProtocolError; +use tonic::Status; + +#[allow(dead_code)] +fn protocol_error_to_grpc_status(error: ProtocolError) -> tonic::Status { + match error { + ProtocolError::VerificationError(_) => { + info!("Failed to validate password"); + Status::aborted("server error") + } + ProtocolError::ServerError => { + info!("Invalid server response"); + Status::aborted("server error") + } + ProtocolError::ServerInvalidEnvelopeCredentialsFormatError => { + info!("Invalid server credential format"); + Status::invalid_argument("bad response") + } + ProtocolError::ClientError => { + info!("Client response cannot be handled"); + Status::invalid_argument("bad client response") + } + ProtocolError::ReflectedValueError => { + info!("OPRF value was reflected"); + Status::invalid_argument("invalid server response") + } + } +} diff --git a/shared/comm-opaque/src/lib.rs b/shared/comm-opaque/src/lib.rs --- a/shared/comm-opaque/src/lib.rs +++ b/shared/comm-opaque/src/lib.rs @@ -1,2 +1,44 @@ +mod client; +mod config; +mod constants; +mod grpc; mod opaque; +mod server; + pub use crate::opaque::Cipher; + +#[test] +pub fn test_register_and_login() { + let pass = "test"; + + // Register user + let (client_reg_res, client_message) = + client::register_start(pass.as_bytes()).unwrap(); + + let (server_reg_res, server_response) = + server::register_start(&client_message).unwrap(); + + let client_upload = + client::register_finish(client_reg_res, &server_response).unwrap(); + + let password_file = + server::register_finish(server_reg_res, &client_upload).unwrap(); + // These bytes are the used to validate future login sessions, normally it + // would saved to a database or other data store + let password_registration_bytes = password_file.serialize(); + + // Login user + let (client_login_res, client_request) = + client::login_start(pass.as_bytes()).unwrap(); + + let registration = + server::deserialize_registration(&password_registration_bytes).unwrap(); + let (server_login_res, server_response) = + server::login_start(registration, &client_request).unwrap(); + + let client_finish_res = + client::login_finish(client_login_res, &server_response).unwrap(); + let client_upload = client_finish_res.message.serialize().unwrap(); + + server::login_finish(server_login_res, &client_upload).unwrap(); +} diff --git a/shared/comm-opaque/src/server.rs b/shared/comm-opaque/src/server.rs new file mode 100644 --- /dev/null +++ b/shared/comm-opaque/src/server.rs @@ -0,0 +1,67 @@ +use opaque_ke::{errors::ProtocolError, ServerRegistration}; +use opaque_ke::{ + CredentialFinalization, CredentialRequest, RegistrationRequest, + RegistrationUpload, ServerLogin, ServerLoginFinishResult, + ServerLoginStartParameters, +}; +use rand::rngs::OsRng; + +use crate::config::CONFIG; +use crate::Cipher; + +#[allow(dead_code)] +pub fn register_start( + payload: &[u8], +) -> Result<(ServerRegistration, Vec), ProtocolError> { + let upload = RegistrationRequest::deserialize(payload)?; + let result = ServerRegistration::::start( + &mut OsRng, + upload, + CONFIG.server_keypair.public(), + )?; + Ok((result.state, result.message.serialize())) +} + +#[allow(dead_code)] +pub fn register_finish( + server_registration: ServerRegistration, + registration_upload_bytes: &[u8], +) -> Result, ProtocolError> { + let upload_payload = + RegistrationUpload::deserialize(registration_upload_bytes)?; + server_registration.finish(upload_payload) +} + +#[allow(dead_code)] +pub fn deserialize_registration( + bytes: &[u8], +) -> Result, ProtocolError> { + ServerRegistration::deserialize(bytes) +} + +#[allow(dead_code)] +pub fn login_start( + server_registration: ServerRegistration, + pake_credential_request: &[u8], +) -> Result<(ServerLogin, Vec), ProtocolError> { + let credential_request = + CredentialRequest::deserialize(pake_credential_request)?; + let result = ServerLogin::start( + &mut OsRng, + server_registration, + CONFIG.server_keypair.private(), + credential_request, + ServerLoginStartParameters::default(), + )?; + Ok((result.state, result.message.serialize()?)) +} + +#[allow(dead_code)] +pub fn login_finish( + server_login: ServerLogin, + pake_credential_finalization: &[u8], +) -> Result, ProtocolError> { + let finalization_payload = + CredentialFinalization::deserialize(&pake_credential_finalization[..])?; + server_login.finish(finalization_payload) +}