From b03a23b2800d96b2ac101cd261a164d52b418dca Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 17 Dec 2025 21:53:33 -0600 Subject: [PATCH] refactor user state --- src/client/app.rs | 161 +------------------------------------- src/client/mod.rs | 3 +- src/client/states.rs | 180 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 159 deletions(-) create mode 100644 src/client/states.rs diff --git a/src/client/app.rs b/src/client/app.rs index c6cafb5..b2623b1 100644 --- a/src/client/app.rs +++ b/src/client/app.rs @@ -1,166 +1,11 @@ -use std::marker::PhantomData; -use nkode_rs::nkode_core::keypad::Keypad; -use crate::shared::models::app::{AuthAPI, CodeLoginData, CodeLoggedInSession, Icon, IconID, KeyLoggedInSession, ICON_ID_SIZE}; +use crate::shared::models::app::{AuthAPI, CodeLoggedInSession, CodeLoginData, Icon, KeyLoggedInSession}; use crate::shared::models::email::Email; -use crate::shared::models::opaque::{UserSecretKey}; +use crate::shared::models::opaque::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::client::opaque::{AuthenticationData, OpaqueAuth, ServerConnectionLogin, ServerConnectionRegister}; +use crate::client::opaque::{AuthenticationData, OpaqueAuth, ServerConnectionLogin, ServerConnectionRegister}; -struct Login; -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: KeyLoggedInSession, -} - -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: KeyLoggedInSession, - 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<'a, R, K> where diff --git a/src/client/mod.rs b/src/client/mod.rs index 08c9146..77daff7 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,2 +1,3 @@ mod app; -pub mod opaque; \ No newline at end of file +pub mod opaque; +mod states; \ No newline at end of file diff --git a/src/client/states.rs b/src/client/states.rs new file mode 100644 index 0000000..f431104 --- /dev/null +++ b/src/client/states.rs @@ -0,0 +1,180 @@ +use std::marker::PhantomData; +use nkode_rs::nkode_core::chacha20prng::Nonce; +use nkode_rs::nkode_core::keypad::Keypad; +use nkode_rs::nkode_core::nkode_cipher::NKodeCipher; +use nkode_rs::from_bytes::FromBytes; +use crate::shared::models::app::{AuthAPI, CodeLoggedInSession, CodeLoginData, Icon, IconID, KeyLoggedInSession, ICON_ID_SIZE}; +use crate::shared::models::email::Email; +use crate::shared::models::opaque::UserSecretKey; + +struct KeyRegister; +struct KeyLogin; +struct KeyLoggedIn; +struct CodeRegister; +struct CodeLogIn; +struct CodeLoggedIn; + + +pub struct UserState { + api: S, + email: Email, + user_secret_key: UserSecretKey, + key_login: Option, + icon_nonce: Option, + icons: Option>, + keypad: Option, + mask: Option>, + cipher: Option, + _state: PhantomData +} + +struct Login; + +struct Register; + +pub struct UserStateKey { + api: S, + email: Email, + user_secret_key: UserSecretKey, + _state: PhantomData +} + +impl UserStateKey { + pub async fn register(self) -> anyhow::Result, String> { + self.api.register_key(&self.email, &self.user_secret_key).await?; + Ok(UserStateKey { + api: self.api, + email: self.email, + user_secret_key: self.user_secret_key, + _state: PhantomData::, + }) + } +} + +impl UserStateKey { + pub async fn login(self) -> anyhow::Result,String> { + let key_login = self.api.login_key(&self.email, &self.user_secret_key).await?; + Ok(UserStateKeyLoggedIn { + api: self.api, + email: self.email, + user_secret_key: self.user_secret_key, + key_login + }) + } +} + +pub struct UserStateKeyLoggedIn { + api: S, + email: Email, + user_secret_key: UserSecretKey, + key_login: KeyLoggedInSession, +} + +impl UserStateKeyLoggedIn { + pub async fn register_code(self) -> anyhow::Result, String> { + let icon_nonce = Nonce::new(); + let icons = self.get_icons(&icon_nonce).await?; + let policy = self.api.get_policy().await?; + Ok(UserStateCodeRegister { + 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) -> anyhow::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) -> anyhow::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( + UserStateCodeLogin { + api: self.api, + email: self.email, + mask: login_data.mask, + icons, + keypad: login_data.keypad, + cipher, + } + ) + } +} + +pub struct UserStateCodeRegister { + api: S, + email: Email, + user_secret_key: UserSecretKey, + key_login: KeyLoggedInSession, + icon_nonce: Nonce, + icons: Vec, + keypad: Keypad, +} + +impl UserStateCodeRegister { + pub async fn register(self, selected_icons: Vec) -> anyhow::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(UserStateCodeLogin { + api: self.api, + mask: data.mask, + email: self.email, + keypad: data.keypad, + cipher, + icons: self.icons + }) + } +} + +pub struct UserStateCodeLogin { + api: S, + email: Email, + mask: Vec, + icons: Vec, + keypad: Keypad, + cipher: NKodeCipher +} + +impl UserStateCodeLogin { + 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) -> anyhow::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 + } +} \ No newline at end of file