diff --git a/src/client/app.rs b/src/client/app.rs index 1971c32..ee6763a 100644 --- a/src/client/app.rs +++ b/src/client/app.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; use nkode_rs::nkode_core::keypad::Keypad; -use crate::shared::models::app::{OpaqueAPI, AuthAPI, CodeLoginData, CodeLoginSession, Icon, IconID, KeyLoginSession, ICON_ID_SIZE, LoginSession}; +use crate::shared::models::app::{OpaqueAPI, AuthAPI, CodeLoginData, CodeLoggedInSession, Icon, IconID, KeyLoggedInSession, ICON_ID_SIZE, LoggedInSession}; use crate::shared::models::email::Email; use crate::shared::models::opaque::{OpaqueLoginSession, UserSecretKey}; use anyhow::Result; @@ -50,7 +50,7 @@ pub struct ClientAppKeyLoggedIn { api: S, email: Email, user_secret_key: UserSecretKey, - key_login: KeyLoginSession, + key_login: KeyLoggedInSession, } impl ClientAppKeyLoggedIn { @@ -104,7 +104,7 @@ pub struct ClientAppCodeRegister { api: S, email: Email, user_secret_key: UserSecretKey, - key_login: KeyLoginSession, + key_login: KeyLoggedInSession, icon_nonce: Nonce, icons: Vec, keypad: Keypad, @@ -156,7 +156,7 @@ impl ClientAppCodeLogin { ).unwrap().to_vec() } - pub async fn login(&self, selected_keys: &Vec) -> Result { + pub async fn login(&self, selected_keys: &Vec) -> Result { let passcode = self.cipher.decipher(selected_keys, self.keypad.indices(), &self.mask).map_err(|e| format!("invalid keys: {e}"))?; self.api.login_code(&self.email, &passcode).await } @@ -186,31 +186,33 @@ where self.opaque_key_register.register(&auth_data).await.map_err(|e| format!("error: {}", e)) } - async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoginSession, data: &CodeLoginData) -> Result<(), String> { + async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, data: &CodeLoginData) -> Result<(), String> { let auth_data = AuthenticationData::from_code(email.as_str(), passcode); self.opaque_code_register.register(&auth_data).await.map_err(|e| format!("error: {}", e)) } - async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result { + async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result { let auth_data = AuthenticationData::from_secret_key(&email.as_str(), secret_key.as_slice()); let session_key = self.opaque_key_login.login(&auth_data).await.map_err(|e| format!("error: {}", e))?; - Ok(KeyLoginSession( - LoginSession { - email: email.clone(), - session_key - } - )) + todo!() + // Ok(KeyLoggedInSession( + // LoggedInSession { + // email: email.clone(), + // session_key + // } + // )) } - async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result { + async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result { let auth_data = AuthenticationData::from_code(email.as_str(), passcode); let session_key = self.opaque_code_login.login(&auth_data).await.map_err(|e| format!("error: {}", e))?; - Ok(CodeLoginSession( - LoginSession { - email: email.clone(), - session_key - } - )) + todo!() + // Ok(CodeLoggedInSession( + // LoggedInSession { + // email: email.clone(), + // session_key + // } + // )) } } @@ -223,7 +225,7 @@ where { async fn get_new_icons( &self, - key_login_session: &KeyLoginSession, + key_login_session: &KeyLoggedInSession, ) -> Result, String> { self.nkode_api .get_new_icons(key_login_session) @@ -232,14 +234,14 @@ where async fn get_login_data( &self, - key_login_session: &KeyLoginSession, + key_login_session: &KeyLoggedInSession, ) -> Result { self.nkode_api .get_login_data(key_login_session) .await } - async fn is_code_registered(&self, key_login_session: &KeyLoginSession) -> Result { + async fn is_code_registered(&self, key_login_session: &KeyLoggedInSession) -> Result { self.nkode_api.is_code_registered(key_login_session).await } diff --git a/src/client/opaque.rs b/src/client/opaque.rs index a144dee..557d5f9 100644 --- a/src/client/opaque.rs +++ b/src/client/opaque.rs @@ -9,6 +9,7 @@ use opaque_ke::{ CredentialFinalization, CredentialRequest, RegistrationRequest, }; +use crate::shared::models::app::LoggedInSession; use crate::shared::models::opaque::{OpaqueRegisterSession, OpaqueLoginSession, NKodeCipherSuite, PasswordFile, OpaqueSessionKey}; #[derive(Debug)] @@ -81,7 +82,7 @@ pub trait ServerConnectionLogin { &self, session_id: &Uuid, message: &CredentialFinalization, - ) -> Result; + ) -> Result; } diff --git a/src/server/app.rs b/src/server/app.rs index e69de29..675a094 100644 --- a/src/server/app.rs +++ b/src/server/app.rs @@ -0,0 +1,182 @@ +use uuid::Uuid; +use opaque_ke::{CredentialFinalization, CredentialRequest, RegistrationRequest, ServerLogin, ServerLoginParameters, ServerRegistration}; +use opaque_ke::argon2::password_hash::rand_core::OsRng; +use nkode_rs::from_bytes::FromBytes; +use crate::server::models::CredKind; +use crate::server::repository::opaque_repo::{AuthRepoError, OpaqueDatabaseRepo, OpaqueSessionRepo}; +use crate::server::repository::user_repo::UserRepo; +use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession, LoggedInSession}; +use crate::shared::models::email::Email; +use crate::shared::models::opaque::{NKodeCipherSuite, NKodeServerSetup, OpaqueLoginSession, OpaqueRegisterSession, OpaqueSessionKey, PasswordFile}; + +pub struct ServerApp { + server_setup: NKodeServerSetup, + opaque_db: R, + opaque_sess: S, + user_db: U, +} + +impl ServerApp { + pub fn new(server_setup: NKodeServerSetup, opaque_db: R, opaque_sess: S, user_db: U) -> Self { + Self { server_setup, opaque_db, opaque_sess, user_db} + } + + pub async fn reg_start( + &mut self, + identifier: &[u8], + request: RegistrationRequest, + ) -> Result { + K::prereq_for_register(&self.opaque_db, identifier) + .map_err(|e| format!("registration prereq failed: {e:?}"))?; + let start = ServerRegistration::::start( + &self.server_setup, + request, + identifier, + ).map_err(|e| format!("opaque reg start: {e:?}"))?; + let cache = self.opaque_sess + .new_reg_session(identifier) + .map_err(|e| format!("reg cache: {e}"))?; + + Ok(OpaqueRegisterSession { session_id: cache.session_id, response: start.message }) + } + + pub async fn reg_finish( + &mut self, + session_id: &Uuid, + password_file: PasswordFile, + ) -> Result<(), String> { + let sess = self.opaque_sess + .get_reg_session(session_id) + .map_err(|e| format!("get reg session: {e}"))?; + K::prereq_for_register(&self.opaque_db, sess.identifier.as_slice()) + .map_err(|e| format!("registration prereq failed: {e:?}"))?; + K::set_password_file(&mut self.opaque_db, sess.identifier.as_slice(), password_file) + .map_err(|e| format!("repo write: {e:?}"))?; + self.opaque_sess + .clear_reg_session(session_id) + .map_err(|e| format!("clear reg session: {e}")) + } + + pub async fn login_start( + &mut self, + identifier: &[u8], + request: CredentialRequest, + ) -> Result { + let password_file = K::get_password_file(&self.opaque_db, identifier) + .map_err(|e| format!("repo read: {e:?}"))?; + + let password_file = + ServerRegistration::::deserialize(password_file.as_slice()) + .map_err(|e| format!("pf deserialize: {e:?}"))?; + let mut server_rng = OsRng; + let start = ServerLogin::start( + &mut server_rng, + &self.server_setup, + Some(password_file), + request, + identifier, + ServerLoginParameters::default(), + ).map_err(|e| format!("opaque login start: {e:?}"))?; + let cache = self.opaque_sess + .new_login_session(identifier, start.state) + .map_err(|e| format!("login cache: {e}"))?; + Ok(OpaqueLoginSession { session_id: cache.session_id, response: start.message }) + } + + pub async fn login_finish( + &mut self, + session_id: &Uuid, + finalize: CredentialFinalization, + ) -> Result { + let cache = self.opaque_sess + .get_login_session(session_id) + .map_err(|e| format!("get login session: {e}"))?; + + let finish = cache.server_login + .finish(finalize, ServerLoginParameters::default()) + .map_err(|e| format!("opaque login finish: {e:?}"))?; + + self.opaque_sess + .clear_login_session(session_id) + .map_err(|e| format!("clear login session: {e}"))?; + let session_key = OpaqueSessionKey::from_bytes(&finish.session_key.to_vec()).unwrap(); + Ok(LoggedInSession{ + session_id: Uuid::new_v4(), + email: Email::from_bytes(cache.identifiers.as_slice()).unwrap(), + session_key + }) + } + + pub async fn key_login_finish( + &mut self, + session_id: &Uuid, + finalize: CredentialFinalization, + ) -> Result { + let session = KeyLoggedInSession(self.login_finish(session_id, finalize).await?); + self.user_db.set_key_session(session.clone())?; + Ok(session) + } + + pub async fn code_login_finish( + &mut self, + session_id: &Uuid, + finalize: CredentialFinalization, + ) -> Result { + let session = CodeLoggedInSession(self.login_finish(session_id, finalize).await?); + self.user_db.set_code_session(session.clone())?; + Ok(session) + } + + pub async fn get_key_session(&mut self, session_id: &Uuid) -> Result { + self.user_db.get_key_session(session_id) + } + + pub async fn get_code_session(&mut self, session_id: &Uuid) -> Result { + self.user_db.get_code_session(session_id) + } + + pub async fn get_code_login_data(&mut self, email: &Email) -> Result { + self.user_db.get_code_login_data(email) + } + + pub async fn set_code_login_data(&mut self, email: Email, data: CodeLoginData) -> Result<(), String> { + self.user_db.set_code_login_data(email, data) + } +} + +#[derive(Clone)] +pub struct Key; + +#[derive(Clone)] +pub struct Code; + +impl CredKind for Key { + fn has(repo: &R, id: &[u8]) -> bool { + repo.has_key(id) + } + fn get_password_file(repo: &R, id: &[u8]) -> Result { + repo.get_key_passcode_file(id) + } + fn set_password_file(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> { + repo.new_key(id, pf) + } +} + +impl CredKind for Code { + fn has(repo: &R, id: &[u8]) -> bool { + repo.has_code(id) + } + fn get_password_file(repo: &R, id: &[u8]) -> Result { + repo.get_code_passcode_file(id) + } + fn set_password_file(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> { + repo.new_code(id, pf) + } + fn prereq_for_register(repo: &R, id: &[u8]) -> Result<(), AuthRepoError> { + if repo.has_key(id) { + Ok(()) + } else { + Err(AuthRepoError::KeyNotRegistered) + } + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs index ecbe7bb..2dfd474 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,3 +1,3 @@ pub mod app; pub mod repository; -pub mod opaque; \ No newline at end of file +mod models; diff --git a/src/server/models.rs b/src/server/models.rs new file mode 100644 index 0000000..d3fb563 --- /dev/null +++ b/src/server/models.rs @@ -0,0 +1,24 @@ +use uuid::Uuid; +use opaque_ke::ServerLogin; +use crate::server::repository::opaque_repo::{AuthRepoError, OpaqueDatabaseRepo}; +use crate::shared::models::opaque::{NKodeCipherSuite, PasswordFile}; + +pub struct RegCache { + pub session_id: Uuid, + pub identifier: Vec, +} + +pub struct LoginCache { + pub session_id: Uuid, + pub identifiers: Vec, + pub server_login: ServerLogin, +} + +pub trait CredKind { + fn has(repo: &R, id: &[u8]) -> bool; + fn get_password_file(repo: &R, id: &[u8]) -> Result; + fn set_password_file(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError>; + fn prereq_for_register(_repo: &R, _id: &[u8]) -> Result<(), AuthRepoError> { + Ok(()) + } +} \ No newline at end of file diff --git a/src/server/opaque.rs b/src/server/opaque.rs deleted file mode 100644 index 27d7b83..0000000 --- a/src/server/opaque.rs +++ /dev/null @@ -1,159 +0,0 @@ -use nkode_rs::from_bytes::FromBytes; -use opaque_ke::{ - rand::rngs::OsRng, CredentialFinalization, CredentialRequest, - RegistrationRequest, ServerLogin, ServerLoginParameters, - ServerRegistration, -}; -use uuid::Uuid; -use crate::shared::models::opaque::{OpaqueLoginSession, NKodeCipherSuite, NKodeServerSetup, OpaqueSessionKey, PasswordFile, OpaqueRegisterSession}; -use crate::server::repository::opaque::repos::{OpaqueDatabaseRepo, AuthRepoError, OpaqueSessionRepo}; - -pub struct RegCache { - pub session_id: Uuid, - pub identifier: Vec, -} - -pub struct LoginCache { - pub session_id: Uuid, - pub identifiers: Vec, - pub server_login: ServerLogin, -} - -pub trait CredKind { - fn has(repo: &R, id: &[u8]) -> bool; - fn get_password_file(repo: &R, id: &[u8]) -> Result; - fn set_password_file(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError>; - fn prereq_for_register(_repo: &R, _id: &[u8]) -> Result<(), AuthRepoError> { - Ok(()) - } -} - -#[derive(Clone)] -pub struct Key; -#[derive(Clone)] -pub struct Code; - -impl CredKind for Key { - fn has(repo: &R, id: &[u8]) -> bool { - repo.has_key(id) - } - fn get_password_file(repo: &R, id: &[u8]) -> Result { - repo.get_key_passcode_file(id) - } - fn set_password_file(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> { - repo.new_key(id, pf) - } -} - -impl CredKind for Code { - fn has(repo: &R, id: &[u8]) -> bool { - repo.has_code(id) - } - fn get_password_file(repo: &R, id: &[u8]) -> Result { - repo.get_code_passcode_file(id) - } - fn set_password_file(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> { - repo.new_code(id, pf) - } - fn prereq_for_register(repo: &R, id: &[u8]) -> Result<(), AuthRepoError> { - if repo.has_key(id) { - Ok(()) - } else { - Err(AuthRepoError::KeyNotRegistered) - } - } -} - -pub struct OpaqueAuth { - server_setup: NKodeServerSetup, - user_repo: R, - session: S, -} - -impl OpaqueAuth { - pub fn new(server_setup: NKodeServerSetup, user_repo: R, session: S) -> Self { - Self { server_setup, user_repo, session } - } - - pub async fn reg_start( - &mut self, - identifier: &[u8], - request: RegistrationRequest, - ) -> Result { - K::prereq_for_register(&self.user_repo, identifier) - .map_err(|e| format!("registration prereq failed: {e:?}"))?; - let start = ServerRegistration::::start( - &self.server_setup, - request, - identifier, - ).map_err(|e| format!("opaque reg start: {e:?}"))?; - let cache = self.session - .new_reg_session(identifier) - .map_err(|e| format!("reg cache: {e}"))?; - - Ok(OpaqueRegisterSession { session_id: cache.session_id, response: start.message }) - } - - pub async fn reg_finish( - &mut self, - session_id: &Uuid, - password_file: PasswordFile, - ) -> Result<(), String> { - let sess = self.session - .get_reg_session(session_id) - .map_err(|e| format!("get reg session: {e}"))?; - K::prereq_for_register(&self.user_repo, sess.identifier.as_slice()) - .map_err(|e| format!("registration prereq failed: {e:?}"))?; - K::set_password_file(&mut self.user_repo, sess.identifier.as_slice(), password_file) - .map_err(|e| format!("repo write: {e:?}"))?; - self.session - .clear_reg_session(session_id) - .map_err(|e| format!("clear reg session: {e}")) - } - - pub async fn login_start( - &mut self, - identifier: &[u8], - request: CredentialRequest, - ) -> Result { - let password_file = K::get_password_file(&self.user_repo, identifier) - .map_err(|e| format!("repo read: {e:?}"))?; - - let password_file = - ServerRegistration::::deserialize(password_file.as_slice()) - .map_err(|e| format!("pf deserialize: {e:?}"))?; - let mut server_rng = OsRng; - let start = ServerLogin::start( - &mut server_rng, - &self.server_setup, - Some(password_file), - request, - identifier, - ServerLoginParameters::default(), - ).map_err(|e| format!("opaque login start: {e:?}"))?; - let cache = self.session - .new_login_session(identifier, start.state) - .map_err(|e| format!("login cache: {e}"))?; - Ok(OpaqueLoginSession { session_id: cache.session_id, response: start.message }) - } - - pub async fn login_finish( - &mut self, - session_id: &Uuid, - finalize: CredentialFinalization, - ) -> Result { - let cache = self.session - .get_login_session(session_id) - .map_err(|e| format!("get login session: {e}"))?; - - let finish = cache.server_login - .finish(finalize, ServerLoginParameters::default()) - .map_err(|e| format!("opaque login finish: {e:?}"))?; - - self.session - .clear_login_session(session_id) - .map_err(|e| format!("clear login session: {e}"))?; - - Ok(OpaqueSessionKey::from_bytes(&finish.session_key.to_vec()).unwrap()) - } -} diff --git a/src/server/repository/opaque/in_memory/in_memory_auth_repo.rs b/src/server/repository/in_memory/in_memory_opaque_db.rs similarity index 92% rename from src/server/repository/opaque/in_memory/in_memory_auth_repo.rs rename to src/server/repository/in_memory/in_memory_opaque_db.rs index 4d7266d..bc15c95 100644 --- a/src/server/repository/opaque/in_memory/in_memory_auth_repo.rs +++ b/src/server/repository/in_memory/in_memory_opaque_db.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; use crate::shared::models::opaque::PasswordFile; -use crate::server::repository::opaque::repos::{OpaqueDatabaseRepo, AuthRepoError}; +use crate::server::repository::opaque_repo::{OpaqueDatabaseRepo, AuthRepoError}; #[derive(Debug, Default)] -pub struct InMemoryAuthRepo { +pub struct InMemoryOpaqueDB { key_entries: HashMap, code_entries: HashMap, } @@ -14,7 +14,7 @@ pub struct KeyID(Vec); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CodeID(Vec); -impl InMemoryAuthRepo { +impl InMemoryOpaqueDB { pub fn new() -> Self { Self::default() } @@ -28,7 +28,7 @@ impl InMemoryAuthRepo { } } -impl OpaqueDatabaseRepo for InMemoryAuthRepo { +impl OpaqueDatabaseRepo for InMemoryOpaqueDB { fn new_key( &mut self, identifier: &[u8], diff --git a/src/server/repository/opaque/in_memory/in_memory_auth_session.rs b/src/server/repository/in_memory/in_memory_opaque_session.rs similarity index 92% rename from src/server/repository/opaque/in_memory/in_memory_auth_session.rs rename to src/server/repository/in_memory/in_memory_opaque_session.rs index fa9db67..0270993 100644 --- a/src/server/repository/opaque/in_memory/in_memory_auth_session.rs +++ b/src/server/repository/in_memory/in_memory_opaque_session.rs @@ -1,23 +1,23 @@ use std::collections::HashMap; -use crate::server::opaque::{LoginCache, RegCache}; use opaque_ke::ServerLogin; use uuid::Uuid; +use crate::server::models::{LoginCache, RegCache}; use crate::shared::models::opaque::NKodeCipherSuite; -use crate::server::repository::opaque::repos::OpaqueSessionRepo; +use crate::server::repository::opaque_repo::OpaqueSessionRepo; #[derive(Default)] -pub struct InMemoryAuthSession { +pub struct InMemoryOpaqueSession { reg_sessions: HashMap, login_sessions: HashMap, } -impl InMemoryAuthSession { +impl InMemoryOpaqueSession { pub fn new() -> Self { Self::default() } } -impl OpaqueSessionRepo for InMemoryAuthSession { +impl OpaqueSessionRepo for InMemoryOpaqueSession { fn new_reg_session(&mut self, identifier: &[u8]) -> Result { let cache = RegCache { session_id: Uuid::new_v4(), diff --git a/src/server/repository/in_memory/in_memory_transport.rs b/src/server/repository/in_memory/in_memory_transport.rs new file mode 100644 index 0000000..10ffd70 --- /dev/null +++ b/src/server/repository/in_memory/in_memory_transport.rs @@ -0,0 +1,100 @@ +use async_trait::async_trait; +use std::marker::PhantomData; +use tokio::sync::Mutex; +use std::sync::Arc; +use uuid::Uuid; +use opaque_ke::{CredentialFinalization, CredentialRequest, RegistrationRequest}; +use crate::shared::models::opaque::{NKodeCipherSuite, NKodeServerSetup, OpaqueLoginSession, OpaqueRegisterSession, PasswordFile}; +use crate::client::opaque::{ClientAuthError, ServerConnectionLogin, ServerConnectionRegister}; +use crate::server::app::{Code, Key}; +use crate::server::app::ServerApp; +use crate::server::models::CredKind; +use crate::server::repository::in_memory::in_memory_opaque_db::InMemoryOpaqueDB; +use crate::server::repository::in_memory::in_memory_opaque_session::InMemoryOpaqueSession; +use crate::server::repository::in_memory::in_memory_user_db::InMemoryUserDB; +use crate::shared::models::app::LoggedInSession; + +#[derive(Clone)] +pub struct InMemoryServer { + auth_db: Arc>>, + _kind: PhantomData, +} + +impl InMemoryServer { + pub fn new(server_setup: NKodeServerSetup) -> Self { + Self { + auth_db: Arc::new(Mutex::new( + ServerApp::new( + server_setup, + InMemoryOpaqueDB::new(), + InMemoryOpaqueSession::new(), + InMemoryUserDB::new() + ) + )), + _kind: PhantomData, + } + } +} + +pub type InMemoryKeyServer = InMemoryServer; +pub type InMemoryCodeServer = InMemoryServer; + +#[async_trait] +impl ServerConnectionRegister for InMemoryServer +where + K: CredKind + Sync, +{ + async fn start( + &self, + identifier: &[u8], + message: &RegistrationRequest, + ) -> Result { + // Server API takes ownership; client trait gives us a reference. + // opaque-ke request types are typically Clone; if not, you'll need to adjust signatures. + self.auth_db.lock().await + .reg_start::(identifier, message.clone()) + .await + .map_err(|e| ClientAuthError::Transport(e)) + } + + async fn finish( + &self, + session_id: &Uuid, + password_file: PasswordFile, + ) -> Result<(), ClientAuthError> { + self.auth_db.lock().await + .reg_finish::(session_id, password_file) + .await + .map_err(|e| ClientAuthError::Transport(e)) + } +} + +#[async_trait] +impl ServerConnectionLogin for InMemoryServer +where + K: CredKind + Send + Sync, +{ + async fn start( + &self, + identifier: &[u8], + request: &CredentialRequest, + ) -> Result { + self.auth_db.lock().await + .login_start::(identifier, request.clone()) + .await + .map_err(|e| ClientAuthError::Transport(e)) + } + + async fn finish( + &self, + session_id: &Uuid, + message: &CredentialFinalization, + ) -> Result { + Ok(self + .auth_db.lock().await + .login_finish(session_id, message.clone()) + .await + .map_err(|e| ClientAuthError::Transport(e))? + ) + } +} diff --git a/src/server/repository/in_memory/in_memory_user_db.rs b/src/server/repository/in_memory/in_memory_user_db.rs new file mode 100644 index 0000000..8a06d1c --- /dev/null +++ b/src/server/repository/in_memory/in_memory_user_db.rs @@ -0,0 +1,67 @@ +use std::collections::HashMap; +use uuid::Uuid; +use crate::server::repository::user_repo::UserRepo; +use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession}; +use crate::shared::models::email::Email; + +pub struct InMemoryUserDB { + key_session: HashMap, + code_session: HashMap, + code_data: HashMap, +} + +impl InMemoryUserDB { + pub fn new() -> Self { + Self { + key_session: HashMap::new(), + code_session: HashMap::new(), + code_data: HashMap::new(), + } + } +} + +impl Default for InMemoryUserDB { + fn default() -> Self { + Self::new() + } +} + +impl UserRepo for InMemoryUserDB { + fn get_key_session(&mut self, session_id: &Uuid) -> Result { + self.key_session + .get(&session_id) + .cloned() + .ok_or_else(|| format!("key session not found for session_id={}", session_id)) + } + + fn get_code_session(&mut self, session_id: &Uuid) -> Result { + self.code_session + .get(&session_id) + .cloned() + .ok_or_else(|| format!("code session not found for session_id={}", session_id)) + } + + fn set_key_session(&mut self, session: KeyLoggedInSession) -> Result<(), String> { + // Assumes KeyLoggedInSession has a session_id: Uuid field (common pattern) + self.key_session.insert(session.0.session_id, session); + Ok(()) + } + + fn set_code_session(&mut self, session: CodeLoggedInSession) -> Result<(), String> { + // Assumes CodeLoggedInSession has a session_id: Uuid field (common pattern) + self.code_session.insert(session.0.session_id, session); + Ok(()) + } + + fn set_code_login_data(&mut self, email: Email, data: CodeLoginData) -> Result<(), String> { + self.code_data.insert(email, data); + Ok(()) + } + + fn get_code_login_data(&mut self, email: &Email) -> Result { + self.code_data + .get(email) + .cloned() + .ok_or_else(|| "code login data not found for email".to_string()) + } +} diff --git a/src/server/repository/in_memory/mod.rs b/src/server/repository/in_memory/mod.rs new file mode 100644 index 0000000..650e2c2 --- /dev/null +++ b/src/server/repository/in_memory/mod.rs @@ -0,0 +1,4 @@ +pub mod in_memory_opaque_db; +pub mod in_memory_transport; +pub mod in_memory_opaque_session; +mod in_memory_user_db; diff --git a/src/server/repository/mod.rs b/src/server/repository/mod.rs index 5517533..a35bfb1 100644 --- a/src/server/repository/mod.rs +++ b/src/server/repository/mod.rs @@ -1 +1,3 @@ -pub mod opaque; +pub mod in_memory; +pub mod opaque_repo; +pub mod user_repo; diff --git a/src/server/repository/opaque/in_memory/in_memory_transport.rs b/src/server/repository/opaque/in_memory/in_memory_transport.rs deleted file mode 100644 index 2245e70..0000000 --- a/src/server/repository/opaque/in_memory/in_memory_transport.rs +++ /dev/null @@ -1,163 +0,0 @@ -use async_trait::async_trait; -use std::marker::PhantomData; -use tokio::sync::Mutex; -use std::sync::Arc; -use uuid::Uuid; -use opaque_ke::{CredentialFinalization, CredentialRequest, RegistrationRequest}; -use crate::shared::models::opaque::{OpaqueLoginSession, NKodeCipherSuite, NKodeServerSetup, OpaqueSessionKey, PasswordFile, OpaqueRegisterSession}; -use crate::client::opaque::{ClientAuthError, ServerConnectionLogin, ServerConnectionRegister}; -use crate::server::opaque::{OpaqueAuth, CredKind, Key, Code}; -use crate::server::repository::opaque::in_memory::in_memory_auth_repo::InMemoryAuthRepo; -use crate::server::repository::opaque::in_memory::in_memory_auth_session::InMemoryAuthSession; - -#[derive(Clone)] -pub struct InMemoryServer { - auth: Arc>>, - _kind: PhantomData, -} - -impl InMemoryServer { - pub fn new(server_setup: NKodeServerSetup) -> Self { - Self { - auth: Arc::new(Mutex::new(OpaqueAuth::new(server_setup, InMemoryAuthRepo::new(), InMemoryAuthSession::new()))), - _kind: PhantomData, - } - } -} - -pub type InMemoryKeyServer = InMemoryServer; -pub type InMemoryCodeServer = InMemoryServer; - -#[async_trait] -impl ServerConnectionRegister for InMemoryServer -where - K: CredKind + Sync, -{ - async fn start( - &self, - identifier: &[u8], - message: &RegistrationRequest, - ) -> Result { - // Server API takes ownership; client trait gives us a reference. - // opaque-ke request types are typically Clone; if not, you'll need to adjust signatures. - self.auth.lock().await - .reg_start::(identifier, message.clone()) - .await - .map_err(|e| ClientAuthError::Transport(e)) - } - - async fn finish( - &self, - session_id: &Uuid, - password_file: PasswordFile, - ) -> Result<(), ClientAuthError> { - self.auth.lock().await - .reg_finish::(session_id, password_file) - .await - .map_err(|e| ClientAuthError::Transport(e)) - } -} - -#[async_trait] -impl ServerConnectionLogin for InMemoryServer -where - K: CredKind + Send + Sync, -{ - async fn start( - &self, - identifier: &[u8], - request: &CredentialRequest, - ) -> Result { - self.auth.lock().await - .login_start::(identifier, request.clone()) - .await - .map_err(|e| ClientAuthError::Transport(e)) - } - - async fn finish( - &self, - session_id: &Uuid, - message: &CredentialFinalization, - ) -> Result { - // Server computes its own session key too; we just need it to validate and complete. - let key = self - .auth.lock().await - .login_finish::(session_id, message.clone()) - .await - .map_err(|e| ClientAuthError::Transport(e))?; - - Ok(key) - } -} - -// pub struct InMemSharedServer { -// inner: Arc>>, -// _k: PhantomData, -// } -// -// impl InMemSharedServer { -// pub fn new(inner: Arc>>) -> Self { -// Self { inner, _k: PhantomData } -// } -// } -// -// #[async_trait::async_trait] -// impl ServerConnectionRegister for InMemSharedServer -// where -// K: CredKind + Send + Sync, -// { -// async fn start( -// &mut self, -// identifier: &[u8], -// message: &RegistrationRequest, -// ) -> Result { -// let mut guard = self.inner.lock().await; -// guard -// .reg_start::(identifier, message.clone()) -// .await -// .map_err(ClientAuthError::Transport) -// } -// -// async fn finish( -// &mut self, -// session_id: &Uuid, -// password_file: PasswordFile, -// ) -> Result<(), ClientAuthError> { -// let mut guard = self.inner.lock().await; -// guard -// .reg_finish::(session_id, password_file) -// .await -// .map_err(ClientAuthError::Transport) -// } -// } -// -// #[async_trait::async_trait] -// impl ServerConnectionLogin for InMemSharedServer -// where -// K: CredKind + Send + Sync, -// { -// async fn start( -// &mut self, -// identifier: &[u8], -// request: &CredentialRequest, -// ) -> Result { -// let mut guard = self.inner.lock().await; -// guard -// .login_start::(identifier, request.clone()) -// .await -// .map_err(ClientAuthError::Transport) -// } -// -// async fn finish( -// &mut self, -// session_id: &Uuid, -// message: &CredentialFinalization, -// ) -> Result { -// let mut guard = self.inner.lock().await; -// let key = guard -// .login_finish::(session_id, message.clone()) -// .await -// .map_err(ClientAuthError::Transport)?; -// Ok(key) -// } -// } diff --git a/src/server/repository/opaque/in_memory/mod.rs b/src/server/repository/opaque/in_memory/mod.rs deleted file mode 100644 index f6d7043..0000000 --- a/src/server/repository/opaque/in_memory/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod in_memory_auth_repo; -pub mod in_memory_transport; -pub mod in_memory_auth_session; \ No newline at end of file diff --git a/src/server/repository/opaque/mod.rs b/src/server/repository/opaque/mod.rs deleted file mode 100644 index 98a9693..0000000 --- a/src/server/repository/opaque/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod in_memory; -pub mod repos; \ No newline at end of file diff --git a/src/server/repository/opaque/repos.rs b/src/server/repository/opaque_repo.rs similarity index 96% rename from src/server/repository/opaque/repos.rs rename to src/server/repository/opaque_repo.rs index ac1910c..2b30d42 100644 --- a/src/server/repository/opaque/repos.rs +++ b/src/server/repository/opaque_repo.rs @@ -1,8 +1,7 @@ use uuid::Uuid; use opaque_ke::ServerLogin; +use crate::server::models::{LoginCache, RegCache}; use crate::shared::models::opaque::{NKodeCipherSuite, PasswordFile}; -use crate::server::opaque::{LoginCache, RegCache}; - #[derive(Debug)] pub enum AuthRepoError { UserExists, diff --git a/src/server/repository/user_repo.rs b/src/server/repository/user_repo.rs new file mode 100644 index 0000000..a7aa429 --- /dev/null +++ b/src/server/repository/user_repo.rs @@ -0,0 +1,14 @@ +use uuid::Uuid; +use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession}; +use crate::shared::models::email::Email; + +pub trait UserRepo { + fn get_key_session(&mut self, session_id: &Uuid) -> Result; + fn get_code_session(&mut self, session_id: &Uuid) -> Result; + + fn set_key_session(&mut self, session: KeyLoggedInSession) -> Result<(), String>; + fn set_code_session(&mut self, session: CodeLoggedInSession) -> Result<(), String>; + + fn set_code_login_data(&mut self, email: Email, data: CodeLoginData) -> Result<(), String>; + fn get_code_login_data(&mut self, email: &Email) -> Result; +} \ No newline at end of file diff --git a/src/shared/models/app.rs b/src/shared/models/app.rs index ee06c5f..45ca8b1 100644 --- a/src/shared/models/app.rs +++ b/src/shared/models/app.rs @@ -4,15 +4,23 @@ use serde::{Deserialize, Serialize}; use getset::Getters; use nkode_rs::from_bytes::FromBytes; use nkode_rs::nkode_core::policy::NKodePolicy; +use uuid::Uuid; use crate::shared::models::email::Email; use crate::shared::models::opaque::{OpaqueSessionKey, UserSecretKey}; -pub struct LoginSession { + + +#[derive(Debug, Clone)] +pub struct LoggedInSession { + pub(crate) session_id: Uuid, pub(crate) email: Email, pub(crate) session_key: OpaqueSessionKey, } -pub struct KeyLoginSession(pub(crate) LoginSession); -pub struct CodeLoginSession(pub(crate) LoginSession); +#[derive(Debug, Clone)] +pub struct KeyLoggedInSession(pub(crate) LoggedInSession); + +#[derive(Debug, Clone)] +pub struct CodeLoggedInSession(pub(crate) LoggedInSession); pub const ICON_ID_SIZE: usize = 32; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -53,16 +61,16 @@ pub struct CodeLoginData { #[async_trait] pub trait OpaqueAPI { async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String>; - async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoginSession, data: &CodeLoginData) -> Result<(), String>; - async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result; - async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result; + async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, data: &CodeLoginData) -> Result<(), String>; + async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result; + async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result; } #[async_trait] pub trait AuthAPI { - async fn get_new_icons(&self, key_login_session: &KeyLoginSession) -> Result, String>; - async fn get_login_data(&self, key_login_session: &KeyLoginSession) -> Result; - async fn is_code_registered(&self, key_login_session: &KeyLoginSession) -> Result; + async fn get_new_icons(&self, key_login_session: &KeyLoggedInSession) -> Result, String>; + async fn get_login_data(&self, key_login_session: &KeyLoggedInSession) -> Result; + async fn is_code_registered(&self, key_login_session: &KeyLoggedInSession) -> Result; async fn get_policy(&self) -> Result; } diff --git a/src/shared/models/opaque.rs b/src/shared/models/opaque.rs index 6249f27..2fa4641 100644 --- a/src/shared/models/opaque.rs +++ b/src/shared/models/opaque.rs @@ -33,7 +33,7 @@ impl FromBytes for UserSecretKey { const OPAQUE_SESSION_KEY_SIZE: usize = 64; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct OpaqueSessionKey(Zeroizing<[u8; OPAQUE_SESSION_KEY_SIZE]>); impl FromBytes for OpaqueSessionKey { diff --git a/tests/in_memory_test.rs b/tests/in_memory_test.rs index d54048f..d0f8061 100644 --- a/tests/in_memory_test.rs +++ b/tests/in_memory_test.rs @@ -1,7 +1,7 @@ use opaque_ke::rand::rngs::OsRng; use nkode_protocol::client::opaque::{AuthenticationData, ClientAuthError, OpaqueAuthLogin, OpaqueAuthRegister}; use nkode_protocol::shared::models::opaque::NKodeServerSetup; -use nkode_protocol::server::repository::opaque::in_memory::in_memory_transport::{InMemoryCodeServer, InMemoryKeyServer}; +use nkode_protocol::server::repository::in_memory::in_memory_transport::{InMemoryCodeServer, InMemoryKeyServer}; #[tokio::test] async fn opaque_key_registration_and_login_roundtrip() { @@ -15,7 +15,6 @@ async fn opaque_key_registration_and_login_roundtrip() { let _ =login_reg.login(&auth_data) .await .expect("login should succeed"); - // assert!(!session_key.is_empty()); } #[tokio::test]