stopping for the night
This commit is contained in:
@@ -1,27 +1,29 @@
|
||||
use std::marker::PhantomData;
|
||||
use nkode_rs::nkode_core::keypad::Keypad;
|
||||
use crate::models::app::{CodeLoginData, CodeLoginSession, Icon, IconID, KeyLoginSession, ClientAuthAPI, ICON_ID_SIZE};
|
||||
use crate::models::app::{OpaqueAPI, AuthAPI, CodeLoginData, CodeLoginSession, Icon, IconID, KeyLoginSession, ICON_ID_SIZE, LoginSession};
|
||||
use crate::models::email::Email;
|
||||
use crate::models::opaque::UserSecretKey;
|
||||
use crate::models::opaque::{OpaqueLoginSession, UserSecretKey};
|
||||
use anyhow::Result;
|
||||
use nkode_rs::nkode_core::nkode_cipher::NKodeCipher;
|
||||
use nkode_rs::from_bytes::FromBytes;
|
||||
use nkode_rs::nkode_core::chacha20prng::Nonce;
|
||||
use async_trait::async_trait;
|
||||
use nkode_rs::nkode_core::policy::{NKodePolicy, DEFAULT_POLICY};
|
||||
use crate::opaque::client::{AuthenticationData, OpaqueAuthLogin, OpaqueAuthRegister, ServerConnectionLogin, ServerConnectionRegister};
|
||||
|
||||
pub struct Login;
|
||||
pub struct Register;
|
||||
|
||||
|
||||
pub struct ClientAppKey<State, S: ClientAuthAPI> {
|
||||
pub struct ClientAppKey<State, S: OpaqueAPI> {
|
||||
api: S,
|
||||
email: Email,
|
||||
user_secret_key: UserSecretKey,
|
||||
_state: PhantomData<State>
|
||||
}
|
||||
|
||||
impl <S: ClientAuthAPI> ClientAppKey<Register,S> {
|
||||
impl <S: OpaqueAPI> ClientAppKey<Register,S> {
|
||||
pub async fn register(self) -> Result<ClientAppKey<Login, S>, String> {
|
||||
// self.repo.set_secret_key(&self.email, &self.user_secret_key).await?;
|
||||
self.api.register_key(&self.email, &self.user_secret_key).await?;
|
||||
Ok(ClientAppKey {
|
||||
api: self.api,
|
||||
@@ -32,7 +34,7 @@ impl <S: ClientAuthAPI> ClientAppKey<Register,S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl <S: ClientAuthAPI> ClientAppKey<Login,S> {
|
||||
impl <S: OpaqueAPI> ClientAppKey<Login,S> {
|
||||
pub async fn login(self) -> Result<ClientAppKeyLoggedIn<S>,String> {
|
||||
let key_login = self.api.login_key(&self.email, &self.user_secret_key).await?;
|
||||
Ok(ClientAppKeyLoggedIn{
|
||||
@@ -44,14 +46,14 @@ impl <S: ClientAuthAPI> ClientAppKey<Login,S> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClientAppKeyLoggedIn<S: ClientAuthAPI> {
|
||||
pub struct ClientAppKeyLoggedIn<S: OpaqueAPI> {
|
||||
api: S,
|
||||
email: Email,
|
||||
user_secret_key: UserSecretKey,
|
||||
key_login: KeyLoginSession,
|
||||
}
|
||||
|
||||
impl <S: ClientAuthAPI> ClientAppKeyLoggedIn<S> {
|
||||
impl <S: OpaqueAPI + AuthAPI> ClientAppKeyLoggedIn<S> {
|
||||
pub async fn register_code(self) -> Result<ClientAppCodeRegister<S>, String> {
|
||||
let icon_nonce = Nonce::new();
|
||||
let icons = self.get_icons(&icon_nonce).await?;
|
||||
@@ -98,7 +100,7 @@ impl <S: ClientAuthAPI> ClientAppKeyLoggedIn<S> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClientAppCodeRegister<S: ClientAuthAPI> {
|
||||
pub struct ClientAppCodeRegister<S: OpaqueAPI> {
|
||||
api: S,
|
||||
email: Email,
|
||||
user_secret_key: UserSecretKey,
|
||||
@@ -108,7 +110,7 @@ pub struct ClientAppCodeRegister<S: ClientAuthAPI> {
|
||||
keypad: Keypad,
|
||||
}
|
||||
|
||||
impl <S: ClientAuthAPI>ClientAppCodeRegister<S> {
|
||||
impl <S: OpaqueAPI + AuthAPI>ClientAppCodeRegister<S> {
|
||||
pub async fn register(self, selected_icons: Vec<IconID>) -> Result<ClientAppCodeLogin<S>, String> {
|
||||
let policy = self.api.get_policy().await?;
|
||||
let keypad = Keypad::new(policy.clone());
|
||||
@@ -137,7 +139,7 @@ impl <S: ClientAuthAPI>ClientAppCodeRegister<S> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClientAppCodeLogin<S: ClientAuthAPI> {
|
||||
pub struct ClientAppCodeLogin<S: OpaqueAPI> {
|
||||
api: S,
|
||||
email: Email,
|
||||
mask: Vec<u64>,
|
||||
@@ -146,7 +148,7 @@ pub struct ClientAppCodeLogin<S: ClientAuthAPI> {
|
||||
cipher: NKodeCipher
|
||||
}
|
||||
|
||||
impl <S: ClientAuthAPI>ClientAppCodeLogin<S> {
|
||||
impl <S: OpaqueAPI>ClientAppCodeLogin<S> {
|
||||
pub fn sort_icons(&self) -> Vec<Icon> {
|
||||
nkode_rs::tensor::reorder(
|
||||
&self.icons,
|
||||
@@ -159,3 +161,89 @@ impl <S: ClientAuthAPI>ClientAppCodeLogin<S> {
|
||||
self.api.login_code(&self.email, &passcode).await
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClientAuth<
|
||||
R: ServerConnectionRegister + Clone,
|
||||
L: ServerConnectionLogin + Clone,
|
||||
K: AuthAPI
|
||||
> {
|
||||
opaque_key_register: OpaqueAuthRegister<R>,
|
||||
opaque_key_login: OpaqueAuthLogin<L>,
|
||||
opaque_code_register: OpaqueAuthRegister<R>,
|
||||
opaque_code_login: OpaqueAuthLogin<L>,
|
||||
nkode_api: K,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<R, L, K> OpaqueAPI for ClientAuth<R, L,K>
|
||||
where
|
||||
R: ServerConnectionRegister + Clone + Sync + Send,
|
||||
L: ServerConnectionLogin + Clone + Sync + Send,
|
||||
K: AuthAPI + Sync + Send,
|
||||
{
|
||||
async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String> {
|
||||
let auth_data = AuthenticationData::from_secret_key(email.as_str(), secret_key.as_slice());
|
||||
self.opaque_key_register.register(&auth_data).await.map_err(|e| format!("error: {}", e))
|
||||
}
|
||||
|
||||
async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoginSession, data: &CodeLoginData) -> Result<(), String> {
|
||||
let auth_data = AuthenticationData::from_code(email.as_str(), passcode);
|
||||
self.opaque_code_register.register(&auth_data).await.map_err(|e| format!("error: {}", e))
|
||||
}
|
||||
|
||||
async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoginSession, String> {
|
||||
let auth_data = AuthenticationData::from_secret_key(&email.as_str(), secret_key.as_slice());
|
||||
let session_key = self.opaque_key_login.login(&auth_data).await.map_err(|e| format!("error: {}", e))?;
|
||||
Ok(KeyLoginSession(
|
||||
LoginSession {
|
||||
email: email.clone(),
|
||||
session_key
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result<CodeLoginSession, String> {
|
||||
let auth_data = AuthenticationData::from_code(email.as_str(), passcode);
|
||||
let session_key = self.opaque_code_login.login(&auth_data).await.map_err(|e| format!("error: {}", e))?;
|
||||
Ok(CodeLoginSession(
|
||||
LoginSession {
|
||||
email: email.clone(),
|
||||
session_key
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<R, L, K> AuthAPI for ClientAuth<R, L, K>
|
||||
where
|
||||
R: ServerConnectionRegister + Clone + Sync + Send,
|
||||
L: ServerConnectionLogin + Clone + Sync + Send,
|
||||
K: AuthAPI + Sync + Send,
|
||||
{
|
||||
async fn get_new_icons(
|
||||
&self,
|
||||
key_login_session: &KeyLoginSession,
|
||||
) -> Result<Vec<Icon>, String> {
|
||||
self.nkode_api
|
||||
.get_new_icons(key_login_session)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_login_data(
|
||||
&self,
|
||||
key_login_session: &KeyLoginSession,
|
||||
) -> Result<CodeLoginData, String> {
|
||||
self.nkode_api
|
||||
.get_login_data(key_login_session)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn is_code_registered(&self, key_login_session: &KeyLoginSession) -> Result<bool, String> {
|
||||
self.nkode_api.is_code_registered(key_login_session).await
|
||||
}
|
||||
|
||||
async fn get_policy(&self) -> Result<NKodePolicy, String> {
|
||||
Ok(DEFAULT_POLICY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
// // 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
use async_trait::async_trait;
|
||||
use nkode_rs::nkode_core::chacha20prng::Nonce; use nkode_rs::nkode_core::keypad::Keypad;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use getset::Getters;
|
||||
use nkode_rs::from_bytes::FromBytes;
|
||||
use nkode_rs::nkode_core::policy::NKodePolicy;
|
||||
use crate::models::email::Email;
|
||||
use crate::models::opaque::{OpaqueSessionKey, UserSecretKey};
|
||||
|
||||
struct LoginSession {
|
||||
email: Email,
|
||||
session_id: Uuid,
|
||||
session_key: OpaqueSessionKey,
|
||||
pub struct LoginSession {
|
||||
pub(crate) email: Email,
|
||||
pub(crate) session_key: OpaqueSessionKey,
|
||||
}
|
||||
|
||||
pub struct KeyLoginSession(LoginSession);
|
||||
pub struct CodeLoginSession(LoginSession);
|
||||
pub struct KeyLoginSession(pub(crate) LoginSession);
|
||||
pub struct CodeLoginSession(pub(crate) LoginSession);
|
||||
|
||||
pub const ICON_ID_SIZE: usize = 32;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@@ -44,8 +42,6 @@ impl Icon {
|
||||
pub struct CodeLoginData {
|
||||
#[get = "pub"]
|
||||
pub(crate) mask: Vec<u64>,
|
||||
// #[get = "pub"]
|
||||
// pub(crate) icons: Vec<Icon>,
|
||||
#[get = "pub"]
|
||||
pub(crate) cipher_nonce: Nonce,
|
||||
#[get = "pub"]
|
||||
@@ -54,11 +50,16 @@ pub struct CodeLoginData {
|
||||
pub(crate) keypad: Keypad,
|
||||
}
|
||||
|
||||
pub trait ClientAuthAPI {
|
||||
#[async_trait]
|
||||
pub trait OpaqueAPI {
|
||||
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 login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoginSession, String>;
|
||||
async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result<CodeLoginSession, String>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait AuthAPI {
|
||||
async fn get_new_icons(&self, key_login_session: &KeyLoginSession) -> Result<Vec<Icon>, 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>;
|
||||
|
||||
@@ -11,8 +11,7 @@ use nkode_rs::from_bytes::FromBytes;
|
||||
|
||||
const USER_KEY_SIZE: usize = 16;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(PartialEq)]
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct UserSecretKey(Zeroizing<[u8; USER_KEY_SIZE]>);
|
||||
|
||||
impl UserSecretKey {
|
||||
@@ -20,6 +19,10 @@ impl UserSecretKey {
|
||||
let out = blake3::derive_key("your-app chacha20 secret key v1", &self.0.as_slice());
|
||||
SecretKey::from_bytes(&out).unwrap()
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
self.0.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytes<USER_KEY_SIZE> for UserSecretKey {
|
||||
@@ -30,6 +33,7 @@ impl FromBytes<USER_KEY_SIZE> for UserSecretKey {
|
||||
|
||||
const OPAQUE_SESSION_KEY_SIZE: usize = 64;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OpaqueSessionKey(Zeroizing<[u8; OPAQUE_SESSION_KEY_SIZE]>);
|
||||
|
||||
impl FromBytes<OPAQUE_SESSION_KEY_SIZE> for OpaqueSessionKey {
|
||||
@@ -52,13 +56,13 @@ pub type NKodeServerSetup = ServerSetup<NKodeCipherSuite, PrivateKey<Ristretto25
|
||||
pub type PasswordFile = GenericArray<u8, RegistrationUploadLen<NKodeCipherSuite>>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct LoginSession {
|
||||
pub struct OpaqueLoginSession {
|
||||
pub response: CredentialResponse<NKodeCipherSuite>,
|
||||
pub session_id: Uuid
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RegisterSession {
|
||||
pub struct OpaqueRegisterSession {
|
||||
pub response: RegistrationResponse<NKodeCipherSuite>,
|
||||
pub session_id: Uuid
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::fmt;
|
||||
use async_trait::async_trait;
|
||||
use nkode_rs::from_bytes::FromBytes;
|
||||
use uuid::Uuid;
|
||||
|
||||
use opaque_ke::rand::rngs::OsRng;
|
||||
use opaque_ke::{
|
||||
ClientLogin, ClientLoginFinishParameters,
|
||||
@@ -8,10 +9,8 @@ use opaque_ke::{
|
||||
CredentialFinalization, CredentialRequest,
|
||||
RegistrationRequest,
|
||||
};
|
||||
|
||||
use crate::models::opaque::{RegisterSession, LoginSession, NKodeCipherSuite, PasswordFile, OpaqueSessionKey};
|
||||
|
||||
|
||||
use crate::models::app::KeyLoginSession;
|
||||
use crate::models::opaque::{OpaqueRegisterSession, OpaqueLoginSession, NKodeCipherSuite, PasswordFile, OpaqueSessionKey};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ClientAuthError {
|
||||
@@ -19,6 +18,17 @@ pub enum ClientAuthError {
|
||||
Transport(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientAuthError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ClientAuthError::Opaque(msg) => write!(f, "{}", msg),
|
||||
ClientAuthError::Transport(msg) => write!(f, "transport error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ClientAuthError {}
|
||||
|
||||
pub struct AuthenticationData {
|
||||
pub identifier: Vec<u8>,
|
||||
pub secret: Vec<u8>,
|
||||
@@ -32,11 +42,11 @@ impl AuthenticationData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_code(email: &str, code: &[usize]) -> Self {
|
||||
pub fn from_code(email: &str, code: &[u64]) -> Self {
|
||||
// fixed-width so it's stable across 32-bit vs 64-bit platforms
|
||||
let mut secret = Vec::with_capacity(code.len() * 8);
|
||||
for &n in code {
|
||||
secret.extend_from_slice(&(n as u64).to_le_bytes());
|
||||
secret.extend_from_slice(&(n).to_le_bytes());
|
||||
}
|
||||
Self {
|
||||
identifier: email.as_bytes().to_vec(),
|
||||
@@ -51,7 +61,7 @@ pub trait ServerConnectionRegister {
|
||||
&self,
|
||||
identifier: &[u8],
|
||||
message: &RegistrationRequest<NKodeCipherSuite>,
|
||||
) -> Result<RegisterSession, ClientAuthError>;
|
||||
) -> Result<OpaqueRegisterSession, ClientAuthError>;
|
||||
|
||||
async fn finish(
|
||||
&self,
|
||||
@@ -66,7 +76,7 @@ pub trait ServerConnectionLogin {
|
||||
&self,
|
||||
identifier: &[u8],
|
||||
request: &CredentialRequest<NKodeCipherSuite>,
|
||||
) -> Result<LoginSession, ClientAuthError>;
|
||||
) -> Result<OpaqueLoginSession, ClientAuthError>;
|
||||
|
||||
async fn finish(
|
||||
&self,
|
||||
@@ -99,7 +109,6 @@ impl <R: ServerConnectionRegister + Clone>OpaqueAuthRegister<R> {
|
||||
pub async fn register(
|
||||
&self,
|
||||
auth: &AuthenticationData,
|
||||
// server: &mut impl ServerConnectionRegister,
|
||||
) -> Result<(), ClientAuthError> {
|
||||
let mut rng = OsRng;
|
||||
let start = ClientRegistration::<NKodeCipherSuite>::start(&mut rng, &auth.secret)
|
||||
@@ -131,7 +140,7 @@ impl <L: ServerConnectionLogin + Clone>OpaqueAuthLogin<L> {
|
||||
pub async fn login(
|
||||
&self,
|
||||
auth: &AuthenticationData,
|
||||
) -> Result<Vec<u8>, ClientAuthError> {
|
||||
) -> Result<OpaqueSessionKey, ClientAuthError> {
|
||||
let mut rng = OsRng;
|
||||
let start = ClientLogin::<NKodeCipherSuite>::start(&mut rng, &auth.secret)
|
||||
.map_err(|e| ClientAuthError::Opaque(format!("client login start: {e:?}")))?;
|
||||
@@ -153,6 +162,9 @@ impl <L: ServerConnectionLogin + Clone>OpaqueAuthLogin<L> {
|
||||
.finish(&server_start.session_id, &finish.message)
|
||||
.await
|
||||
.map_err(|e| ClientAuthError::Transport(format!("server login finish: {e:?}")))?;
|
||||
Ok(finish.session_key.to_vec())
|
||||
Ok(
|
||||
OpaqueSessionKey::from_bytes(finish.session_key.as_slice())
|
||||
.map_err(|e| ClientAuthError::Opaque(format!("from bytes error {e:?}")))?
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use opaque_ke::{
|
||||
ServerRegistration,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
use crate::models::opaque::{LoginSession, NKodeCipherSuite, NKodeServerSetup, OpaqueSessionKey, PasswordFile, RegisterSession};
|
||||
use crate::models::opaque::{OpaqueLoginSession, NKodeCipherSuite, NKodeServerSetup, OpaqueSessionKey, PasswordFile, OpaqueRegisterSession};
|
||||
use crate::repository::opaque::repos::{OpaqueDatabaseRepo, AuthRepoError, OpaqueSessionRepo};
|
||||
|
||||
pub struct RegCache {
|
||||
@@ -79,7 +79,7 @@ impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo> OpaqueAuth<R, S> {
|
||||
&mut self,
|
||||
identifier: &[u8],
|
||||
request: RegistrationRequest<NKodeCipherSuite>,
|
||||
) -> Result<RegisterSession, String> {
|
||||
) -> Result<OpaqueRegisterSession, String> {
|
||||
K::prereq_for_register(&self.user_repo, identifier)
|
||||
.map_err(|e| format!("registration prereq failed: {e:?}"))?;
|
||||
let start = ServerRegistration::<NKodeCipherSuite>::start(
|
||||
@@ -91,7 +91,7 @@ impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo> OpaqueAuth<R, S> {
|
||||
.new_reg_session(identifier)
|
||||
.map_err(|e| format!("reg cache: {e}"))?;
|
||||
|
||||
Ok(RegisterSession { session_id: cache.session_id, response: start.message })
|
||||
Ok(OpaqueRegisterSession { session_id: cache.session_id, response: start.message })
|
||||
}
|
||||
|
||||
pub async fn reg_finish<K: CredKind>(
|
||||
@@ -115,14 +115,13 @@ impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo> OpaqueAuth<R, S> {
|
||||
&mut self,
|
||||
identifier: &[u8],
|
||||
request: CredentialRequest<NKodeCipherSuite>,
|
||||
) -> Result<LoginSession, String> {
|
||||
) -> Result<OpaqueLoginSession, String> {
|
||||
let password_file = K::get_pf(&self.user_repo, identifier)
|
||||
.map_err(|e| format!("repo read: {e:?}"))?;
|
||||
|
||||
let password_file =
|
||||
ServerRegistration::<NKodeCipherSuite>::deserialize(password_file.as_slice())
|
||||
.map_err(|e| format!("pf deserialize: {e:?}"))?;
|
||||
|
||||
let mut server_rng = OsRng;
|
||||
let start = ServerLogin::start(
|
||||
&mut server_rng,
|
||||
@@ -132,12 +131,10 @@ impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo> OpaqueAuth<R, S> {
|
||||
identifier,
|
||||
ServerLoginParameters::default(),
|
||||
).map_err(|e| format!("opaque login start: {e:?}"))?;
|
||||
|
||||
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(OpaqueLoginSession { session_id: cache.session_id, response: start.message })
|
||||
}
|
||||
|
||||
pub async fn login_finish<K: CredKind>(
|
||||
|
||||
@@ -4,7 +4,7 @@ use tokio::sync::Mutex;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
use opaque_ke::{CredentialFinalization, CredentialRequest, RegistrationRequest};
|
||||
use crate::models::opaque::{LoginSession, NKodeCipherSuite, NKodeServerSetup, OpaqueSessionKey, PasswordFile, RegisterSession};
|
||||
use crate::models::opaque::{OpaqueLoginSession, NKodeCipherSuite, NKodeServerSetup, OpaqueSessionKey, PasswordFile, OpaqueRegisterSession};
|
||||
use crate::opaque::client::{ClientAuthError, ServerConnectionLogin, ServerConnectionRegister};
|
||||
use crate::opaque::server::{OpaqueAuth, CredKind, Key, Code};
|
||||
use crate::repository::opaque::in_memory::in_memory_auth_repo::InMemoryAuthRepo;
|
||||
@@ -31,13 +31,13 @@ pub type InMemoryCodeServer = InMemoryServer<Code>;
|
||||
#[async_trait]
|
||||
impl<K> ServerConnectionRegister for InMemoryServer<K>
|
||||
where
|
||||
K: CredKind + Send + Sync,
|
||||
K: CredKind + Sync,
|
||||
{
|
||||
async fn start(
|
||||
&self,
|
||||
identifier: &[u8],
|
||||
message: &RegistrationRequest<NKodeCipherSuite>,
|
||||
) -> Result<RegisterSession, ClientAuthError> {
|
||||
) -> Result<OpaqueRegisterSession, 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.lock().await
|
||||
@@ -67,7 +67,7 @@ where
|
||||
&self,
|
||||
identifier: &[u8],
|
||||
request: &CredentialRequest<NKodeCipherSuite>,
|
||||
) -> Result<LoginSession, ClientAuthError> {
|
||||
) -> Result<OpaqueLoginSession, ClientAuthError> {
|
||||
self.auth.lock().await
|
||||
.login_start::<K>(identifier, request.clone())
|
||||
.await
|
||||
|
||||
@@ -12,10 +12,10 @@ async fn opaque_key_registration_and_login_roundtrip() {
|
||||
let auth_data = AuthenticationData::from_secret_key("a@b.com", b"supersecret16bytes");
|
||||
auth_reg.register(&auth_data).await.expect("registration should succeed");
|
||||
let login_reg = OpaqueAuthLogin::new(server);
|
||||
let session_key =login_reg.login(&auth_data)
|
||||
let _ =login_reg.login(&auth_data)
|
||||
.await
|
||||
.expect("login should succeed");
|
||||
assert!(!session_key.is_empty());
|
||||
// assert!(!session_key.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -38,9 +38,9 @@ async fn opaque_login_fails_if_not_registered() {
|
||||
async fn cannot_register_code_before_key() {
|
||||
let mut rng = OsRng;
|
||||
let server_setup = NKodeServerSetup::new(&mut rng);
|
||||
let mut server = InMemoryCodeServer::new(server_setup);
|
||||
let 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", &[1u64,2,3,4]);
|
||||
let err = auth_reg.register(&auth)
|
||||
.await
|
||||
.expect_err("should fail because key is not registered");
|
||||
|
||||
Reference in New Issue
Block a user