test client server opaque

This commit is contained in:
2025-12-13 16:55:17 -06:00
parent 246fd97f5b
commit e0f79344a6
10 changed files with 572 additions and 224 deletions

View File

@@ -5,16 +5,13 @@ use opaque_ke::rand::rngs::OsRng;
use opaque_ke::{
ClientLogin, ClientLoginFinishParameters,
ClientRegistration, ClientRegistrationFinishParameters,
CredentialFinalization, CredentialRequest, CredentialResponse,
RegistrationRequest, RegistrationResponse,
CredentialFinalization, CredentialRequest,
RegistrationRequest,
};
use crate::models::{
NKodeCipherSuite,
PasswordFile,
KeyLoginSession,
KeyRegisterSession,
};
use crate::models::{RegisterSession, LoginSession, NKodeCipherSuite, PasswordFile};
#[derive(Debug)]
pub enum ClientAuthError {
@@ -22,8 +19,6 @@ pub enum ClientAuthError {
Transport(String),
}
// --- Normalize auth inputs to (identifier, secret-bytes) ---
pub struct AuthenticationData {
pub identifier: Vec<u8>,
pub secret: Vec<u8>,
@@ -50,40 +45,13 @@ impl AuthenticationData {
}
}
// --- Small adapter traits so server can return any “session wrapper” type ---
pub trait RegStartSession {
fn session_id(&self) -> &Uuid;
fn response(&self) -> &RegistrationResponse<NKodeCipherSuite>;
}
pub trait LoginStartSession {
fn session_id(&self) -> &Uuid;
fn response(&self) -> &CredentialResponse<NKodeCipherSuite>;
}
// If your protocol types already have these methods, just implement the traits:
impl RegStartSession for KeyRegisterSession {
fn session_id(&self) -> &Uuid { self.session_id() }
fn response(&self) -> &RegistrationResponse<NKodeCipherSuite> { self.response() }
}
impl LoginStartSession for KeyLoginSession {
fn session_id(&self) -> &Uuid { self.session_id() }
fn response(&self) -> &CredentialResponse<NKodeCipherSuite> { self.response() }
}
// --- Server connection traits: generic over returned session wrapper types ---
#[async_trait]
pub trait ServerConnectionRegister {
type Start: RegStartSession + Send;
async fn start(
&mut self,
identifier: &[u8],
message: &RegistrationRequest<NKodeCipherSuite>,
) -> Result<Self::Start, ClientAuthError>;
) -> Result<RegisterSession, ClientAuthError>;
async fn finish(
&mut self,
@@ -94,13 +62,11 @@ pub trait ServerConnectionRegister {
#[async_trait]
pub trait ServerConnectionLogin {
type Start: LoginStartSession + Send;
async fn start(
&mut self,
identifier: &[u8],
request: &CredentialRequest<NKodeCipherSuite>,
) -> Result<Self::Start, ClientAuthError>;
) -> Result<LoginSession, ClientAuthError>;
async fn finish(
&mut self,
@@ -125,7 +91,7 @@ impl OpaqueAuthentication {
.start(&auth.identifier, &start.message)
.await
.map_err(|e| ClientAuthError::Transport(format!("server reg start: {e:?}")))?;
let server_msg = server_start.response().clone();
let server_msg = server_start.response;
let finish = start
.state
.finish(
@@ -135,10 +101,9 @@ impl OpaqueAuthentication {
ClientRegistrationFinishParameters::default(),
)
.map_err(|e| ClientAuthError::Opaque(format!("client reg finish: {e:?}")))?;
// Assuming PasswordFile is Vec<u8> (serialized server-side password file)
let password_file: PasswordFile = finish.message.serialize();
server
.finish(server_start.session_id(), password_file)
.finish(&server_start.session_id, password_file)
.await
.map_err(|e| ClientAuthError::Transport(format!("server reg finish: {e:?}")))?;
Ok(())
@@ -155,7 +120,7 @@ impl OpaqueAuthentication {
.start(&auth.identifier, &start.message)
.await
.map_err(|e| ClientAuthError::Transport(format!("server login start: {e:?}")))?;
let server_msg = server_start.response().clone();
let server_msg = server_start.response.clone();
let finish = start
.state
.finish(
@@ -166,7 +131,7 @@ impl OpaqueAuthentication {
)
.map_err(|e| ClientAuthError::Opaque(format!("client login finish: {e:?}")))?;
server
.finish(server_start.session_id(), &finish.message)
.finish(&server_start.session_id, &finish.message)
.await
.map_err(|e| ClientAuthError::Transport(format!("server login finish: {e:?}")))?;
Ok(finish.session_key.to_vec())

View File

@@ -0,0 +1,87 @@
use std::collections::HashMap;
use crate::models::PasswordFile;
use crate::server::{AuthRepo, 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 AuthRepo 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,101 @@
use std::collections::HashMap;
use crate::server::{RegCache, LoginCache, AuthSession};
use opaque_ke::{ServerLogin};
use crate::models::NKodeCipherSuite;
use uuid::Uuid;
#[derive(Default)]
pub struct InMemoryAuthSession {
reg_sessions: HashMap<Uuid, RegCache>,
login_sessions: HashMap<Uuid, LoginCache>,
}
impl InMemoryAuthSession {
pub fn new() -> Self {
Self::default()
}
}
impl AuthSession 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())
}
}

163
src/in_memory_transport.rs Normal file
View File

@@ -0,0 +1,163 @@
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::client::{ClientAuthError, ServerConnectionLogin, ServerConnectionRegister};
use crate::models::{LoginSession, RegisterSession, NKodeCipherSuite, NKodeServerSetup, PasswordFile};
use crate::server::{OpaqueAuth, CredKind, Key, Code};
use crate::in_memory_auth_repo::InMemoryAuthRepo;
use crate::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,
}
}
}
/// Convenience aliases
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 SharedServer<K> {
inner: Arc<Mutex<OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>>>,
_k: PhantomData<K>,
}
impl<K> SharedServer<K> {
pub fn new(inner: Arc<Mutex<OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>>>) -> Self {
Self { inner, _k: PhantomData }
}
}
#[async_trait::async_trait]
impl<K> ServerConnectionRegister for SharedServer<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 SharedServer<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

@@ -1,3 +1,6 @@
mod models;
mod client;
mod server;
pub mod models;
pub mod client;
pub mod server;
pub mod in_memory_auth_repo;
pub mod in_memory_auth_session;
pub mod in_memory_transport;

View File

@@ -1,11 +1,10 @@
use opaque_ke::{RegistrationResponse, Ristretto255, TripleDh, ServerSetup, CredentialResponse, RegistrationUploadLen};
use opaque_ke::{Ristretto255, TripleDh, ServerSetup, CredentialResponse, RegistrationUploadLen, RegistrationResponse};
use opaque_ke::keypair::{OprfSeed, PrivateKey};
use sha2::Sha512;
use opaque_ke::CipherSuite;
use opaque_ke::argon2::Argon2;
use opaque_ke::generic_array::GenericArray;
use uuid::Uuid;
use getset::Getters;
pub const NONCE_SIZE: usize = 12;
pub const SESSION_KEY_SIZE: usize = 32;
@@ -22,14 +21,6 @@ impl CipherSuite for NKodeCipherSuite {
pub type NKodeServerSetup = ServerSetup<NKodeCipherSuite, PrivateKey<Ristretto255>, OprfSeed<Sha512>>;
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
pub struct RegisterSession {
#[get = "pub"]
response: RegistrationResponse<NKodeCipherSuite>,
#[get = "pub"]
session_id: Uuid
}
pub type PasswordFile = GenericArray<u8, RegistrationUploadLen<NKodeCipherSuite>>;
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -38,7 +29,8 @@ pub struct LoginSession {
pub session_id: Uuid
}
pub type KeyRegisterSession = RegisterSession;
pub type KeyLoginSession = LoginSession;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RegisterSession {
pub response: RegistrationResponse<NKodeCipherSuite>,
pub session_id: Uuid
}

View File

@@ -1,30 +1,19 @@
//! Single-file example: remove Key-vs-Code duplication by introducing a CredKind trait
//! and implementing the OPAQUE flows once for Registration<K> and Login<K> states.
use std::marker::PhantomData;
use opaque_ke::{
rand::rngs::OsRng, CredentialFinalization, CredentialRequest,
RegistrationRequest, RegistrationResponse, ServerLogin, ServerLoginParameters,
RegistrationRequest, ServerLogin, ServerLoginParameters,
ServerRegistration,
};
use uuid::Uuid;
// --- Your crate types (as referenced in your snippet) ---
use crate::models::{LoginSession, NKodeCipherSuite, NKodeServerSetup, PasswordFile};
// ---------------- Errors ----------------
use crate::models::{RegisterSession, LoginSession, NKodeCipherSuite, NKodeServerSetup, PasswordFile};
#[derive(Debug)]
enum AuthRepoError {
pub enum AuthRepoError {
UserExists,
KeyNotRegistered,
CodeNotRegistered,
}
// ---------------- Repo abstraction ----------------
trait AuthRepo {
pub trait AuthRepo {
fn new_key(&mut self, identifier: &[u8], password_file: PasswordFile) -> Result<(), AuthRepoError>;
fn new_code(&mut self, identifier: &[u8], password_file: PasswordFile) -> Result<(), AuthRepoError>;
@@ -35,21 +24,18 @@ trait AuthRepo {
fn get_code_passcode_file(&self, identifier: &[u8]) -> Result<PasswordFile, AuthRepoError>;
}
// ---------------- Session abstraction ----------------
#[derive(Clone)]
struct RegCache {
session_id: Uuid,
identifier: Vec<u8>,
pub struct RegCache {
pub session_id: Uuid,
pub identifier: Vec<u8>,
}
struct LoginCache {
session_id: Uuid,
identifiers: Vec<u8>,
server_login: ServerLogin<NKodeCipherSuite>,
pub struct LoginCache {
pub session_id: Uuid,
pub identifiers: Vec<u8>,
pub server_login: ServerLogin<NKodeCipherSuite>,
}
trait AuthSession {
pub trait AuthSession {
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>;
@@ -63,40 +49,18 @@ trait AuthSession {
fn clear_login_session(&mut self, session_id: &Uuid) -> Result<(), String>;
}
// ---------------- Core OpaqueAuth struct ----------------
struct OpaqueAuth<State, R: AuthRepo, S: AuthSession> {
server_setup: NKodeServerSetup,
user_repo: R,
session: S,
_state: PhantomData<State>,
}
impl<State, R, S> OpaqueAuth<State, R, S>
where
R: AuthRepo,
S: AuthSession,
{
pub fn new(server_setup: NKodeServerSetup, user_repo: R, session: S) -> Self {
Self {
server_setup,
user_repo,
session,
_state: PhantomData,
}
}
}
// ---------------- “Kind” trait: Key vs Code differences live here ----------------
trait CredKind {
pub trait CredKind {
fn has<R: AuthRepo>(repo: &R, id: &[u8]) -> bool;
fn get_pf<R: AuthRepo>(repo: &R, id: &[u8]) -> Result<PasswordFile, AuthRepoError>;
fn put_pf<R: AuthRepo>(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError>;
fn prereq_for_register<R: AuthRepo>(_repo: &R, _id: &[u8]) -> Result<(), AuthRepoError> {
Ok(())
}
}
struct Key;
struct Code;
pub struct Key;
pub struct Code;
impl CredKind for Key {
fn has<R: AuthRepo>(repo: &R, id: &[u8]) -> bool {
@@ -120,106 +84,74 @@ impl CredKind for Code {
fn put_pf<R: AuthRepo>(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> {
repo.new_code(id, pf)
}
fn prereq_for_register<R: AuthRepo>(repo: &R, id: &[u8]) -> Result<(), AuthRepoError> {
if repo.has_key(id) {
Ok(())
} else {
Err(AuthRepoError::KeyNotRegistered)
}
}
}
// ---------------- State markers ----------------
struct Registration<K>(PhantomData<K>);
struct Login<K>(PhantomData<K>);
// Optional: aliases to make call sites read nicely
type KeyAuthRegistration<R, S> = OpaqueAuth<Registration<Key>, R, S>;
type CodeAuthRegistration<R, S> = OpaqueAuth<Registration<Code>, R, S>;
type KeyAuthLogin<R, S> = OpaqueAuth<Login<Key>, R, S>;
type CodeAuthLogin<R, S> = OpaqueAuth<Login<Code>, R, S>;
// ---------------- Return types ----------------
struct RegSession {
session_id: Uuid,
response: RegistrationResponse<NKodeCipherSuite>,
pub struct OpaqueAuth<R: AuthRepo, S: AuthSession> {
server_setup: NKodeServerSetup,
user_repo: R,
session: S,
}
// NOTE: you already have crate::protocol::LoginSession, so we use that.
// If you want it here, uncomment below and remove crate import.
// struct LoginSession {
// session_id: Uuid,
// response: CredentialResponse<NKodeCipherSuite>,
// }
impl<R: AuthRepo, S: AuthSession> OpaqueAuth<R, S> {
pub fn new(server_setup: NKodeServerSetup, user_repo: R, session: S) -> Self {
Self { server_setup, user_repo, session }
}
// ---------------- Shared registration flow ----------------
impl<K, R, S> OpaqueAuth<Registration<K>, R, S>
where
K: CredKind,
R: AuthRepo,
S: AuthSession,
{
pub async fn start(
pub async fn reg_start<K: CredKind>(
&mut self,
identifier: &[u8],
request: RegistrationRequest<NKodeCipherSuite>,
) -> Result<RegSession, String> {
) -> Result<RegisterSession, 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
).map_err(|e| format!("opaque reg start: {e:?}"))?;
let cache = self.session
.new_reg_session(identifier)
.map_err(|e| format!("reg cache: {e}"))?;
Ok(RegSession {
session_id: cache.session_id,
response: start.message,
})
Ok(RegisterSession { session_id: cache.session_id, response: start.message })
}
pub async fn finish(
pub async fn reg_finish<K: CredKind>(
&mut self,
session_id: &Uuid,
password_file: PasswordFile,
) -> Result<(), String> {
let sess = self
.session
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::put_pf(&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}"))
}
}
// ---------------- Shared login flow ----------------
impl<K, R, S> OpaqueAuth<Login<K>, R, S>
where
K: CredKind,
R: AuthRepo,
S: AuthSession,
{
pub async fn start(
pub async fn login_start<K: CredKind>(
&mut self,
identifier: &[u8],
request: CredentialRequest<NKodeCipherSuite>,
) -> Result<LoginSession, String> {
// Lookup password file for K (Key vs Code)
let password_file = K::get_pf(&self.user_repo, identifier)
.map_err(|e| format!("repo read: {e:?}"))?;
// Deserialize into OPAQUE password file type
let password_file =
ServerRegistration::<NKodeCipherSuite>::deserialize(password_file.as_slice())
.map_err(|e| format!("pf deserialize: {e:?}"))?;
// OPAQUE login start
let mut server_rng = OsRng;
let start = ServerLogin::start(
&mut server_rng,
@@ -228,36 +160,32 @@ where
request,
identifier,
ServerLoginParameters::default(),
)
.map_err(|e| format!("opaque login start: {e:?}"))?;
).map_err(|e| format!("opaque login start: {e:?}"))?;
// Cache server state
let cache = self
.session
let cache = self.session
.new_login_session(identifier, start.state)
.map_err(|e| format!("login cache: {e}"))?;
Ok(LoginSession {
session_id: cache.session_id,
response: start.message,
})
Ok(LoginSession { session_id: cache.session_id, response: start.message })
}
pub async fn finish(
pub async fn login_finish<K: CredKind>(
&mut self,
session_id: &Uuid,
finalize: CredentialFinalization<NKodeCipherSuite>,
) -> Result<Vec<u8>, String> {
let cache = self
.session
let cache = self.session
.get_login_session(session_id)
.map_err(|e| format!("get login session: {e}"))?;
let finish = cache
.server_login
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(finish.session_key.to_vec())
}
}