implement server.rs
This commit is contained in:
@@ -53,12 +53,10 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct LoginSession {
|
pub struct LoginSession {
|
||||||
#[get = "pub"]
|
pub response: CredentialResponse<NKodeCipherSuite>,
|
||||||
response: CredentialResponse<NKodeCipherSuite>,
|
pub session_id: Uuid
|
||||||
#[get = "pub"]
|
|
||||||
session_id: Uuid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
301
src/server.rs
301
src/server.rs
@@ -1,117 +1,274 @@
|
|||||||
|
//! 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 std::marker::PhantomData;
|
||||||
use opaque_ke::{CredentialFinalization, CredentialRequest, Identifiers, RegistrationRequest};
|
|
||||||
use crate::protocol::{KeyLoginPhaseISession, NKodeCipherSuite, PasswordFile};
|
use opaque_ke::{
|
||||||
|
rand::rngs::OsRng, CredentialFinalization, CredentialRequest,
|
||||||
|
RegistrationRequest, RegistrationResponse, ServerLogin, ServerLoginParameters,
|
||||||
|
ServerRegistration,
|
||||||
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
// --- Your crate types (as referenced in your snippet) ---
|
||||||
|
use crate::protocol::{LoginSession, NKodeCipherSuite, NKodeServerSetup, PasswordFile};
|
||||||
|
|
||||||
|
// ---------------- Errors ----------------
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum AuthRepoError {
|
enum AuthRepoError {
|
||||||
UserExists,
|
UserExists,
|
||||||
KeyNotRegistered,
|
KeyNotRegistered,
|
||||||
CodeNotRegistered
|
CodeNotRegistered,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------- Repo abstraction ----------------
|
||||||
|
|
||||||
trait AuthRepo {
|
trait AuthRepo {
|
||||||
fn new_key(email: String, password_file: PasswordFile) -> Result<(), AuthRepoError>;
|
fn new_key(&mut self, identifier: &[u8], password_file: PasswordFile) -> Result<(), AuthRepoError>;
|
||||||
fn new_code(email: String, password_file: PasswordFile) -> Result<(), AuthRepoError>;
|
fn new_code(&mut self, identifier: &[u8], password_file: PasswordFile) -> Result<(), AuthRepoError>;
|
||||||
fn has_code(email: String) -> bool;
|
|
||||||
fn has_key(email: String) -> bool;
|
fn has_code(&self, identifier: &[u8]) -> bool;
|
||||||
fn get_key_passcode_file(email: String) -> Result<PasswordFile, AuthRepoError>;
|
fn has_key(&self, identifier: &[u8]) -> bool;
|
||||||
fn get_code_passcode_file(email: String) -> Result<PasswordFile, AuthRepoError>;
|
|
||||||
|
fn get_key_passcode_file(&self, identifier: &[u8]) -> Result<PasswordFile, AuthRepoError>;
|
||||||
|
fn get_code_passcode_file(&self, identifier: &[u8]) -> Result<PasswordFile, AuthRepoError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RegSession;
|
// ---------------- Session abstraction ----------------
|
||||||
struct LoginSession;
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct RegCache {
|
||||||
|
session_id: Uuid,
|
||||||
|
identifier: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LoginCache {
|
||||||
|
session_id: Uuid,
|
||||||
|
identifiers: Vec<u8>,
|
||||||
|
server_login: ServerLogin<NKodeCipherSuite>,
|
||||||
|
}
|
||||||
|
|
||||||
trait AuthSession {
|
trait AuthSession {
|
||||||
fn new_reg_session(identifier: &[u8], request: &RegistrationRequest<NKodeCipherSuite>) -> Result<RegSession, String>;
|
fn new_reg_session(&mut self, identifier: &[u8]) -> Result<RegCache, String>;
|
||||||
fn get_reg_session(session_id: Uuid) -> Result<RegSession, String>;
|
fn get_reg_session(&self, session_id: &Uuid) -> Result<RegCache, String>;
|
||||||
fn new_login_session(identifier: &[u8], request: &CredentialRequest<NKodeCipherSuite>) -> Result<LoginSession, String>;
|
fn clear_reg_session(&mut self, session_id: &Uuid) -> Result<(), String>;
|
||||||
fn get_login_session(session_id: Uuid) -> Result<LoginSession, 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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OpaqueAuth<State,R: AuthRepo, S: AuthSession> {
|
// ---------------- Core OpaqueAuth struct ----------------
|
||||||
|
|
||||||
|
struct OpaqueAuth<State, R: AuthRepo, S: AuthSession> {
|
||||||
|
server_setup: NKodeServerSetup,
|
||||||
user_repo: R,
|
user_repo: R,
|
||||||
session: S,
|
session: S,
|
||||||
_state: PhantomData<State>
|
_state: PhantomData<State>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KeyAuthRegistration;
|
impl<State, R, S> OpaqueAuth<State, R, S>
|
||||||
impl<R: AuthRepo,S: AuthSession> OpaqueAuth<KeyAuthRegistration, R, S>
|
where
|
||||||
|
R: AuthRepo,
|
||||||
|
S: AuthSession,
|
||||||
{
|
{
|
||||||
async fn start(
|
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 {
|
||||||
|
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>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Key;
|
||||||
|
struct Code;
|
||||||
|
|
||||||
|
impl CredKind for Key {
|
||||||
|
fn has<R: AuthRepo>(repo: &R, id: &[u8]) -> bool {
|
||||||
|
repo.has_key(id)
|
||||||
|
}
|
||||||
|
fn get_pf<R: AuthRepo>(repo: &R, id: &[u8]) -> Result<PasswordFile, AuthRepoError> {
|
||||||
|
repo.get_key_passcode_file(id)
|
||||||
|
}
|
||||||
|
fn put_pf<R: AuthRepo>(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> {
|
||||||
|
repo.new_key(id, pf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CredKind for Code {
|
||||||
|
fn has<R: AuthRepo>(repo: &R, id: &[u8]) -> bool {
|
||||||
|
repo.has_code(id)
|
||||||
|
}
|
||||||
|
fn get_pf<R: AuthRepo>(repo: &R, id: &[u8]) -> Result<PasswordFile, AuthRepoError> {
|
||||||
|
repo.get_code_passcode_file(id)
|
||||||
|
}
|
||||||
|
fn put_pf<R: AuthRepo>(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> {
|
||||||
|
repo.new_code(id, pf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- 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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ---------------- Shared registration flow ----------------
|
||||||
|
|
||||||
|
impl<K, R, S> OpaqueAuth<Registration<K>, R, S>
|
||||||
|
where
|
||||||
|
K: CredKind,
|
||||||
|
R: AuthRepo,
|
||||||
|
S: AuthSession,
|
||||||
|
{
|
||||||
|
pub async fn start(
|
||||||
&mut self,
|
&mut self,
|
||||||
identifier: &[u8],
|
identifier: &[u8],
|
||||||
message: &RegistrationRequest<NKodeCipherSuite>
|
request: RegistrationRequest<NKodeCipherSuite>,
|
||||||
) -> Result<RegSession, String> {
|
) -> Result<RegSession, String> {
|
||||||
todo!()
|
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(RegSession {
|
||||||
|
session_id: cache.session_id,
|
||||||
|
response: start.message,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn finish(
|
pub async fn finish(
|
||||||
&mut self,
|
&mut self,
|
||||||
session_id: &Uuid,
|
session_id: &Uuid,
|
||||||
password_file: PasswordFile,
|
password_file: PasswordFile,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
todo!()
|
let sess = self
|
||||||
|
.session
|
||||||
|
.get_reg_session(session_id)
|
||||||
|
.map_err(|e| format!("get reg session: {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}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KeyAuthLogin;
|
// ---------------- Shared login flow ----------------
|
||||||
impl<R: AuthRepo,S: AuthSession> OpaqueAuth<KeyAuthLogin, R, S>
|
|
||||||
|
impl<K, R, S> OpaqueAuth<Login<K>, R, S>
|
||||||
|
where
|
||||||
|
K: CredKind,
|
||||||
|
R: AuthRepo,
|
||||||
|
S: AuthSession,
|
||||||
{
|
{
|
||||||
async fn start(
|
pub async fn start(
|
||||||
&mut self,
|
&mut self,
|
||||||
identifier: &[u8],
|
identifier: &[u8],
|
||||||
request_bytes: &CredentialRequest<NKodeCipherSuite>
|
request: CredentialRequest<NKodeCipherSuite>,
|
||||||
) -> Result<LoginSession, String> {
|
) -> Result<LoginSession, String> {
|
||||||
todo!()
|
// 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,
|
||||||
|
&self.server_setup,
|
||||||
|
Some(password_file),
|
||||||
|
request,
|
||||||
|
identifier,
|
||||||
|
ServerLoginParameters::default(),
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("opaque login start: {e:?}"))?;
|
||||||
|
|
||||||
|
// Cache server state
|
||||||
|
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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn finish(
|
pub async fn finish(
|
||||||
&mut self,
|
&mut self,
|
||||||
session_id: &Uuid,
|
session_id: &Uuid,
|
||||||
message: &CredentialFinalization<NKodeCipherSuite>
|
finalize: CredentialFinalization<NKodeCipherSuite>,
|
||||||
) -> Result<(), String> {
|
) -> Result<Vec<u8>, String> {
|
||||||
todo!()
|
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:?}"))?;
|
||||||
|
|
||||||
|
Ok(finish.session_key.to_vec())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CodeAuthRegistration;
|
// ---------------- Usage notes ----------------
|
||||||
impl<R: AuthRepo,S: AuthSession> OpaqueAuth<CodeAuthRegistration, R, S>
|
//
|
||||||
{
|
// You now have these concrete “types” for your call sites:
|
||||||
async fn start(
|
//
|
||||||
&mut self,
|
// KeyAuthRegistration<R, S> == OpaqueAuth<Registration<Key>, R, S>
|
||||||
identifier: &[u8],
|
// CodeAuthRegistration<R, S> == OpaqueAuth<Registration<Code>, R, S>
|
||||||
message: &RegistrationRequest<NKodeCipherSuite>
|
// KeyAuthLogin<R, S> == OpaqueAuth<Login<Key>, R, S>
|
||||||
) -> Result<RegSession, String> {
|
// CodeAuthLogin<R, S> == OpaqueAuth<Login<Code>, R, S>
|
||||||
todo!()
|
//
|
||||||
}
|
// And you only wrote the reg/login OPAQUE logic once.
|
||||||
|
|
||||||
async fn finish(
|
|
||||||
&mut self,
|
|
||||||
session_id: &Uuid,
|
|
||||||
password_file: PasswordFile,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CodeAuthLogin;
|
|
||||||
impl<R: AuthRepo,S: AuthSession> OpaqueAuth<CodeAuthLogin, R, S>
|
|
||||||
{
|
|
||||||
async fn start(
|
|
||||||
&mut self,
|
|
||||||
identifier: &[u8],
|
|
||||||
request_bytes: &CredentialRequest<NKodeCipherSuite>
|
|
||||||
) -> Result<LoginSession, String> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn finish(
|
|
||||||
&mut self,
|
|
||||||
session_id: &Uuid,
|
|
||||||
message: &CredentialFinalization<NKodeCipherSuite>
|
|
||||||
) -> Result<(), String> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user