use std::marker::PhantomData; use nkode_rs::nkode_core::keypad::Keypad; use crate::models::app::{OpaqueAPI, AuthAPI, CodeLoginData, CodeLoginSession, Icon, IconID, KeyLoginSession, ICON_ID_SIZE, LoginSession}; use crate::models::email::Email; 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 { api: S, email: Email, user_secret_key: UserSecretKey, _state: PhantomData } impl ClientAppKey { pub async fn register(self) -> Result, String> { self.api.register_key(&self.email, &self.user_secret_key).await?; Ok(ClientAppKey { api: self.api, email: self.email, user_secret_key: self.user_secret_key, _state: PhantomData::, }) } } impl ClientAppKey { pub async fn login(self) -> Result,String> { let key_login = self.api.login_key(&self.email, &self.user_secret_key).await?; Ok(ClientAppKeyLoggedIn{ api: self.api, email: self.email, user_secret_key: self.user_secret_key, key_login }) } } pub struct ClientAppKeyLoggedIn { api: S, email: Email, user_secret_key: UserSecretKey, key_login: KeyLoginSession, } impl ClientAppKeyLoggedIn { pub async fn register_code(self) -> Result, String> { let icon_nonce = Nonce::new(); let icons = self.get_icons(&icon_nonce).await?; let policy = self.api.get_policy().await?; Ok(ClientAppCodeRegister { api: self.api, email: self.email, user_secret_key: self.user_secret_key, key_login: self.key_login, icons, icon_nonce, keypad: Keypad::new(policy), }) } async fn get_icons(&self, icon_nonce: &Nonce) -> Result, String> { let chacha20_key = self.user_secret_key.chacha20_secret_key(); let mut chacha_cipher = nkode_rs::nkode_core::chacha20prng::ChaCha20PRNG::new(&chacha20_key, icon_nonce); let mut icons = self.api.get_new_icons(&self.key_login).await?; for icon in &mut icons { let bytes = chacha_cipher.read_u8(ICON_ID_SIZE)?; let new_id = IconID::from_bytes(bytes.as_slice()).unwrap(); icon.update_id(new_id); } Ok(icons) } pub async fn login_code(self) -> Result, String> { let login_data = self.api.get_login_data(&self.key_login).await?; let icons = self.get_icons(&login_data.icon_nonce()).await?; let policy = self.api.get_policy().await?; let nkode_secret_key = self.user_secret_key.chacha20_secret_key(); let cipher = NKodeCipher::from_nonce(policy, &nkode_secret_key, login_data.icon_nonce())?; Ok( ClientAppCodeLogin{ api: self.api, email: self.email, mask: login_data.mask, icons, keypad: login_data.keypad, cipher, } ) } } pub struct ClientAppCodeRegister { api: S, email: Email, user_secret_key: UserSecretKey, key_login: KeyLoginSession, icon_nonce: Nonce, icons: Vec, keypad: Keypad, } impl ClientAppCodeRegister { pub async fn register(self, selected_icons: Vec) -> Result, String> { let policy = self.api.get_policy().await?; let keypad = Keypad::new(policy.clone()); let secret_key = self.user_secret_key.chacha20_secret_key(); let (cipher, cipher_nonce) = NKodeCipher::new(policy, &secret_key); let passcode_idx: Vec = selected_icons .iter() .filter_map(|x1| self.icons.iter().position(|x| x.id() == x1)) .collect(); let ciphered_nkode = cipher.encipher(&passcode_idx).unwrap(); let data = CodeLoginData{ mask: ciphered_nkode.mask, cipher_nonce, icon_nonce: self.icon_nonce, keypad, }; self.api.register_code(&self.email, &ciphered_nkode.passcode, &self.key_login, &data).await?; Ok(ClientAppCodeLogin{ api: self.api, mask: data.mask, email: self.email, keypad: data.keypad, cipher, icons: self.icons }) } } pub struct ClientAppCodeLogin { api: S, email: Email, mask: Vec, icons: Vec, keypad: Keypad, cipher: NKodeCipher } impl ClientAppCodeLogin { pub fn sort_icons(&self) -> Vec { nkode_rs::tensor::reorder( &self.icons, self.keypad.indices() ).unwrap().to_vec() } pub async fn login(&self, selected_keys: &Vec) -> Result { let passcode = self.cipher.decipher(selected_keys, self.keypad.indices(), &self.mask).map_err(|e| format!("invalid keys: {e}"))?; self.api.login_code(&self.email, &passcode).await } } pub struct ClientAuth< R: ServerConnectionRegister + Clone, L: ServerConnectionLogin + Clone, K: AuthAPI > { opaque_key_register: OpaqueAuthRegister, opaque_key_login: OpaqueAuthLogin, opaque_code_register: OpaqueAuthRegister, opaque_code_login: OpaqueAuthLogin, nkode_api: K, } #[async_trait] impl OpaqueAPI for ClientAuth 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 { 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 { 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 AuthAPI for ClientAuth 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, String> { self.nkode_api .get_new_icons(key_login_session) .await } async fn get_login_data( &self, key_login_session: &KeyLoginSession, ) -> Result { self.nkode_api .get_login_data(key_login_session) .await } async fn is_code_registered(&self, key_login_session: &KeyLoginSession) -> Result { self.nkode_api.is_code_registered(key_login_session).await } async fn get_policy(&self) -> Result { Ok(DEFAULT_POLICY) } }