refactor file struct

This commit is contained in:
2025-12-14 11:49:50 -06:00
parent e6a7dc4993
commit 71552911b1
18 changed files with 86 additions and 76 deletions

1
src/repository/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod opaque;

View File

@@ -0,0 +1,86 @@
use std::collections::HashMap;
use crate::models::opaque::PasswordFile;
use crate::repository::opaque::repos::{OpaqueDatabaseRepo, AuthRepoError};
#[derive(Debug, Default)]
pub struct InMemoryAuthRepo {
key_entries: HashMap<KeyID, PasswordFile>,
code_entries: HashMap<CodeID, PasswordFile>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyID(Vec<u8>);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CodeID(Vec<u8>);
impl InMemoryAuthRepo {
pub fn new() -> Self {
Self::default()
}
fn code_exists(&self, identifier: &CodeID) -> bool {
self.code_entries.contains_key(identifier)
}
fn key_exists(&self, identifier: &KeyID) -> bool {
self.key_entries.contains_key(identifier)
}
}
impl OpaqueDatabaseRepo for InMemoryAuthRepo {
fn new_key(
&mut self,
identifier: &[u8],
password_file: PasswordFile,
) -> Result<(), AuthRepoError> {
if self.key_exists(&KeyID(identifier.to_vec())) {
return Err(AuthRepoError::UserExists);
}
self.key_entries
.insert(KeyID(identifier.to_vec()), password_file);
Ok(())
}
fn new_code(
&mut self,
identifier: &[u8],
password_file: PasswordFile,
) -> Result<(), AuthRepoError> {
if !self.has_key(identifier) {
return Err(AuthRepoError::KeyNotRegistered);
}
if self.code_exists(&CodeID(identifier.to_vec())) {
return Err(AuthRepoError::UserExists);
}
self.code_entries
.insert(CodeID(identifier.to_vec()), password_file);
Ok(())
}
fn has_code(&self, identifier: &[u8]) -> bool {
self.code_entries
.contains_key(&CodeID(identifier.to_vec()))
}
fn has_key(&self, identifier: &[u8]) -> bool {
self.key_entries
.contains_key(&KeyID(identifier.to_vec()))
}
fn get_key_passcode_file(&self, identifier: &[u8]) -> Result<PasswordFile, AuthRepoError> {
self.key_entries
.get(&KeyID(identifier.to_vec()))
.cloned()
.ok_or(AuthRepoError::KeyNotRegistered)
}
fn get_code_passcode_file(&self, identifier: &[u8]) -> Result<PasswordFile, AuthRepoError> {
self.code_entries
.get(&CodeID(identifier.to_vec()))
.cloned()
.ok_or(AuthRepoError::CodeNotRegistered)
}
}

View File

@@ -0,0 +1,102 @@
use std::collections::HashMap;
use crate::opaque::server::{LoginCache, RegCache};
use opaque_ke::ServerLogin;
use uuid::Uuid;
use crate::models::opaque::NKodeCipherSuite;
use crate::repository::opaque::repos::OpaqueSessionRepo;
#[derive(Default)]
pub struct InMemoryAuthSession {
reg_sessions: HashMap<Uuid, RegCache>,
login_sessions: HashMap<Uuid, LoginCache>,
}
impl InMemoryAuthSession {
pub fn new() -> Self {
Self::default()
}
}
impl OpaqueSessionRepo for InMemoryAuthSession {
fn new_reg_session(&mut self, identifier: &[u8]) -> Result<RegCache, String> {
let cache = RegCache {
session_id: Uuid::new_v4(),
identifier: identifier.to_vec(),
};
// Extremely unlikely collision, but keep the invariant anyway.
if self.reg_sessions.contains_key(&cache.session_id) {
return Err("session_id collision".to_string());
}
self.reg_sessions.insert(cache.session_id, RegCache {
session_id: cache.session_id,
identifier: cache.identifier.clone(),
});
Ok(cache)
}
fn get_reg_session(&self, session_id: &Uuid) -> Result<RegCache, String> {
self.reg_sessions
.get(session_id)
.map(|c| RegCache {
session_id: c.session_id,
identifier: c.identifier.clone(),
})
.ok_or_else(|| "registration session not found".to_string())
}
fn clear_reg_session(&mut self, session_id: &Uuid) -> Result<(), String> {
self.reg_sessions
.remove(session_id)
.map(|_| ())
.ok_or_else(|| "registration session not found".to_string())
}
fn new_login_session(
&mut self,
identifier: &[u8],
server_login: ServerLogin<NKodeCipherSuite>,
) -> Result<LoginCache, String> {
let cache = LoginCache {
session_id: Uuid::new_v4(),
identifiers: identifier.to_vec(),
server_login,
};
if self.login_sessions.contains_key(&cache.session_id) {
return Err("session_id collision".to_string());
}
self.login_sessions.insert(
cache.session_id,
LoginCache {
session_id: cache.session_id,
identifiers: cache.identifiers.clone(),
// move is fine; we already moved into cache, so clone to keep both:
server_login: cache.server_login.clone(),
},
);
Ok(cache)
}
fn get_login_session(&self, session_id: &Uuid) -> Result<LoginCache, String> {
self.login_sessions
.get(session_id)
.map(|c| LoginCache {
session_id: c.session_id,
identifiers: c.identifiers.clone(),
server_login: c.server_login.clone(),
})
.ok_or_else(|| "login session not found".to_string())
}
fn clear_login_session(&mut self, session_id: &Uuid) -> Result<(), String> {
self.login_sessions
.remove(session_id)
.map(|_| ())
.ok_or_else(|| "login session not found".to_string())
}
}

View File

@@ -0,0 +1,162 @@
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::models::opaque::{LoginSession, NKodeCipherSuite, NKodeServerSetup, PasswordFile, RegisterSession};
use crate::opaque::client::{ClientAuthError, ServerConnectionLogin, ServerConnectionRegister};
use crate::opaque::server::{OpaqueAuth, CredKind, Key, Code};
use crate::repository::opaque::in_memory::in_memory_auth_repo::InMemoryAuthRepo;
use crate::repository::opaque::in_memory::in_memory_auth_session::InMemoryAuthSession;
pub struct InMemoryServer<K: CredKind> {
auth: OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>,
_kind: PhantomData<K>,
}
impl<K: CredKind> InMemoryServer<K> {
pub fn new(server_setup: NKodeServerSetup) -> Self {
Self {
auth: 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 + Send + Sync,
{
async fn start(
&mut self,
identifier: &[u8],
message: &RegistrationRequest<NKodeCipherSuite>,
) -> Result<RegisterSession, 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
.reg_start::<K>(identifier, message.clone())
.await
.map_err(|e| ClientAuthError::Transport(e))
}
async fn finish(
&mut self,
session_id: &Uuid,
password_file: PasswordFile,
) -> Result<(), ClientAuthError> {
self.auth
.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(
&mut self,
identifier: &[u8],
request: &CredentialRequest<NKodeCipherSuite>,
) -> Result<LoginSession, ClientAuthError> {
self.auth
.login_start::<K>(identifier, request.clone())
.await
.map_err(|e| ClientAuthError::Transport(e))
}
async fn finish(
&mut self,
session_id: &Uuid,
message: &CredentialFinalization<NKodeCipherSuite>,
) -> Result<(), ClientAuthError> {
// Server computes its own session key too; we just need it to validate and complete.
let _server_session_key = self
.auth
.login_finish::<K>(session_id, message.clone())
.await
.map_err(|e| ClientAuthError::Transport(e))?;
Ok(())
}
}
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<(), ClientAuthError> {
let mut guard = self.inner.lock().await;
let _ = guard
.login_finish::<K>(session_id, message.clone())
.await
.map_err(ClientAuthError::Transport)?;
Ok(())
}
}

View File

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

View File

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

View File

@@ -0,0 +1,36 @@
use uuid::Uuid;
use opaque_ke::ServerLogin;
use crate::models::opaque::{NKodeCipherSuite, PasswordFile};
use crate::opaque::server::{LoginCache, RegCache};
#[derive(Debug)]
pub enum AuthRepoError {
UserExists,
KeyNotRegistered,
CodeNotRegistered,
}
pub trait OpaqueDatabaseRepo {
fn new_key(&mut self, identifier: &[u8], password_file: PasswordFile) -> Result<(), AuthRepoError>;
fn new_code(&mut self, identifier: &[u8], password_file: PasswordFile) -> Result<(), AuthRepoError>;
fn has_code(&self, identifier: &[u8]) -> bool;
fn has_key(&self, identifier: &[u8]) -> bool;
fn get_key_passcode_file(&self, identifier: &[u8]) -> Result<PasswordFile, AuthRepoError>;
fn get_code_passcode_file(&self, identifier: &[u8]) -> Result<PasswordFile, AuthRepoError>;
}
pub trait OpaqueSessionRepo {
fn new_reg_session(&mut self, identifier: &[u8]) -> Result<RegCache, String>;
fn get_reg_session(&self, session_id: &Uuid) -> Result<RegCache, String>;
fn clear_reg_session(&mut self, session_id: &Uuid) -> Result<(), String>;
fn new_login_session(
&mut self,
identifier: &[u8],
server_login: ServerLogin<NKodeCipherSuite>,
) -> Result<LoginCache, String>;
fn get_login_session(&self, session_id: &Uuid) -> Result<LoginCache, String>;
fn clear_login_session(&mut self, session_id: &Uuid) -> Result<(), String>;
}