refactor client opaque

This commit is contained in:
2025-12-16 14:07:03 -06:00
parent e29468e0b6
commit be95241ed4
8 changed files with 342 additions and 150 deletions

View File

@@ -1,6 +1,6 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use nkode_rs::nkode_core::keypad::Keypad; use nkode_rs::nkode_core::keypad::Keypad;
use crate::models::app::{CodeLoginData, CodeLoginSession, Icon, IconID, KeyLoginSession, ServerAPI, ICON_ID_SIZE}; use crate::models::app::{CodeLoginData, CodeLoginSession, Icon, IconID, KeyLoginSession, ClientAuthAPI, ICON_ID_SIZE};
use crate::models::email::Email; use crate::models::email::Email;
use crate::models::opaque::UserSecretKey; use crate::models::opaque::UserSecretKey;
use anyhow::Result; use anyhow::Result;
@@ -12,14 +12,14 @@ pub struct Login;
pub struct Register; pub struct Register;
pub struct ClientAppKey<State, S: ServerAPI> { pub struct ClientAppKey<State, S: ClientAuthAPI> {
api: S, api: S,
email: Email, email: Email,
user_secret_key: UserSecretKey, user_secret_key: UserSecretKey,
_state: PhantomData<State> _state: PhantomData<State>
} }
impl <S: ServerAPI> ClientAppKey<Register,S> { impl <S: ClientAuthAPI> ClientAppKey<Register,S> {
pub async fn register(self) -> Result<ClientAppKey<Login, S>, String> { pub async fn register(self) -> Result<ClientAppKey<Login, S>, String> {
// self.repo.set_secret_key(&self.email, &self.user_secret_key).await?; // self.repo.set_secret_key(&self.email, &self.user_secret_key).await?;
self.api.register_key(&self.email, &self.user_secret_key).await?; self.api.register_key(&self.email, &self.user_secret_key).await?;
@@ -32,7 +32,7 @@ impl <S: ServerAPI> ClientAppKey<Register,S> {
} }
} }
impl <S: ServerAPI> ClientAppKey<Login,S> { impl <S: ClientAuthAPI> ClientAppKey<Login,S> {
pub async fn login(self) -> Result<ClientAppKeyLoggedIn<S>,String> { pub async fn login(self) -> Result<ClientAppKeyLoggedIn<S>,String> {
let key_login = self.api.login_key(&self.email, &self.user_secret_key).await?; let key_login = self.api.login_key(&self.email, &self.user_secret_key).await?;
Ok(ClientAppKeyLoggedIn{ Ok(ClientAppKeyLoggedIn{
@@ -44,14 +44,14 @@ impl <S: ServerAPI> ClientAppKey<Login,S> {
} }
} }
pub struct ClientAppKeyLoggedIn<S: ServerAPI> { pub struct ClientAppKeyLoggedIn<S: ClientAuthAPI> {
api: S, api: S,
email: Email, email: Email,
user_secret_key: UserSecretKey, user_secret_key: UserSecretKey,
key_login: KeyLoginSession, key_login: KeyLoginSession,
} }
impl <S: ServerAPI> ClientAppKeyLoggedIn<S> { impl <S: ClientAuthAPI> ClientAppKeyLoggedIn<S> {
pub async fn register_code(self) -> Result<ClientAppCodeRegister<S>, String> { pub async fn register_code(self) -> Result<ClientAppCodeRegister<S>, String> {
let icon_nonce = Nonce::new(); let icon_nonce = Nonce::new();
let icons = self.get_icons(&icon_nonce).await?; let icons = self.get_icons(&icon_nonce).await?;
@@ -98,7 +98,7 @@ impl <S: ServerAPI> ClientAppKeyLoggedIn<S> {
} }
} }
pub struct ClientAppCodeRegister<S: ServerAPI> { pub struct ClientAppCodeRegister<S: ClientAuthAPI> {
api: S, api: S,
email: Email, email: Email,
user_secret_key: UserSecretKey, user_secret_key: UserSecretKey,
@@ -108,7 +108,7 @@ pub struct ClientAppCodeRegister<S: ServerAPI> {
keypad: Keypad, keypad: Keypad,
} }
impl <S: ServerAPI>ClientAppCodeRegister<S> { impl <S: ClientAuthAPI>ClientAppCodeRegister<S> {
pub async fn register(self, selected_icons: Vec<IconID>) -> Result<ClientAppCodeLogin<S>, String> { pub async fn register(self, selected_icons: Vec<IconID>) -> Result<ClientAppCodeLogin<S>, String> {
let policy = self.api.get_policy().await?; let policy = self.api.get_policy().await?;
let keypad = Keypad::new(policy.clone()); let keypad = Keypad::new(policy.clone());
@@ -137,7 +137,7 @@ impl <S: ServerAPI>ClientAppCodeRegister<S> {
} }
} }
pub struct ClientAppCodeLogin<S: ServerAPI> { pub struct ClientAppCodeLogin<S: ClientAuthAPI> {
api: S, api: S,
email: Email, email: Email,
mask: Vec<u64>, mask: Vec<u64>,
@@ -146,7 +146,7 @@ pub struct ClientAppCodeLogin<S: ServerAPI> {
cipher: NKodeCipher cipher: NKodeCipher
} }
impl <S: ServerAPI>ClientAppCodeLogin<S> { impl <S: ClientAuthAPI>ClientAppCodeLogin<S> {
pub fn sort_icons(&self) -> Vec<Icon> { pub fn sort_icons(&self) -> Vec<Icon> {
nkode_rs::tensor::reorder( nkode_rs::tensor::reorder(
&self.icons, &self.icons,

View File

@@ -0,0 +1,197 @@
// // in_memory_server.rs
// use std::collections::HashMap;
// use std::hash::Hash;
// use std::sync::Arc;
// use nkode_rs::nkode_core::policy::NKodePolicy;
// use tokio::sync::RwLock;
//
// use crate::models::app::{
// CodeLoginData, CodeLoginSession, Icon, KeyLoginSession, ClientAuthAPI,
// };
// use crate::models::email::Email;
// use crate::models::opaque::UserSecretKey;
//
//
// #[derive(Clone)]
// pub struct InMemoryServer<FKeySess, FCodeSess> {
// state: Arc<RwLock<State>>,
// policy: NKodePolicy,
// icon_pool: Vec<Icon>,
// new_key_session: FKeySess,
// new_code_session: FCodeSess,
// }
//
// struct State {
// users: HashMap<Email, UserRecord>,
// key_sessions: HashMap<KeyLoginSession, Email>,
// code_sessions: HashMap<CodeLoginSession, Email>,
// }
//
// struct UserRecord {
// secret_key: UserSecretKey,
// code: Option<StoredCode>,
// }
//
// struct StoredCode {
// passcode: Vec<u64>,
// data: CodeLoginData,
// }
//
// impl Default for State {
// fn default() -> Self {
// Self {
// users: HashMap::new(),
// key_sessions: HashMap::new(),
// code_sessions: HashMap::new(),
// }
// }
// }
//
// impl<FKeySess, FCodeSess> InMemoryServer<FKeySess, FCodeSess> {
// /// `icon_pool` is what `get_new_icons()` returns (cloned) each time.
// /// `new_key_session` and `new_code_session` let you decide how sessions are created
// /// without changing the ServerAPI trait or guessing constructors.
// pub fn new(
// policy: NKodePolicy,
// icon_pool: Vec<Icon>,
// new_key_session: FKeySess,
// new_code_session: FCodeSess,
// ) -> Self {
// Self {
// state: Arc::new(RwLock::new(State::default())),
// policy,
// icon_pool,
// new_key_session,
// new_code_session,
// }
// }
// }
//
// impl<FKeySess, FCodeSess> InMemoryServer<FKeySess, FCodeSess>
// where
// // bounds needed for HashMap keys/values and cloning across calls
// Email: Eq + Hash + Clone,
// UserSecretKey: Clone + PartialEq,
// Icon: Clone,
// CodeLoginData: Clone,
// KeyLoginSession: Eq + Hash + Clone,
// CodeLoginSession: Eq + Hash + Clone,
// FKeySess: Fn() -> KeyLoginSession + Send + Sync + 'static,
// FCodeSess: Fn() -> CodeLoginSession + Send + Sync + 'static,
// {
// fn err(msg: impl Into<String>) -> Result<(), String> {
// Err(msg.into())
// }
//
// fn ok<T>(v: T) -> Result<T, String> {
// Ok(v)
// }
//
// async fn email_from_key_session(&self, sess: &KeyLoginSession) -> Result<Email, String> {
// let st = self.state.read().await;
// st.key_sessions
// .get(sess)
// .cloned()
// .ok_or_else(|| "invalid key login session".to_string())
// }
// }
//
// impl<FKeySess, FCodeSess> ClientAuthAPI for InMemoryServer<FKeySess, FCodeSess>
// where
// Email: Eq + Hash + Clone,
// UserSecretKey: Clone + PartialEq,
// Icon: Clone,
// CodeLoginData: Clone,
// KeyLoginSession: Eq + Hash + Clone,
// CodeLoginSession: Eq + Hash + Clone,
//
// FKeySess: Fn() -> KeyLoginSession + Send + Sync + 'static,
// FCodeSess: Fn() -> CodeLoginSession + Send + Sync + 'static,
// {
// async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String> {
// let mut st = self.state.write().await;
//
// if st.users.contains_key(email) {
// return Err("email already registered".to_string());
// }
//
// st.users.insert(
// email.clone(),
// UserRecord {
// secret_key: secret_key.clone(),
// code: None,
// },
// );
//
// Ok(())
// }
//
// async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoginSession, String> {
// let mut st = self.state.write().await;
//
// let user = st.users.get(email).ok_or_else(|| "unknown email".to_string())?;
// if &user.secret_key != secret_key {
// return Err("invalid secret key".to_string());
// }
// let sess = (self.new_key_session)();
// st.key_sessions.insert(sess.clone(), email.clone());
// Ok(sess)
// }
//
// async fn is_code_registered(&self, key_login_session: &KeyLoginSession) -> Result<bool, String> {
// let email = self.email_from_key_session(key_login_session).await?;
// let st = self.state.read().await;
// let user = st.users.get(&email).ok_or_else(|| "unknown email".to_string())?;
// Ok(user.code.is_some())
// }
//
// async fn get_policy(&self) -> Result<NKodePolicy, String> {
// Ok(self.policy.clone())
// }
//
// async fn get_new_icons(&self, key_login_session: &KeyLoginSession) -> Result<Vec<Icon>, String> {
// // Validate session (mimics auth gate)
// let _email = self.email_from_key_session(key_login_session).await?;
// Ok(self.icon_pool.clone())
// }
//
// async fn register_code(
// &self,
// email: &Email,
// passcode: &[u64],
// key_login_session: &KeyLoginSession,
// data: &CodeLoginData,
// ) -> Result<(), String> {
// let sess_email = self.email_from_key_session(key_login_session).await?;
// if &sess_email != email {
// return Err("session email mismatch".to_string());
// }
// let mut st = self.state.write().await;
// let user = st.users.get_mut(email).ok_or_else(|| "unknown email".to_string())?;
// user.code = Some(StoredCode {
// passcode: passcode.to_vec(),
// data: data.clone(),
// });
// Ok(())
// }
//
// async fn get_login_data(&self, key_login_session: &KeyLoginSession) -> Result<CodeLoginData, String> {
// let email = self.email_from_key_session(key_login_session).await?;
// let st = self.state.read().await;
// let user = st.users.get(&email).ok_or_else(|| "unknown email".to_string())?;
// let code = user.code.as_ref().ok_or_else(|| "code not registered".to_string())?;
// Ok(code.data.clone())
// }
//
// async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result<CodeLoginSession, String> {
// let mut st = self.state.write().await;
// let user = st.users.get(email).ok_or_else(|| "unknown email".to_string())?;
// let code = user.code.as_ref().ok_or_else(|| "code not registered".to_string())?;
// if code.passcode.as_slice() != passcode {
// return Err("invalid passcode".to_string());
// }
// let sess = (self.new_code_session)();
// st.code_sessions.insert(sess.clone(), email.clone());
// Ok(sess)
// }
// }

View File

@@ -7,7 +7,6 @@ use nkode_rs::nkode_core::policy::NKodePolicy;
use crate::models::email::Email; use crate::models::email::Email;
use crate::models::opaque::{OpaqueSessionKey, UserSecretKey}; use crate::models::opaque::{OpaqueSessionKey, UserSecretKey};
#[allow(dead_code)]
struct LoginSession { struct LoginSession {
email: Email, email: Email,
session_id: Uuid, session_id: Uuid,
@@ -55,7 +54,7 @@ pub struct CodeLoginData {
pub(crate) keypad: Keypad, pub(crate) keypad: Keypad,
} }
pub trait ServerAPI { pub trait ClientAuthAPI {
async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String>; 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 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_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoginSession, String>;
@@ -64,4 +63,5 @@ pub trait ServerAPI {
async fn get_login_data(&self, key_login_session: &KeyLoginSession) -> Result<CodeLoginData, 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 is_code_registered(&self, key_login_session: &KeyLoginSession) -> Result<bool, String>;
async fn get_policy(&self) -> Result<NKodePolicy, String>; async fn get_policy(&self) -> Result<NKodePolicy, String>;
} }

View File

@@ -11,6 +11,8 @@ use nkode_rs::from_bytes::FromBytes;
const USER_KEY_SIZE: usize = 16; const USER_KEY_SIZE: usize = 16;
#[derive(Clone)]
#[derive(PartialEq)]
pub struct UserSecretKey(Zeroizing<[u8; USER_KEY_SIZE]>); pub struct UserSecretKey(Zeroizing<[u8; USER_KEY_SIZE]>);
impl UserSecretKey { impl UserSecretKey {
@@ -26,7 +28,7 @@ impl FromBytes<USER_KEY_SIZE> for UserSecretKey {
} }
} }
const OPAQUE_SESSION_KEY_SIZE: usize = 32; const OPAQUE_SESSION_KEY_SIZE: usize = 64;
pub struct OpaqueSessionKey(Zeroizing<[u8; OPAQUE_SESSION_KEY_SIZE]>); pub struct OpaqueSessionKey(Zeroizing<[u8; OPAQUE_SESSION_KEY_SIZE]>);

View File

@@ -48,13 +48,13 @@ impl AuthenticationData {
#[async_trait] #[async_trait]
pub trait ServerConnectionRegister { pub trait ServerConnectionRegister {
async fn start( async fn start(
&mut self, &self,
identifier: &[u8], identifier: &[u8],
message: &RegistrationRequest<NKodeCipherSuite>, message: &RegistrationRequest<NKodeCipherSuite>,
) -> Result<RegisterSession, ClientAuthError>; ) -> Result<RegisterSession, ClientAuthError>;
async fn finish( async fn finish(
&mut self, &self,
session_id: &Uuid, session_id: &Uuid,
password_file: PasswordFile, password_file: PasswordFile,
) -> Result<(), ClientAuthError>; ) -> Result<(), ClientAuthError>;
@@ -63,31 +63,48 @@ pub trait ServerConnectionRegister {
#[async_trait] #[async_trait]
pub trait ServerConnectionLogin { pub trait ServerConnectionLogin {
async fn start( async fn start(
&mut self, &self,
identifier: &[u8], identifier: &[u8],
request: &CredentialRequest<NKodeCipherSuite>, request: &CredentialRequest<NKodeCipherSuite>,
) -> Result<LoginSession, ClientAuthError>; ) -> Result<LoginSession, ClientAuthError>;
async fn finish( async fn finish(
&mut self, &self,
session_id: &Uuid, session_id: &Uuid,
message: &CredentialFinalization<NKodeCipherSuite>, message: &CredentialFinalization<NKodeCipherSuite>,
) -> Result<OpaqueSessionKey, ClientAuthError>; ) -> Result<OpaqueSessionKey, ClientAuthError>;
} }
// --- OPAQUE client driver ---
pub struct OpaqueAuthentication;
impl OpaqueAuthentication { pub struct OpaqueAuthRegister<R: ServerConnectionRegister + Clone>(R);
impl <R: ServerConnectionRegister + Clone> OpaqueAuthRegister<R> {
pub fn new(server: R) -> Self {
Self(server)
}
}
pub struct OpaqueAuthLogin<L: ServerConnectionLogin + Clone>(L);
impl <L: ServerConnectionLogin + Clone> OpaqueAuthLogin<L> {
pub fn new(server: L) -> Self {
Self(server)
}
}
impl <R: ServerConnectionRegister + Clone>OpaqueAuthRegister<R> {
pub async fn register( pub async fn register(
&self,
auth: &AuthenticationData, auth: &AuthenticationData,
server: &mut impl ServerConnectionRegister, // server: &mut impl ServerConnectionRegister,
) -> Result<(), ClientAuthError> { ) -> Result<(), ClientAuthError> {
let mut rng = OsRng; let mut rng = OsRng;
let start = ClientRegistration::<NKodeCipherSuite>::start(&mut rng, &auth.secret) let start = ClientRegistration::<NKodeCipherSuite>::start(&mut rng, &auth.secret)
.map_err(|e| ClientAuthError::Opaque(format!("client reg start: {e:?}")))?; .map_err(|e| ClientAuthError::Opaque(format!("client reg start: {e:?}")))?;
let server_start = server let server_start = self.0
.start(&auth.identifier, &start.message) .start(&auth.identifier, &start.message)
.await .await
.map_err(|e| ClientAuthError::Transport(format!("server reg start: {e:?}")))?; .map_err(|e| ClientAuthError::Transport(format!("server reg start: {e:?}")))?;
@@ -102,21 +119,23 @@ impl OpaqueAuthentication {
) )
.map_err(|e| ClientAuthError::Opaque(format!("client reg finish: {e:?}")))?; .map_err(|e| ClientAuthError::Opaque(format!("client reg finish: {e:?}")))?;
let password_file: PasswordFile = finish.message.serialize(); let password_file: PasswordFile = finish.message.serialize();
server self.0
.finish(&server_start.session_id, password_file) .finish(&server_start.session_id, password_file)
.await .await
.map_err(|e| ClientAuthError::Transport(format!("server reg finish: {e:?}")))?; .map_err(|e| ClientAuthError::Transport(format!("server reg finish: {e:?}")))?;
Ok(()) Ok(())
} }
}
impl <L: ServerConnectionLogin + Clone>OpaqueAuthLogin<L> {
pub async fn login( pub async fn login(
&self,
auth: &AuthenticationData, auth: &AuthenticationData,
server: &mut impl ServerConnectionLogin,
) -> Result<Vec<u8>, ClientAuthError> { ) -> Result<Vec<u8>, ClientAuthError> {
let mut rng = OsRng; let mut rng = OsRng;
let start = ClientLogin::<NKodeCipherSuite>::start(&mut rng, &auth.secret) let start = ClientLogin::<NKodeCipherSuite>::start(&mut rng, &auth.secret)
.map_err(|e| ClientAuthError::Opaque(format!("client login start: {e:?}")))?; .map_err(|e| ClientAuthError::Opaque(format!("client login start: {e:?}")))?;
let server_start = server let server_start = self.0
.start(&auth.identifier, &start.message) .start(&auth.identifier, &start.message)
.await .await
.map_err(|e| ClientAuthError::Transport(format!("server login start: {e:?}")))?; .map_err(|e| ClientAuthError::Transport(format!("server login start: {e:?}")))?;
@@ -130,7 +149,7 @@ impl OpaqueAuthentication {
ClientLoginFinishParameters::default(), ClientLoginFinishParameters::default(),
) )
.map_err(|e| ClientAuthError::Opaque(format!("client login finish: {e:?}")))?; .map_err(|e| ClientAuthError::Opaque(format!("client login finish: {e:?}")))?;
server self.0
.finish(&server_start.session_id, &finish.message) .finish(&server_start.session_id, &finish.message)
.await .await
.map_err(|e| ClientAuthError::Transport(format!("server login finish: {e:?}")))?; .map_err(|e| ClientAuthError::Transport(format!("server login finish: {e:?}")))?;

View File

@@ -28,7 +28,9 @@ pub trait CredKind {
} }
} }
#[derive(Clone)]
pub struct Key; pub struct Key;
#[derive(Clone)]
pub struct Code; pub struct Code;
impl CredKind for Key { impl CredKind for Key {

View File

@@ -10,15 +10,16 @@ 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_repo::InMemoryAuthRepo;
use crate::repository::opaque::in_memory::in_memory_auth_session::InMemoryAuthSession; use crate::repository::opaque::in_memory::in_memory_auth_session::InMemoryAuthSession;
#[derive(Clone)]
pub struct InMemoryServer<K: CredKind> { pub struct InMemoryServer<K: CredKind> {
auth: OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>, auth: Arc<Mutex<OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>>>,
_kind: PhantomData<K>, _kind: PhantomData<K>,
} }
impl<K: CredKind> InMemoryServer<K> { impl<K: CredKind> InMemoryServer<K> {
pub fn new(server_setup: NKodeServerSetup) -> Self { pub fn new(server_setup: NKodeServerSetup) -> Self {
Self { Self {
auth: OpaqueAuth::new(server_setup, InMemoryAuthRepo::new(), InMemoryAuthSession::new()), auth: Arc::new(Mutex::new(OpaqueAuth::new(server_setup, InMemoryAuthRepo::new(), InMemoryAuthSession::new()))),
_kind: PhantomData, _kind: PhantomData,
} }
} }
@@ -33,24 +34,24 @@ where
K: CredKind + Send + Sync, K: CredKind + Send + Sync,
{ {
async fn start( async fn start(
&mut self, &self,
identifier: &[u8], identifier: &[u8],
message: &RegistrationRequest<NKodeCipherSuite>, message: &RegistrationRequest<NKodeCipherSuite>,
) -> Result<RegisterSession, ClientAuthError> { ) -> Result<RegisterSession, ClientAuthError> {
// Server API takes ownership; client trait gives us a reference. // 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. // opaque-ke request types are typically Clone; if not, you'll need to adjust signatures.
self.auth self.auth.lock().await
.reg_start::<K>(identifier, message.clone()) .reg_start::<K>(identifier, message.clone())
.await .await
.map_err(|e| ClientAuthError::Transport(e)) .map_err(|e| ClientAuthError::Transport(e))
} }
async fn finish( async fn finish(
&mut self, &self,
session_id: &Uuid, session_id: &Uuid,
password_file: PasswordFile, password_file: PasswordFile,
) -> Result<(), ClientAuthError> { ) -> Result<(), ClientAuthError> {
self.auth self.auth.lock().await
.reg_finish::<K>(session_id, password_file) .reg_finish::<K>(session_id, password_file)
.await .await
.map_err(|e| ClientAuthError::Transport(e)) .map_err(|e| ClientAuthError::Transport(e))
@@ -63,24 +64,24 @@ where
K: CredKind + Send + Sync, K: CredKind + Send + Sync,
{ {
async fn start( async fn start(
&mut self, &self,
identifier: &[u8], identifier: &[u8],
request: &CredentialRequest<NKodeCipherSuite>, request: &CredentialRequest<NKodeCipherSuite>,
) -> Result<LoginSession, ClientAuthError> { ) -> Result<LoginSession, ClientAuthError> {
self.auth self.auth.lock().await
.login_start::<K>(identifier, request.clone()) .login_start::<K>(identifier, request.clone())
.await .await
.map_err(|e| ClientAuthError::Transport(e)) .map_err(|e| ClientAuthError::Transport(e))
} }
async fn finish( async fn finish(
&mut self, &self,
session_id: &Uuid, session_id: &Uuid,
message: &CredentialFinalization<NKodeCipherSuite>, message: &CredentialFinalization<NKodeCipherSuite>,
) -> Result<OpaqueSessionKey, ClientAuthError> { ) -> Result<OpaqueSessionKey, ClientAuthError> {
// Server computes its own session key too; we just need it to validate and complete. // Server computes its own session key too; we just need it to validate and complete.
let key = self let key = self
.auth .auth.lock().await
.login_finish::<K>(session_id, message.clone()) .login_finish::<K>(session_id, message.clone())
.await .await
.map_err(|e| ClientAuthError::Transport(e))?; .map_err(|e| ClientAuthError::Transport(e))?;
@@ -89,74 +90,74 @@ where
} }
} }
pub struct InMemSharedServer<K> { // pub struct InMemSharedServer<K> {
inner: Arc<Mutex<OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>>>, // inner: Arc<Mutex<OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>>>,
_k: PhantomData<K>, // _k: PhantomData<K>,
} // }
//
impl<K> InMemSharedServer<K> { // impl<K> InMemSharedServer<K> {
pub fn new(inner: Arc<Mutex<OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>>>) -> Self { // pub fn new(inner: Arc<Mutex<OpaqueAuth<InMemoryAuthRepo, InMemoryAuthSession>>>) -> Self {
Self { inner, _k: PhantomData } // Self { inner, _k: PhantomData }
} // }
} // }
//
#[async_trait::async_trait] // #[async_trait::async_trait]
impl<K> ServerConnectionRegister for InMemSharedServer<K> // impl<K> ServerConnectionRegister for InMemSharedServer<K>
where // where
K: CredKind + Send + Sync, // K: CredKind + Send + Sync,
{ // {
async fn start( // async fn start(
&mut self, // &mut self,
identifier: &[u8], // identifier: &[u8],
message: &RegistrationRequest<NKodeCipherSuite>, // message: &RegistrationRequest<NKodeCipherSuite>,
) -> Result<RegisterSession, ClientAuthError> { // ) -> Result<RegisterSession, ClientAuthError> {
let mut guard = self.inner.lock().await; // let mut guard = self.inner.lock().await;
guard // guard
.reg_start::<K>(identifier, message.clone()) // .reg_start::<K>(identifier, message.clone())
.await // .await
.map_err(ClientAuthError::Transport) // .map_err(ClientAuthError::Transport)
} // }
//
async fn finish( // async fn finish(
&mut self, // &mut self,
session_id: &Uuid, // session_id: &Uuid,
password_file: PasswordFile, // password_file: PasswordFile,
) -> Result<(), ClientAuthError> { // ) -> Result<(), ClientAuthError> {
let mut guard = self.inner.lock().await; // let mut guard = self.inner.lock().await;
guard // guard
.reg_finish::<K>(session_id, password_file) // .reg_finish::<K>(session_id, password_file)
.await // .await
.map_err(ClientAuthError::Transport) // .map_err(ClientAuthError::Transport)
} // }
} // }
//
#[async_trait::async_trait] // #[async_trait::async_trait]
impl<K> ServerConnectionLogin for InMemSharedServer<K> // impl<K> ServerConnectionLogin for InMemSharedServer<K>
where // where
K: CredKind + Send + Sync, // K: CredKind + Send + Sync,
{ // {
async fn start( // async fn start(
&mut self, // &mut self,
identifier: &[u8], // identifier: &[u8],
request: &CredentialRequest<NKodeCipherSuite>, // request: &CredentialRequest<NKodeCipherSuite>,
) -> Result<LoginSession, ClientAuthError> { // ) -> Result<LoginSession, ClientAuthError> {
let mut guard = self.inner.lock().await; // let mut guard = self.inner.lock().await;
guard // guard
.login_start::<K>(identifier, request.clone()) // .login_start::<K>(identifier, request.clone())
.await // .await
.map_err(ClientAuthError::Transport) // .map_err(ClientAuthError::Transport)
} // }
//
async fn finish( // async fn finish(
&mut self, // &mut self,
session_id: &Uuid, // session_id: &Uuid,
message: &CredentialFinalization<NKodeCipherSuite>, // message: &CredentialFinalization<NKodeCipherSuite>,
) -> Result<OpaqueSessionKey, ClientAuthError> { // ) -> Result<OpaqueSessionKey, ClientAuthError> {
let mut guard = self.inner.lock().await; // let mut guard = self.inner.lock().await;
let key = guard // let key = guard
.login_finish::<K>(session_id, message.clone()) // .login_finish::<K>(session_id, message.clone())
.await // .await
.map_err(ClientAuthError::Transport)?; // .map_err(ClientAuthError::Transport)?;
Ok(key) // Ok(key)
} // }
} // }

View File

@@ -1,61 +1,31 @@
use std::sync::Arc;
use opaque_ke::rand::rngs::OsRng; use opaque_ke::rand::rngs::OsRng;
use tokio::sync::Mutex; use nkode_protocol::opaque::client::{AuthenticationData, ClientAuthError, OpaqueAuthLogin, OpaqueAuthRegister};
use nkode_protocol::opaque::client::{AuthenticationData, OpaqueAuthentication, ClientAuthError};
use nkode_protocol::models::opaque::NKodeServerSetup; use nkode_protocol::models::opaque::NKodeServerSetup;
use nkode_protocol::opaque::server::{Code, Key, OpaqueAuth}; use nkode_protocol::repository::opaque::in_memory::in_memory_transport::{InMemoryCodeServer, InMemoryKeyServer};
use nkode_protocol::repository::opaque::in_memory::in_memory_auth_repo::InMemoryAuthRepo;
use nkode_protocol::repository::opaque::in_memory::in_memory_auth_session::InMemoryAuthSession;
use nkode_protocol::repository::opaque::in_memory::in_memory_transport::{InMemoryCodeServer, InMemoryKeyServer, InMemSharedServer};
#[tokio::test] #[tokio::test]
async fn opaque_key_registration_and_login_roundtrip() { async fn opaque_key_registration_and_login_roundtrip() {
let mut rng = OsRng; let mut rng = OsRng;
let server_setup = NKodeServerSetup::new(&mut rng); let server_setup = NKodeServerSetup::new(&mut rng);
let mut server = InMemoryKeyServer::new(server_setup); let server = InMemoryKeyServer::new(server_setup);
let auth = AuthenticationData::from_secret_key("a@b.com", b"supersecret16bytes"); let auth_reg = OpaqueAuthRegister::new(server.clone());
OpaqueAuthentication::register(&auth, &mut server) let auth_data = AuthenticationData::from_secret_key("a@b.com", b"supersecret16bytes");
.await auth_reg.register(&auth_data).await.expect("registration should succeed");
.expect("registration should succeed"); let login_reg = OpaqueAuthLogin::new(server);
let session_key = OpaqueAuthentication::login(&auth, &mut server) let session_key =login_reg.login(&auth_data)
.await .await
.expect("login should succeed"); .expect("login should succeed");
assert!(!session_key.is_empty()); assert!(!session_key.is_empty());
} }
#[tokio::test]
async fn opaque_code_registration_and_login_roundtrip() {
let mut rng = OsRng;
let server_setup = NKodeServerSetup::new(&mut rng);
let shared = Arc::new(Mutex::new(OpaqueAuth::new(
server_setup,
InMemoryAuthRepo::new(),
InMemoryAuthSession::new(),
)));
let mut key_server = InMemSharedServer::<Key>::new(shared.clone());
let mut code_server = InMemSharedServer::<Code>::new(shared.clone());
let email = "c@d.com";
let key_auth = AuthenticationData::from_secret_key(email, b"supersecret16bytes");
OpaqueAuthentication::register(&key_auth, &mut key_server)
.await
.expect("key registration should succeed");
let code = vec![1usize, 2, 3, 4, 5, 6];
let code_auth = AuthenticationData::from_code(email, &code);
OpaqueAuthentication::register(&code_auth, &mut code_server)
.await
.expect("code registration should succeed after key exists");
let session_key = OpaqueAuthentication::login(&code_auth, &mut code_server)
.await
.expect("login should succeed");
assert!(!session_key.is_empty());
}
#[tokio::test] #[tokio::test]
async fn opaque_login_fails_if_not_registered() { async fn opaque_login_fails_if_not_registered() {
let mut rng = OsRng; let mut rng = OsRng;
let server_setup = NKodeServerSetup::new(&mut rng); let server_setup = NKodeServerSetup::new(&mut rng);
let mut server = InMemoryKeyServer::new(server_setup); let server = InMemoryKeyServer::new(server_setup);
let auth = AuthenticationData::from_secret_key("nope@nope.com", b"supersecret16bytes"); let auth = AuthenticationData::from_secret_key("nope@nope.com", b"supersecret16bytes");
let err = OpaqueAuthentication::login(&auth, &mut server) let login_reg = OpaqueAuthLogin::new(server);
let err = login_reg.login(&auth)
.await .await
.expect_err("login should fail if user not registered"); .expect_err("login should fail if user not registered");
match err { match err {
@@ -69,8 +39,9 @@ async fn cannot_register_code_before_key() {
let mut rng = OsRng; let mut rng = OsRng;
let server_setup = NKodeServerSetup::new(&mut rng); let server_setup = NKodeServerSetup::new(&mut rng);
let mut server = InMemoryCodeServer::new(server_setup); let mut server = InMemoryCodeServer::new(server_setup);
let auth_reg = OpaqueAuthRegister::new(server.clone());
let auth = AuthenticationData::from_code("x@y.com", &[1usize,2,3,4]); let auth = AuthenticationData::from_code("x@y.com", &[1usize,2,3,4]);
let err = OpaqueAuthentication::register(&auth, &mut server) let err = auth_reg.register(&auth)
.await .await
.expect_err("should fail because key is not registered"); .expect_err("should fail because key is not registered");
match err { match err {