implement signed session data
This commit is contained in:
160
Cargo.lock
generated
160
Cargo.lock
generated
@@ -43,6 +43,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-polyfill"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.2.0"
|
||||
@@ -74,6 +83,26 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
||||
dependencies = [
|
||||
"bincode_derive",
|
||||
"serde",
|
||||
"unty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode_derive"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09"
|
||||
dependencies = [
|
||||
"virtue",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
@@ -153,6 +182,15 @@ dependencies = [
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
@@ -174,6 +212,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.5.5"
|
||||
@@ -332,6 +376,18 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.1"
|
||||
@@ -414,6 +470,29 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"hash32",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"spin",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.12.4"
|
||||
@@ -457,20 +536,34 @@ version = "0.2.178"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nkode-protocol"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"bincode",
|
||||
"blake3",
|
||||
"email_address",
|
||||
"getset",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"nkode-rs",
|
||||
"opaque-ke",
|
||||
"postcard",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"uuid",
|
||||
"zeroize",
|
||||
@@ -548,6 +641,19 @@ dependencies = [
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"embedded-io 0.4.0",
|
||||
"embedded-io 0.6.1",
|
||||
"heapless",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@@ -677,6 +783,12 @@ version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.7.3"
|
||||
@@ -765,6 +877,15 @@ dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
@@ -775,6 +896,12 @@ dependencies = [
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@@ -792,6 +919,26 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.48.0"
|
||||
@@ -825,6 +972,12 @@ version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unty"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.19.0"
|
||||
@@ -833,6 +986,7 @@ checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"js-sys",
|
||||
"serde_core",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -842,6 +996,12 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "virtue"
|
||||
version = "0.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
|
||||
|
||||
[[package]]
|
||||
name = "voprf"
|
||||
version = "0.5.0"
|
||||
|
||||
@@ -8,7 +8,7 @@ opaque-ke = { version = "4.0.1",default-features = true, features = [ "std","arg
|
||||
rand = { version = "0.8.5", features = ["std"] }
|
||||
sha2 = "0.10.9"
|
||||
async-trait = "0.1.89"
|
||||
uuid = { version = "1.19.0", features = ["v4"] }
|
||||
uuid = { version = "1.19.0", features = ["serde","v4"] }
|
||||
tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread", "sync"] }
|
||||
nkode-rs = { path = "nkode-rs" }
|
||||
zeroize = "1.8.2"
|
||||
@@ -17,5 +17,10 @@ email_address = "0.2.9"
|
||||
anyhow = "1.0.100"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
getset = "0.1.6"
|
||||
hmac = "0.12.1"
|
||||
hkdf = "0.12.4"
|
||||
thiserror = "2.0.17"
|
||||
bincode = "2.0.1"
|
||||
postcard = { version = "1.1.3", features = ["use-std"] }
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
use async_trait::async_trait;
|
||||
use crate::client::client_auth_api::ClientAuth;
|
||||
use crate::client::opaque::{ServerConnectionLogin, ServerConnectionRegister};
|
||||
use crate::shared::models::app::AuthAPI;
|
||||
use crate::shared::models::email::Email;
|
||||
use crate::shared::models::opaque::UserSecretKey;
|
||||
use crate::shared::email::Email;
|
||||
use crate::shared::opaque::UserSecretKey;
|
||||
use crate::shared::user_api::UserAPI;
|
||||
//https://chatgpt.com/c/69441737-c990-8333-9737-7ac75232da1d
|
||||
|
||||
struct ClientApp<K, C>
|
||||
struct ClientApp<'a, R, U, C>
|
||||
where
|
||||
K: AuthAPI,
|
||||
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||
U: UserAPI,
|
||||
C: ClientRepo
|
||||
{
|
||||
auth_api: K,
|
||||
auth_api: ClientAuth<'a, R, U>,
|
||||
client_repo: C,
|
||||
}
|
||||
|
||||
impl <K, C>ClientApp<K, C>
|
||||
impl <'a, R, U, C>ClientApp<'a, R, U, C>
|
||||
where
|
||||
K: AuthAPI,
|
||||
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||
U: UserAPI,
|
||||
C: ClientRepo
|
||||
{
|
||||
fn new(auth_api: K, client_repo: C) -> Self {
|
||||
fn new(auth_api: ClientAuth<'a, R, U>, client_repo: C) -> Self {
|
||||
Self {
|
||||
auth_api,
|
||||
client_repo
|
||||
|
||||
@@ -1,38 +1,45 @@
|
||||
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::email::Email;
|
||||
use crate::shared::opaque::UserSecretKey;
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use nkode_rs::nkode_core::policy::{NKodePolicy, DEFAULT_POLICY};
|
||||
use crate::client::opaque::{OpaqueAuthData, OpaqueAuth, ServerConnectionLogin, ServerConnectionRegister};
|
||||
use crate::shared::models::store::UserAuthStore;
|
||||
use crate::shared::signed_session_data::SignedSessionData;
|
||||
use crate::shared::user_api::UserAPI;
|
||||
|
||||
pub struct ClientAuth<'a, R, U>
|
||||
where
|
||||
R: ServerConnectionRegister + ServerConnectionLogin + Clone,
|
||||
U: UserAuthStore
|
||||
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||
U: UserAPI
|
||||
{
|
||||
opaque_key_register: OpaqueAuth<'a, R>,
|
||||
opaque_key_login: OpaqueAuth<'a, R>,
|
||||
opaque_code_register: OpaqueAuth<'a, R>,
|
||||
opaque_code_login: OpaqueAuth<'a, R>,
|
||||
user_store: U
|
||||
user_api: U
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, R, U> AuthAPI for ClientAuth<'a, R, U>
|
||||
where
|
||||
R: ServerConnectionRegister + ServerConnectionLogin + Clone + Sync + Send,
|
||||
U: UserAuthStore + Sync + Send,
|
||||
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||
U: UserAPI,
|
||||
{
|
||||
async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String> {
|
||||
let auth_data = OpaqueAuthData::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: &KeyLoggedInSession, data: &CodeLoginData) -> Result<(), String> {
|
||||
async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, data: CodeLoginData) -> Result<(), String> {
|
||||
let auth_data = OpaqueAuthData::from_code(email.as_str(), passcode);
|
||||
self.opaque_code_register.register(&auth_data).await.map_err(|e| format!("error: {}", e))
|
||||
self.opaque_code_register.register(&auth_data).await.map_err(|e| format!("error: {}", e))?;
|
||||
let signed_session = SignedSessionData::new(
|
||||
key_login_session.0.session_id,
|
||||
data,
|
||||
&key_login_session.0.session_key
|
||||
).map_err(|e| format!("error: {e:?}"))?;
|
||||
self.user_api.set_code_login_data(signed_session).await
|
||||
}
|
||||
|
||||
async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoggedInSession, String> {
|
||||
@@ -49,28 +56,29 @@ where
|
||||
|
||||
async fn get_new_icons(
|
||||
&self,
|
||||
key_login_session: &KeyLoggedInSession,
|
||||
) -> Result<Vec<Icon>, String> {
|
||||
// self.nkode_api
|
||||
// .get_new_icons(key_login_session)
|
||||
// .await
|
||||
//
|
||||
todo!()
|
||||
self.user_api.get_new_icons().await
|
||||
}
|
||||
|
||||
async fn get_login_data(
|
||||
&self,
|
||||
key_login_session: &KeyLoggedInSession,
|
||||
) -> Result<CodeLoginData, String> {
|
||||
// self.nkode_api
|
||||
// .get_login_data(key_login_session)
|
||||
// .await
|
||||
todo!()
|
||||
let session = SignedSessionData::new(
|
||||
key_login_session.0.session_id,
|
||||
key_login_session.0.email.clone(),
|
||||
&key_login_session.0.session_key
|
||||
).map_err(|e| format!("error: {e:?}"))?;
|
||||
self.user_api.get_login_data(session).await
|
||||
}
|
||||
|
||||
async fn is_code_registered(&self, key_login_session: &KeyLoggedInSession) -> Result<bool, String> {
|
||||
// self.nkode_api.is_code_registered(key_login_session).await
|
||||
todo!()
|
||||
let session = SignedSessionData::new(
|
||||
key_login_session.0.session_id,
|
||||
key_login_session.0.email.clone(),
|
||||
&key_login_session.0.session_key
|
||||
).map_err(|e| format!("error: {e:?}"))?;
|
||||
self.user_api.is_code_registered(session).await
|
||||
}
|
||||
|
||||
async fn get_policy(&self) -> Result<NKodePolicy, String> {
|
||||
|
||||
@@ -9,7 +9,7 @@ use opaque_ke::{
|
||||
RegistrationRequest,
|
||||
};
|
||||
use crate::shared::models::app::LoggedInSession;
|
||||
use crate::shared::models::opaque::{OpaqueRegisterSession, OpaqueLoginSession, NKodeCipherSuite, PasswordFile};
|
||||
use crate::shared::opaque::{OpaqueRegisterSession, OpaqueLoginSession, NKodeCipherSuite, PasswordFile};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ClientAuthError {
|
||||
@@ -55,7 +55,7 @@ impl OpaqueAuthData {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ServerConnectionRegister {
|
||||
pub trait ServerConnectionRegister: Send + Sync {
|
||||
async fn start(
|
||||
&self,
|
||||
identifier: &[u8],
|
||||
@@ -70,7 +70,7 @@ pub trait ServerConnectionRegister {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ServerConnectionLogin {
|
||||
pub trait ServerConnectionLogin: Send + Sync {
|
||||
async fn start(
|
||||
&self,
|
||||
identifier: &[u8],
|
||||
|
||||
@@ -4,8 +4,8 @@ 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;
|
||||
use crate::shared::email::Email;
|
||||
use crate::shared::opaque::UserSecretKey;
|
||||
|
||||
struct KeyLogin;
|
||||
|
||||
@@ -68,7 +68,7 @@ impl <S: AuthAPI> UserStateKeyLoggedIn<S> {
|
||||
async fn get_icons(&self, icon_nonce: &Nonce) -> Result<Vec<Icon>, 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?;
|
||||
let mut icons = self.api.get_new_icons().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();
|
||||
@@ -123,7 +123,7 @@ impl <S: AuthAPI> UserStateCodeRegister<S> {
|
||||
icon_nonce: self.icon_nonce,
|
||||
keypad,
|
||||
};
|
||||
self.api.register_code(&self.email, &ciphered_nkode.passcode, &self.key_login, &data).await?;
|
||||
self.api.register_code(&self.email, &ciphered_nkode.passcode, &self.key_login, data.clone()).await?;
|
||||
Ok(UserStateCodeLogin {
|
||||
api: self.api,
|
||||
mask: data.mask,
|
||||
|
||||
@@ -5,20 +5,20 @@ use opaque_ke::argon2::password_hash::rand_core::OsRng;
|
||||
use nkode_rs::from_bytes::FromBytes;
|
||||
use crate::server::models::CredKind;
|
||||
use crate::server::repository::opaque_repo::{AuthRepoError, OpaqueDatabaseRepo, OpaqueSessionRepo};
|
||||
use crate::shared::models::store::UserAuthStore;
|
||||
use crate::server::repository::UserRepo::UserRepo;
|
||||
use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession, LoggedInSession};
|
||||
use crate::shared::models::email::Email;
|
||||
use crate::shared::models::opaque::{NKodeCipherSuite, NKodeServerSetup, OpaqueLoginSession, OpaqueRegisterSession, OpaqueSessionKey, PasswordFile};
|
||||
use crate::shared::email::Email;
|
||||
use crate::shared::opaque::{NKodeCipherSuite, NKodeServerSetup, OpaqueLoginSession, OpaqueRegisterSession, OpaqueSessionKey, PasswordFile};
|
||||
|
||||
|
||||
pub struct ServerApp<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserAuthStore> {
|
||||
pub struct ServerApp<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> {
|
||||
server_setup: NKodeServerSetup,
|
||||
opaque_db: R,
|
||||
opaque_sess: S,
|
||||
user_db: U,
|
||||
}
|
||||
|
||||
impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserAuthStore> ServerApp<R, S, U> {
|
||||
impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> ServerApp<R, S, U> {
|
||||
pub fn new(server_setup: NKodeServerSetup, opaque_db: R, opaque_sess: S, user_db: U) -> Self {
|
||||
Self { server_setup, opaque_db, opaque_sess, user_db}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use async_trait::async_trait;
|
||||
use uuid::Uuid;
|
||||
use opaque_ke::ServerLogin;
|
||||
use crate::server::repository::opaque_repo::{AuthRepoError, OpaqueDatabaseRepo};
|
||||
use crate::shared::models::opaque::{NKodeCipherSuite, PasswordFile};
|
||||
use crate::shared::opaque::{NKodeCipherSuite, PasswordFile};
|
||||
|
||||
pub struct RegCache {
|
||||
pub session_id: Uuid,
|
||||
@@ -16,7 +16,7 @@ pub struct LoginCache {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait CredKind {
|
||||
pub trait CredKind: Send + Sync {
|
||||
async fn has<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> bool;
|
||||
async fn get_password_file<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> Result<PasswordFile, AuthRepoError>;
|
||||
async fn set_password_file<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError>;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use async_trait::async_trait;
|
||||
use uuid::Uuid;
|
||||
use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession};
|
||||
use crate::shared::models::email::Email;
|
||||
use crate::shared::email::Email;
|
||||
|
||||
#[async_trait]
|
||||
pub trait UserAuthStore {
|
||||
pub trait UserRepo: Send + Sync {
|
||||
async fn get_key_session(&self, session_id: &Uuid) -> Result<KeyLoggedInSession, String>;
|
||||
async fn get_code_session(&self, session_id: &Uuid) -> Result<CodeLoggedInSession, String>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::shared::models::opaque::PasswordFile;
|
||||
use crate::shared::opaque::PasswordFile;
|
||||
use crate::server::repository::opaque_repo::{OpaqueDatabaseRepo, AuthRepoError};
|
||||
use tokio::sync::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -5,7 +5,7 @@ use opaque_ke::ServerLogin;
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
use crate::server::models::{LoginCache, RegCache};
|
||||
use crate::shared::models::opaque::NKodeCipherSuite;
|
||||
use crate::shared::opaque::NKodeCipherSuite;
|
||||
use crate::server::repository::opaque_repo::OpaqueSessionRepo;
|
||||
|
||||
#[derive(Default)]
|
||||
|
||||
@@ -2,7 +2,7 @@ use async_trait::async_trait;
|
||||
use std::marker::PhantomData;
|
||||
use uuid::Uuid;
|
||||
use opaque_ke::{CredentialFinalization, CredentialRequest, RegistrationRequest};
|
||||
use crate::shared::models::opaque::{NKodeCipherSuite, OpaqueLoginSession, OpaqueRegisterSession, PasswordFile};
|
||||
use crate::shared::opaque::{NKodeCipherSuite, OpaqueLoginSession, OpaqueRegisterSession, PasswordFile};
|
||||
use crate::client::opaque::{ClientAuthError, ServerConnectionLogin, ServerConnectionRegister};
|
||||
use crate::server::app::{Code, Key};
|
||||
use crate::server::app::ServerApp;
|
||||
@@ -12,15 +12,15 @@ use crate::server::repository::in_memory::in_memory_opaque_session::InMemoryOpaq
|
||||
use crate::server::repository::in_memory::in_memory_user_db::InMemoryUserDB;
|
||||
use crate::shared::models::app::LoggedInSession;
|
||||
|
||||
pub type InMemoryKeyServer<'a> = InMemoryServer<'a, Key>;
|
||||
pub type InMemoryCodeServer<'a> = InMemoryServer<'a, Code>;
|
||||
pub type InMemoryKeyServer<'a> = InMemoryServerTransport<'a, Key>;
|
||||
pub type InMemoryCodeServer<'a> = InMemoryServerTransport<'a, Code>;
|
||||
|
||||
pub struct InMemoryServer<'a, K: CredKind> {
|
||||
pub struct InMemoryServerTransport<'a, K: CredKind> {
|
||||
auth_db: &'a ServerApp<InMemoryOpaqueDB, InMemoryOpaqueSession, InMemoryUserDB>,
|
||||
_kind: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<'a, K: CredKind> InMemoryServer<'a, K> {
|
||||
impl<'a, K: CredKind> InMemoryServerTransport<'a, K> {
|
||||
pub fn new(server_app: &'a ServerApp<InMemoryOpaqueDB, InMemoryOpaqueSession, InMemoryUserDB>) -> Self {
|
||||
Self {
|
||||
auth_db: server_app,
|
||||
@@ -30,17 +30,15 @@ impl<'a, K: CredKind> InMemoryServer<'a, K> {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, K> ServerConnectionRegister for InMemoryServer<'a, K>
|
||||
impl<'a, K> ServerConnectionRegister for InMemoryServerTransport<'a, K>
|
||||
where
|
||||
K: CredKind + Sync,
|
||||
K: CredKind,
|
||||
{
|
||||
async fn start(
|
||||
&self,
|
||||
identifier: &[u8],
|
||||
message: &RegistrationRequest<NKodeCipherSuite>,
|
||||
) -> 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_db
|
||||
.reg_start::<K>(identifier, message.clone())
|
||||
.await
|
||||
@@ -59,7 +57,7 @@ where
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, K> ServerConnectionLogin for InMemoryServer<'a,K>
|
||||
impl<'a, K> ServerConnectionLogin for InMemoryServerTransport<'a,K>
|
||||
where
|
||||
K: CredKind + Send + Sync,
|
||||
{
|
||||
|
||||
@@ -3,9 +3,9 @@ use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
use crate::shared::models::store::UserAuthStore;
|
||||
use crate::server::repository::UserRepo::UserRepo;
|
||||
use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession};
|
||||
use crate::shared::models::email::Email;
|
||||
use crate::shared::email::Email;
|
||||
|
||||
pub struct InMemoryUserDB {
|
||||
key_session: Arc<Mutex<HashMap<Uuid, KeyLoggedInSession>>>,
|
||||
@@ -30,7 +30,7 @@ impl Default for InMemoryUserDB {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UserAuthStore for InMemoryUserDB {
|
||||
impl UserRepo for InMemoryUserDB {
|
||||
async fn get_key_session(&self, session_id: &Uuid) -> Result<KeyLoggedInSession, String> {
|
||||
self.key_session.lock().await
|
||||
.get(&session_id)
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod in_memory;
|
||||
pub mod opaque_repo;
|
||||
pub mod UserRepo;
|
||||
|
||||
@@ -2,7 +2,7 @@ use async_trait::async_trait;
|
||||
use uuid::Uuid;
|
||||
use opaque_ke::ServerLogin;
|
||||
use crate::server::models::{LoginCache, RegCache};
|
||||
use crate::shared::models::opaque::{NKodeCipherSuite, PasswordFile};
|
||||
use crate::shared::opaque::{NKodeCipherSuite, PasswordFile};
|
||||
#[derive(Debug)]
|
||||
pub enum AuthRepoError {
|
||||
UserExists,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::fmt;
|
||||
use email_address::{EmailAddress, Options};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Email(String);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -1 +1,5 @@
|
||||
pub mod models;
|
||||
pub mod email;
|
||||
pub mod opaque;
|
||||
pub mod signed_session_data;
|
||||
pub mod user_api;
|
||||
@@ -5,8 +5,8 @@ use getset::Getters;
|
||||
use nkode_rs::from_bytes::FromBytes;
|
||||
use nkode_rs::nkode_core::policy::NKodePolicy;
|
||||
use uuid::Uuid;
|
||||
use crate::shared::models::email::Email;
|
||||
use crate::shared::models::opaque::{OpaqueSessionKey, UserSecretKey};
|
||||
use crate::shared::email::Email;
|
||||
use crate::shared::opaque::{OpaqueSessionKey, UserSecretKey};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoggedInSession {
|
||||
@@ -60,11 +60,11 @@ pub struct CodeLoginData {
|
||||
#[async_trait]
|
||||
pub trait AuthAPI {
|
||||
async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String>;
|
||||
async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, data: &CodeLoginData) -> Result<(), String>;
|
||||
async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, data: CodeLoginData) -> Result<(), String>;
|
||||
async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoggedInSession, String>;
|
||||
async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result<CodeLoggedInSession, String>;
|
||||
|
||||
async fn get_new_icons(&self, key_login_session: &KeyLoggedInSession) -> Result<Vec<Icon>, String>;
|
||||
async fn get_new_icons(&self) -> Result<Vec<Icon>, String>;
|
||||
async fn get_login_data(&self, key_login_session: &KeyLoggedInSession) -> Result<CodeLoginData, String>;
|
||||
async fn is_code_registered(&self, key_login_session: &KeyLoggedInSession) -> Result<bool, String>;
|
||||
async fn get_policy(&self) -> Result<NKodePolicy, String>;
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
pub mod opaque;
|
||||
pub mod app;
|
||||
pub mod email;
|
||||
pub mod store;
|
||||
@@ -52,6 +52,12 @@ impl FromBytes<OPAQUE_SESSION_KEY_SIZE> for OpaqueSessionKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl OpaqueSessionKey {
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.0.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NKodeCipherSuite;
|
||||
|
||||
impl CipherSuite for NKodeCipherSuite {
|
||||
110
src/shared/signed_session_data.rs
Normal file
110
src/shared/signed_session_data.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use uuid::Uuid;
|
||||
|
||||
use hmac::{Hmac, Mac};
|
||||
use hkdf::Hkdf;
|
||||
use sha2::Sha256;
|
||||
|
||||
use crate::shared::opaque::OpaqueSessionKey;
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
pub const SIGNATURE_SIZE: usize = 32;
|
||||
|
||||
/// Domain separation + versioning so you can safely change formats later.
|
||||
const SIGNING_DOMAIN: &[u8] = b"nkode-protocol:signed-session-data:v1";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Signature(pub [u8; SIGNATURE_SIZE]);
|
||||
|
||||
// TODO: This there's should be two signature types
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(bound(
|
||||
serialize = "T: Serialize",
|
||||
deserialize = "T: DeserializeOwned",
|
||||
))]
|
||||
pub struct SignedSessionData<T> {
|
||||
pub session_id: Uuid,
|
||||
pub data: T,
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SignedSessionError {
|
||||
#[error("failed to serialize data for signing: {0}")]
|
||||
Serialize(#[from] postcard::Error),
|
||||
#[error("invalid signature")]
|
||||
InvalidSignature,
|
||||
}
|
||||
|
||||
impl<T> SignedSessionData<T>
|
||||
where
|
||||
T: Serialize + DeserializeOwned,
|
||||
{
|
||||
/// Create a signed envelope around (session_id, issued/expires, data).
|
||||
pub fn new(
|
||||
session_id: Uuid,
|
||||
data: T,
|
||||
session_key: &OpaqueSessionKey,
|
||||
) -> Result<Self, SignedSessionError> {
|
||||
let signature = Self::compute_signature(session_id, &data, session_key)?;
|
||||
Ok(Self {
|
||||
session_id,
|
||||
// issued_at_unix,
|
||||
// expires_at_unix,
|
||||
data,
|
||||
signature,
|
||||
})
|
||||
}
|
||||
|
||||
/// Verify signature + time bounds.
|
||||
pub fn verify(&self, session_key: &OpaqueSessionKey) -> Result<(), SignedSessionError> {
|
||||
let expected = Self::compute_signature(
|
||||
self.session_id,
|
||||
&self.data,
|
||||
session_key,
|
||||
)?;
|
||||
if !constant_time_eq(&self.signature.0, &expected.0) {
|
||||
return Err(SignedSessionError::InvalidSignature);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_signature(
|
||||
session_id: Uuid,
|
||||
data: &T,
|
||||
session_key: &OpaqueSessionKey,
|
||||
) -> Result<Signature, SignedSessionError> {
|
||||
let auth_key = derive_auth_key(session_key.as_bytes());
|
||||
let mut msg = Vec::with_capacity(128);
|
||||
msg.extend_from_slice(SIGNING_DOMAIN);
|
||||
msg.extend_from_slice(session_id.as_bytes());
|
||||
let data_bytes = postcard::to_stdvec(data)?;
|
||||
msg.extend_from_slice(&data_bytes);
|
||||
let mut mac = HmacSha256::new_from_slice(&auth_key).expect("HMAC can take any key size");
|
||||
mac.update(&msg);
|
||||
let tag = mac.finalize().into_bytes();
|
||||
let mut out = [0u8; SIGNATURE_SIZE];
|
||||
out.copy_from_slice(&tag[..SIGNATURE_SIZE]);
|
||||
Ok(Signature(out))
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_auth_key(session_key: &[u8]) -> [u8; 32] {
|
||||
let hk = Hkdf::<Sha256>::new(None, session_key);
|
||||
let mut out = [0u8; 32];
|
||||
hk.expand(b"myapp:hkdf:api-auth-key:v1", &mut out)
|
||||
.expect("HKDF expand");
|
||||
out
|
||||
}
|
||||
|
||||
fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
|
||||
if a.len() != b.len() {
|
||||
return false;
|
||||
}
|
||||
let mut r = 0u8;
|
||||
for i in 0..a.len() {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
r == 0
|
||||
}
|
||||
13
src/shared/user_api.rs
Normal file
13
src/shared/user_api.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use async_trait::async_trait;
|
||||
use crate::shared::email::Email;
|
||||
use crate::shared::models::app::{CodeLoginData, Icon};
|
||||
use crate::shared::signed_session_data::SignedSessionData;
|
||||
|
||||
#[async_trait]
|
||||
pub trait UserAPI: Sync + Send {
|
||||
// TODO: this should have a session
|
||||
async fn get_new_icons(&self) -> Result<Vec<Icon>, String>;
|
||||
async fn get_login_data(&self, session: SignedSessionData<Email>) -> Result<CodeLoginData, String>;
|
||||
async fn is_code_registered(&self, session: SignedSessionData<Email>) -> Result<bool, String>;
|
||||
async fn set_code_login_data(&self, session: SignedSessionData<CodeLoginData>) -> Result<(), String>;
|
||||
}
|
||||
@@ -3,8 +3,8 @@ use nkode_protocol::client::opaque::{OpaqueAuthData, ClientAuthError, OpaqueAuth
|
||||
use nkode_protocol::server::app::{ServerApp};
|
||||
use nkode_protocol::server::repository::in_memory::in_memory_opaque_db::InMemoryOpaqueDB;
|
||||
use nkode_protocol::server::repository::in_memory::in_memory_opaque_session::InMemoryOpaqueSession;
|
||||
use nkode_protocol::shared::models::opaque::NKodeServerSetup;
|
||||
use nkode_protocol::server::repository::in_memory::in_memory_transport::{InMemoryCodeServer, InMemoryKeyServer, InMemoryServer};
|
||||
use nkode_protocol::shared::opaque::NKodeServerSetup;
|
||||
use nkode_protocol::server::repository::in_memory::in_memory_transport::{InMemoryCodeServer, InMemoryKeyServer, InMemoryServerTransport};
|
||||
use nkode_protocol::server::repository::in_memory::in_memory_user_db::InMemoryUserDB;
|
||||
|
||||
#[tokio::test]
|
||||
@@ -17,7 +17,7 @@ async fn opaque_key_registration_and_login_roundtrip() {
|
||||
InMemoryOpaqueSession::new(),
|
||||
InMemoryUserDB::new()
|
||||
);
|
||||
let key_server: InMemoryKeyServer = InMemoryServer::new(&server);
|
||||
let key_server: InMemoryKeyServer = InMemoryServerTransport::new(&server);
|
||||
let auth = OpaqueAuth::new(&key_server);
|
||||
let auth_data = OpaqueAuthData::from_secret_key("a@b.com", b"supersecret16bytes");
|
||||
auth.register(&auth_data).await.expect("registration should succeed");
|
||||
@@ -58,7 +58,7 @@ async fn cannot_register_code_before_key() {
|
||||
InMemoryOpaqueSession::new(),
|
||||
InMemoryUserDB::new()
|
||||
);
|
||||
let key_server: InMemoryCodeServer = InMemoryServer::new(&server);
|
||||
let key_server: InMemoryCodeServer = InMemoryServerTransport::new(&server);
|
||||
let auth = OpaqueAuth::new(&key_server);
|
||||
let auth_data = OpaqueAuthData::from_code("x@y.com", &[1u64,2,3,4]);
|
||||
let err = auth.register(&auth_data)
|
||||
|
||||
Reference in New Issue
Block a user