refactor client opaque
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|||||||
@@ -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>;
|
||||||
@@ -65,3 +64,4 @@ pub trait ServerAPI {
|
|||||||
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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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]>);
|
||||||
|
|
||||||
|
|||||||
@@ -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:?}")))?;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user