refactor auth_repo to server_app

This commit is contained in:
2025-12-17 12:29:17 -06:00
parent 467f4feadd
commit 3029a41386
20 changed files with 450 additions and 375 deletions

View File

@@ -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<S: OpaqueAPI> {
api: S,
email: Email,
user_secret_key: UserSecretKey,
key_login: KeyLoginSession,
key_login: KeyLoggedInSession,
}
impl <S: OpaqueAPI + AuthAPI> ClientAppKeyLoggedIn<S> {
@@ -104,7 +104,7 @@ pub struct ClientAppCodeRegister<S: OpaqueAPI> {
api: S,
email: Email,
user_secret_key: UserSecretKey,
key_login: KeyLoginSession,
key_login: KeyLoggedInSession,
icon_nonce: Nonce,
icons: Vec<Icon>,
keypad: Keypad,
@@ -156,7 +156,7 @@ impl <S: OpaqueAPI>ClientAppCodeLogin<S> {
).unwrap().to_vec()
}
pub async fn login(&self, selected_keys: &Vec<usize>) -> Result<CodeLoginSession, String> {
pub async fn login(&self, selected_keys: &Vec<usize>) -> Result<CodeLoggedInSession, String> {
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<KeyLoginSession, String> {
async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoggedInSession, String> {
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<CodeLoginSession, String> {
async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result<CodeLoggedInSession, String> {
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<Vec<Icon>, 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<CodeLoginData, String> {
self.nkode_api
.get_login_data(key_login_session)
.await
}
async fn is_code_registered(&self, key_login_session: &KeyLoginSession) -> Result<bool, String> {
async fn is_code_registered(&self, key_login_session: &KeyLoggedInSession) -> Result<bool, String> {
self.nkode_api.is_code_registered(key_login_session).await
}

View File

@@ -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<NKodeCipherSuite>,
) -> Result<OpaqueSessionKey, ClientAuthError>;
) -> Result<LoggedInSession, ClientAuthError>;
}

View File

@@ -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<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> {
server_setup: NKodeServerSetup,
opaque_db: R,
opaque_sess: S,
user_db: U,
}
impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> ServerApp<R, S, U> {
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<K: CredKind>(
&mut self,
identifier: &[u8],
request: RegistrationRequest<NKodeCipherSuite>,
) -> Result<OpaqueRegisterSession, String> {
K::prereq_for_register(&self.opaque_db, identifier)
.map_err(|e| format!("registration prereq failed: {e:?}"))?;
let start = ServerRegistration::<NKodeCipherSuite>::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<K: CredKind>(
&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<K: CredKind>(
&mut self,
identifier: &[u8],
request: CredentialRequest<NKodeCipherSuite>,
) -> Result<OpaqueLoginSession, String> {
let password_file = K::get_password_file(&self.opaque_db, identifier)
.map_err(|e| format!("repo read: {e:?}"))?;
let password_file =
ServerRegistration::<NKodeCipherSuite>::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<NKodeCipherSuite>,
) -> Result<LoggedInSession, String> {
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<NKodeCipherSuite>,
) -> Result<KeyLoggedInSession, String> {
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<NKodeCipherSuite>,
) -> Result<CodeLoggedInSession, String> {
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<KeyLoggedInSession, String> {
self.user_db.get_key_session(session_id)
}
pub async fn get_code_session(&mut self, session_id: &Uuid) -> Result<CodeLoggedInSession, String> {
self.user_db.get_code_session(session_id)
}
pub async fn get_code_login_data(&mut self, email: &Email) -> Result<CodeLoginData, String> {
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<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> bool {
repo.has_key(id)
}
fn get_password_file<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> Result<PasswordFile, AuthRepoError> {
repo.get_key_passcode_file(id)
}
fn set_password_file<R: OpaqueDatabaseRepo>(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> {
repo.new_key(id, pf)
}
}
impl CredKind for Code {
fn has<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> bool {
repo.has_code(id)
}
fn get_password_file<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> Result<PasswordFile, AuthRepoError> {
repo.get_code_passcode_file(id)
}
fn set_password_file<R: OpaqueDatabaseRepo>(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> {
repo.new_code(id, pf)
}
fn prereq_for_register<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> Result<(), AuthRepoError> {
if repo.has_key(id) {
Ok(())
} else {
Err(AuthRepoError::KeyNotRegistered)
}
}
}

View File

@@ -1,3 +1,3 @@
pub mod app;
pub mod repository;
pub mod opaque;
mod models;

24
src/server/models.rs Normal file
View File

@@ -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<u8>,
}
pub struct LoginCache {
pub session_id: Uuid,
pub identifiers: Vec<u8>,
pub server_login: ServerLogin<NKodeCipherSuite>,
}
pub trait CredKind {
fn has<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> bool;
fn get_password_file<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> Result<PasswordFile, AuthRepoError>;
fn set_password_file<R: OpaqueDatabaseRepo>(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError>;
fn prereq_for_register<R: OpaqueDatabaseRepo>(_repo: &R, _id: &[u8]) -> Result<(), AuthRepoError> {
Ok(())
}
}

View File

@@ -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<u8>,
}
pub struct LoginCache {
pub session_id: Uuid,
pub identifiers: Vec<u8>,
pub server_login: ServerLogin<NKodeCipherSuite>,
}
pub trait CredKind {
fn has<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> bool;
fn get_password_file<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> Result<PasswordFile, AuthRepoError>;
fn set_password_file<R: OpaqueDatabaseRepo>(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError>;
fn prereq_for_register<R: OpaqueDatabaseRepo>(_repo: &R, _id: &[u8]) -> Result<(), AuthRepoError> {
Ok(())
}
}
#[derive(Clone)]
pub struct Key;
#[derive(Clone)]
pub struct Code;
impl CredKind for Key {
fn has<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> bool {
repo.has_key(id)
}
fn get_password_file<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> Result<PasswordFile, AuthRepoError> {
repo.get_key_passcode_file(id)
}
fn set_password_file<R: OpaqueDatabaseRepo>(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> {
repo.new_key(id, pf)
}
}
impl CredKind for Code {
fn has<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> bool {
repo.has_code(id)
}
fn get_password_file<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> Result<PasswordFile, AuthRepoError> {
repo.get_code_passcode_file(id)
}
fn set_password_file<R: OpaqueDatabaseRepo>(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> {
repo.new_code(id, pf)
}
fn prereq_for_register<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> Result<(), AuthRepoError> {
if repo.has_key(id) {
Ok(())
} else {
Err(AuthRepoError::KeyNotRegistered)
}
}
}
pub struct OpaqueAuth<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo> {
server_setup: NKodeServerSetup,
user_repo: R,
session: S,
}
impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo> OpaqueAuth<R, S> {
pub fn new(server_setup: NKodeServerSetup, user_repo: R, session: S) -> Self {
Self { server_setup, user_repo, session }
}
pub async fn reg_start<K: CredKind>(
&mut self,
identifier: &[u8],
request: RegistrationRequest<NKodeCipherSuite>,
) -> Result<OpaqueRegisterSession, String> {
K::prereq_for_register(&self.user_repo, identifier)
.map_err(|e| format!("registration prereq failed: {e:?}"))?;
let start = ServerRegistration::<NKodeCipherSuite>::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<K: CredKind>(
&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<K: CredKind>(
&mut self,
identifier: &[u8],
request: CredentialRequest<NKodeCipherSuite>,
) -> Result<OpaqueLoginSession, String> {
let password_file = K::get_password_file(&self.user_repo, identifier)
.map_err(|e| format!("repo read: {e:?}"))?;
let password_file =
ServerRegistration::<NKodeCipherSuite>::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<K: CredKind>(
&mut self,
session_id: &Uuid,
finalize: CredentialFinalization<NKodeCipherSuite>,
) -> Result<OpaqueSessionKey, String> {
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())
}
}

View File

@@ -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<KeyID, PasswordFile>,
code_entries: HashMap<CodeID, PasswordFile>,
}
@@ -14,7 +14,7 @@ pub struct KeyID(Vec<u8>);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CodeID(Vec<u8>);
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],

View File

@@ -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<Uuid, RegCache>,
login_sessions: HashMap<Uuid, LoginCache>,
}
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<RegCache, String> {
let cache = RegCache {
session_id: Uuid::new_v4(),

View File

@@ -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<K: CredKind> {
auth_db: Arc<Mutex<ServerApp<InMemoryOpaqueDB, InMemoryOpaqueSession, InMemoryUserDB>>>,
_kind: PhantomData<K>,
}
impl<K: CredKind> InMemoryServer<K> {
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<Key>;
pub type InMemoryCodeServer = InMemoryServer<Code>;
#[async_trait]
impl<K> ServerConnectionRegister for InMemoryServer<K>
where
K: CredKind + Sync,
{
async fn start(
&self,
identifier: &[u8],
message: &RegistrationRequest<NKodeCipherSuite>,
) -> Result<OpaqueRegisterSession, ClientAuthError> {
// 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::<K>(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::<K>(session_id, password_file)
.await
.map_err(|e| ClientAuthError::Transport(e))
}
}
#[async_trait]
impl<K> ServerConnectionLogin for InMemoryServer<K>
where
K: CredKind + Send + Sync,
{
async fn start(
&self,
identifier: &[u8],
request: &CredentialRequest<NKodeCipherSuite>,
) -> Result<OpaqueLoginSession, ClientAuthError> {
self.auth_db.lock().await
.login_start::<K>(identifier, request.clone())
.await
.map_err(|e| ClientAuthError::Transport(e))
}
async fn finish(
&self,
session_id: &Uuid,
message: &CredentialFinalization<NKodeCipherSuite>,
) -> Result<LoggedInSession, ClientAuthError> {
Ok(self
.auth_db.lock().await
.login_finish(session_id, message.clone())
.await
.map_err(|e| ClientAuthError::Transport(e))?
)
}
}

View File

@@ -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<Uuid, KeyLoggedInSession>,
code_session: HashMap<Uuid, CodeLoggedInSession>,
code_data: HashMap<Email, CodeLoginData>,
}
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<KeyLoggedInSession, String> {
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<CodeLoggedInSession, String> {
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<CodeLoginData, String> {
self.code_data
.get(email)
.cloned()
.ok_or_else(|| "code login data not found for email".to_string())
}
}

View File

@@ -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;

View File

@@ -1 +1,3 @@
pub mod opaque;
pub mod in_memory;
pub mod opaque_repo;
pub mod user_repo;

View File

@@ -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<K: CredKind> {
auth: Arc<Mutex<OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>>>,
_kind: PhantomData<K>,
}
impl<K: CredKind> InMemoryServer<K> {
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<Key>;
pub type InMemoryCodeServer = InMemoryServer<Code>;
#[async_trait]
impl<K> ServerConnectionRegister for InMemoryServer<K>
where
K: CredKind + Sync,
{
async fn start(
&self,
identifier: &[u8],
message: &RegistrationRequest<NKodeCipherSuite>,
) -> Result<OpaqueRegisterSession, ClientAuthError> {
// 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::<K>(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::<K>(session_id, password_file)
.await
.map_err(|e| ClientAuthError::Transport(e))
}
}
#[async_trait]
impl<K> ServerConnectionLogin for InMemoryServer<K>
where
K: CredKind + Send + Sync,
{
async fn start(
&self,
identifier: &[u8],
request: &CredentialRequest<NKodeCipherSuite>,
) -> Result<OpaqueLoginSession, ClientAuthError> {
self.auth.lock().await
.login_start::<K>(identifier, request.clone())
.await
.map_err(|e| ClientAuthError::Transport(e))
}
async fn finish(
&self,
session_id: &Uuid,
message: &CredentialFinalization<NKodeCipherSuite>,
) -> Result<OpaqueSessionKey, ClientAuthError> {
// Server computes its own session key too; we just need it to validate and complete.
let key = self
.auth.lock().await
.login_finish::<K>(session_id, message.clone())
.await
.map_err(|e| ClientAuthError::Transport(e))?;
Ok(key)
}
}
// pub struct InMemSharedServer<K> {
// inner: Arc<Mutex<OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>>>,
// _k: PhantomData<K>,
// }
//
// impl<K> InMemSharedServer<K> {
// pub fn new(inner: Arc<Mutex<OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>>>) -> Self {
// Self { inner, _k: PhantomData }
// }
// }
//
// #[async_trait::async_trait]
// impl<K> ServerConnectionRegister for InMemSharedServer<K>
// where
// K: CredKind + Send + Sync,
// {
// async fn start(
// &mut self,
// identifier: &[u8],
// message: &RegistrationRequest<NKodeCipherSuite>,
// ) -> Result<RegisterSession, ClientAuthError> {
// let mut guard = self.inner.lock().await;
// guard
// .reg_start::<K>(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::<K>(session_id, password_file)
// .await
// .map_err(ClientAuthError::Transport)
// }
// }
//
// #[async_trait::async_trait]
// impl<K> ServerConnectionLogin for InMemSharedServer<K>
// where
// K: CredKind + Send + Sync,
// {
// async fn start(
// &mut self,
// identifier: &[u8],
// request: &CredentialRequest<NKodeCipherSuite>,
// ) -> Result<LoginSession, ClientAuthError> {
// let mut guard = self.inner.lock().await;
// guard
// .login_start::<K>(identifier, request.clone())
// .await
// .map_err(ClientAuthError::Transport)
// }
//
// async fn finish(
// &mut self,
// session_id: &Uuid,
// message: &CredentialFinalization<NKodeCipherSuite>,
// ) -> Result<OpaqueSessionKey, ClientAuthError> {
// let mut guard = self.inner.lock().await;
// let key = guard
// .login_finish::<K>(session_id, message.clone())
// .await
// .map_err(ClientAuthError::Transport)?;
// Ok(key)
// }
// }

View File

@@ -1,3 +0,0 @@
pub mod in_memory_auth_repo;
pub mod in_memory_transport;
pub mod in_memory_auth_session;

View File

@@ -1,2 +0,0 @@
pub mod in_memory;
pub mod repos;

View File

@@ -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,

View File

@@ -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<KeyLoggedInSession, String>;
fn get_code_session(&mut self, session_id: &Uuid) -> Result<CodeLoggedInSession, String>;
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<CodeLoginData, String>;
}

View File

@@ -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<KeyLoginSession, String>;
async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result<CodeLoginSession, String>;
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<KeyLoggedInSession, String>;
async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result<CodeLoggedInSession, String>;
}
#[async_trait]
pub trait AuthAPI {
async fn get_new_icons(&self, key_login_session: &KeyLoginSession) -> Result<Vec<Icon>, String>;
async fn get_login_data(&self, key_login_session: &KeyLoginSession) -> Result<CodeLoginData, String>;
async fn is_code_registered(&self, key_login_session: &KeyLoginSession) -> Result<bool, String>;
async fn get_new_icons(&self, key_login_session: &KeyLoggedInSession) -> Result<Vec<Icon>, String>;
async fn get_login_data(&self, key_login_session: &KeyLoggedInSession) -> Result<CodeLoginData, String>;
async fn is_code_registered(&self, key_login_session: &KeyLoggedInSession) -> Result<bool, String>;
async fn get_policy(&self) -> Result<NKodePolicy, String>;
}

View File

@@ -33,7 +33,7 @@ impl FromBytes<USER_KEY_SIZE> 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<OPAQUE_SESSION_KEY_SIZE> for OpaqueSessionKey {

View File

@@ -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]