implement signed session data
This commit is contained in:
160
Cargo.lock
generated
160
Cargo.lock
generated
@@ -43,6 +43,15 @@ dependencies = [
|
|||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "base16ct"
|
name = "base16ct"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -74,6 +83,26 @@ dependencies = [
|
|||||||
"zeroize",
|
"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]]
|
[[package]]
|
||||||
name = "blake2"
|
name = "blake2"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -153,6 +182,15 @@ dependencies = [
|
|||||||
"inout",
|
"inout",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cobs"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
@@ -174,6 +212,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "critical-section"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-bigint"
|
name = "crypto-bigint"
|
||||||
version = "0.5.5"
|
version = "0.5.5"
|
||||||
@@ -332,6 +376,18 @@ dependencies = [
|
|||||||
"serde",
|
"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]]
|
[[package]]
|
||||||
name = "ff"
|
name = "ff"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
@@ -414,6 +470,29 @@ dependencies = [
|
|||||||
"subtle",
|
"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]]
|
[[package]]
|
||||||
name = "hkdf"
|
name = "hkdf"
|
||||||
version = "0.12.4"
|
version = "0.12.4"
|
||||||
@@ -457,20 +536,34 @@ version = "0.2.178"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
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]]
|
[[package]]
|
||||||
name = "nkode-protocol"
|
name = "nkode-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"bincode",
|
||||||
"blake3",
|
"blake3",
|
||||||
"email_address",
|
"email_address",
|
||||||
"getset",
|
"getset",
|
||||||
|
"hkdf",
|
||||||
|
"hmac",
|
||||||
"nkode-rs",
|
"nkode-rs",
|
||||||
"opaque-ke",
|
"opaque-ke",
|
||||||
|
"postcard",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid",
|
"uuid",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@@ -548,6 +641,19 @@ dependencies = [
|
|||||||
"spki",
|
"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]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
@@ -677,6 +783,12 @@ version = "1.0.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sec1"
|
name = "sec1"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
@@ -765,6 +877,15 @@ dependencies = [
|
|||||||
"rand_core 0.6.4",
|
"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]]
|
[[package]]
|
||||||
name = "spki"
|
name = "spki"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
@@ -775,6 +896,12 @@ dependencies = [
|
|||||||
"der",
|
"der",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
@@ -792,6 +919,26 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"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]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.48.0"
|
version = "1.48.0"
|
||||||
@@ -825,6 +972,12 @@ version = "1.0.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unty"
|
||||||
|
version = "0.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
@@ -833,6 +986,7 @@ checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"serde_core",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -842,6 +996,12 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtue"
|
||||||
|
version = "0.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "voprf"
|
name = "voprf"
|
||||||
version = "0.5.0"
|
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"] }
|
rand = { version = "0.8.5", features = ["std"] }
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
async-trait = "0.1.89"
|
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"] }
|
tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread", "sync"] }
|
||||||
nkode-rs = { path = "nkode-rs" }
|
nkode-rs = { path = "nkode-rs" }
|
||||||
zeroize = "1.8.2"
|
zeroize = "1.8.2"
|
||||||
@@ -17,5 +17,10 @@ email_address = "0.2.9"
|
|||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
getset = "0.1.6"
|
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 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::app::AuthAPI;
|
||||||
use crate::shared::models::email::Email;
|
use crate::shared::email::Email;
|
||||||
use crate::shared::models::opaque::UserSecretKey;
|
use crate::shared::opaque::UserSecretKey;
|
||||||
|
use crate::shared::user_api::UserAPI;
|
||||||
//https://chatgpt.com/c/69441737-c990-8333-9737-7ac75232da1d
|
//https://chatgpt.com/c/69441737-c990-8333-9737-7ac75232da1d
|
||||||
|
|
||||||
struct ClientApp<K, C>
|
struct ClientApp<'a, R, U, C>
|
||||||
where
|
where
|
||||||
K: AuthAPI,
|
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||||
|
U: UserAPI,
|
||||||
C: ClientRepo
|
C: ClientRepo
|
||||||
{
|
{
|
||||||
auth_api: K,
|
auth_api: ClientAuth<'a, R, U>,
|
||||||
client_repo: C,
|
client_repo: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <K, C>ClientApp<K, C>
|
impl <'a, R, U, C>ClientApp<'a, R, U, C>
|
||||||
where
|
where
|
||||||
K: AuthAPI,
|
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||||
|
U: UserAPI,
|
||||||
C: ClientRepo
|
C: ClientRepo
|
||||||
{
|
{
|
||||||
fn new(auth_api: K, client_repo: C) -> Self {
|
fn new(auth_api: ClientAuth<'a, R, U>, client_repo: C) -> Self {
|
||||||
Self {
|
Self {
|
||||||
auth_api,
|
auth_api,
|
||||||
client_repo
|
client_repo
|
||||||
|
|||||||
@@ -1,38 +1,45 @@
|
|||||||
use crate::shared::models::app::{AuthAPI, CodeLoggedInSession, CodeLoginData, Icon, KeyLoggedInSession};
|
use crate::shared::models::app::{AuthAPI, CodeLoggedInSession, CodeLoginData, Icon, KeyLoggedInSession};
|
||||||
use crate::shared::models::email::Email;
|
use crate::shared::email::Email;
|
||||||
use crate::shared::models::opaque::UserSecretKey;
|
use crate::shared::opaque::UserSecretKey;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use nkode_rs::nkode_core::policy::{NKodePolicy, DEFAULT_POLICY};
|
use nkode_rs::nkode_core::policy::{NKodePolicy, DEFAULT_POLICY};
|
||||||
use crate::client::opaque::{OpaqueAuthData, OpaqueAuth, ServerConnectionLogin, ServerConnectionRegister};
|
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>
|
pub struct ClientAuth<'a, R, U>
|
||||||
where
|
where
|
||||||
R: ServerConnectionRegister + ServerConnectionLogin + Clone,
|
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||||
U: UserAuthStore
|
U: UserAPI
|
||||||
{
|
{
|
||||||
opaque_key_register: OpaqueAuth<'a, R>,
|
opaque_key_register: OpaqueAuth<'a, R>,
|
||||||
opaque_key_login: OpaqueAuth<'a, R>,
|
opaque_key_login: OpaqueAuth<'a, R>,
|
||||||
opaque_code_register: OpaqueAuth<'a, R>,
|
opaque_code_register: OpaqueAuth<'a, R>,
|
||||||
opaque_code_login: OpaqueAuth<'a, R>,
|
opaque_code_login: OpaqueAuth<'a, R>,
|
||||||
user_store: U
|
user_api: U
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<'a, R, U> AuthAPI for ClientAuth<'a, R, U>
|
impl<'a, R, U> AuthAPI for ClientAuth<'a, R, U>
|
||||||
where
|
where
|
||||||
R: ServerConnectionRegister + ServerConnectionLogin + Clone + Sync + Send,
|
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||||
U: UserAuthStore + Sync + Send,
|
U: UserAPI,
|
||||||
{
|
{
|
||||||
async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String> {
|
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());
|
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))
|
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);
|
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> {
|
async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoggedInSession, String> {
|
||||||
@@ -49,28 +56,29 @@ where
|
|||||||
|
|
||||||
async fn get_new_icons(
|
async fn get_new_icons(
|
||||||
&self,
|
&self,
|
||||||
key_login_session: &KeyLoggedInSession,
|
|
||||||
) -> Result<Vec<Icon>, String> {
|
) -> Result<Vec<Icon>, String> {
|
||||||
// self.nkode_api
|
self.user_api.get_new_icons().await
|
||||||
// .get_new_icons(key_login_session)
|
|
||||||
// .await
|
|
||||||
//
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_login_data(
|
async fn get_login_data(
|
||||||
&self,
|
&self,
|
||||||
key_login_session: &KeyLoggedInSession,
|
key_login_session: &KeyLoggedInSession,
|
||||||
) -> Result<CodeLoginData, String> {
|
) -> Result<CodeLoginData, String> {
|
||||||
// self.nkode_api
|
let session = SignedSessionData::new(
|
||||||
// .get_login_data(key_login_session)
|
key_login_session.0.session_id,
|
||||||
// .await
|
key_login_session.0.email.clone(),
|
||||||
todo!()
|
&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> {
|
async fn is_code_registered(&self, key_login_session: &KeyLoggedInSession) -> Result<bool, String> {
|
||||||
// self.nkode_api.is_code_registered(key_login_session).await
|
let session = SignedSessionData::new(
|
||||||
todo!()
|
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> {
|
async fn get_policy(&self) -> Result<NKodePolicy, String> {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use opaque_ke::{
|
|||||||
RegistrationRequest,
|
RegistrationRequest,
|
||||||
};
|
};
|
||||||
use crate::shared::models::app::LoggedInSession;
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum ClientAuthError {
|
pub enum ClientAuthError {
|
||||||
@@ -55,7 +55,7 @@ impl OpaqueAuthData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait ServerConnectionRegister {
|
pub trait ServerConnectionRegister: Send + Sync {
|
||||||
async fn start(
|
async fn start(
|
||||||
&self,
|
&self,
|
||||||
identifier: &[u8],
|
identifier: &[u8],
|
||||||
@@ -70,7 +70,7 @@ pub trait ServerConnectionRegister {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait ServerConnectionLogin {
|
pub trait ServerConnectionLogin: Send + Sync {
|
||||||
async fn start(
|
async fn start(
|
||||||
&self,
|
&self,
|
||||||
identifier: &[u8],
|
identifier: &[u8],
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use nkode_rs::nkode_core::keypad::Keypad;
|
|||||||
use nkode_rs::nkode_core::nkode_cipher::NKodeCipher;
|
use nkode_rs::nkode_core::nkode_cipher::NKodeCipher;
|
||||||
use nkode_rs::from_bytes::FromBytes;
|
use nkode_rs::from_bytes::FromBytes;
|
||||||
use crate::shared::models::app::{AuthAPI, CodeLoggedInSession, CodeLoginData, Icon, IconID, KeyLoggedInSession, ICON_ID_SIZE};
|
use crate::shared::models::app::{AuthAPI, CodeLoggedInSession, CodeLoginData, Icon, IconID, KeyLoggedInSession, ICON_ID_SIZE};
|
||||||
use crate::shared::models::email::Email;
|
use crate::shared::email::Email;
|
||||||
use crate::shared::models::opaque::UserSecretKey;
|
use crate::shared::opaque::UserSecretKey;
|
||||||
|
|
||||||
struct KeyLogin;
|
struct KeyLogin;
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ impl <S: AuthAPI> UserStateKeyLoggedIn<S> {
|
|||||||
async fn get_icons(&self, icon_nonce: &Nonce) -> Result<Vec<Icon>, String> {
|
async fn get_icons(&self, icon_nonce: &Nonce) -> Result<Vec<Icon>, String> {
|
||||||
let chacha20_key = self.user_secret_key.chacha20_secret_key();
|
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 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 {
|
for icon in &mut icons {
|
||||||
let bytes = chacha_cipher.read_u8(ICON_ID_SIZE)?;
|
let bytes = chacha_cipher.read_u8(ICON_ID_SIZE)?;
|
||||||
let new_id = IconID::from_bytes(bytes.as_slice()).unwrap();
|
let new_id = IconID::from_bytes(bytes.as_slice()).unwrap();
|
||||||
@@ -123,7 +123,7 @@ impl <S: AuthAPI> UserStateCodeRegister<S> {
|
|||||||
icon_nonce: self.icon_nonce,
|
icon_nonce: self.icon_nonce,
|
||||||
keypad,
|
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 {
|
Ok(UserStateCodeLogin {
|
||||||
api: self.api,
|
api: self.api,
|
||||||
mask: data.mask,
|
mask: data.mask,
|
||||||
|
|||||||
@@ -5,20 +5,20 @@ use opaque_ke::argon2::password_hash::rand_core::OsRng;
|
|||||||
use nkode_rs::from_bytes::FromBytes;
|
use nkode_rs::from_bytes::FromBytes;
|
||||||
use crate::server::models::CredKind;
|
use crate::server::models::CredKind;
|
||||||
use crate::server::repository::opaque_repo::{AuthRepoError, OpaqueDatabaseRepo, OpaqueSessionRepo};
|
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::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession, LoggedInSession};
|
||||||
use crate::shared::models::email::Email;
|
use crate::shared::email::Email;
|
||||||
use crate::shared::models::opaque::{NKodeCipherSuite, NKodeServerSetup, OpaqueLoginSession, OpaqueRegisterSession, OpaqueSessionKey, PasswordFile};
|
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,
|
server_setup: NKodeServerSetup,
|
||||||
opaque_db: R,
|
opaque_db: R,
|
||||||
opaque_sess: S,
|
opaque_sess: S,
|
||||||
user_db: U,
|
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 {
|
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}
|
Self { server_setup, opaque_db, opaque_sess, user_db}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use async_trait::async_trait;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use opaque_ke::ServerLogin;
|
use opaque_ke::ServerLogin;
|
||||||
use crate::server::repository::opaque_repo::{AuthRepoError, OpaqueDatabaseRepo};
|
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 struct RegCache {
|
||||||
pub session_id: Uuid,
|
pub session_id: Uuid,
|
||||||
@@ -16,7 +16,7 @@ pub struct LoginCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait CredKind {
|
pub trait CredKind: Send + Sync {
|
||||||
async fn has<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8]) -> bool;
|
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 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>;
|
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 async_trait::async_trait;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession};
|
use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession};
|
||||||
use crate::shared::models::email::Email;
|
use crate::shared::email::Email;
|
||||||
|
|
||||||
#[async_trait]
|
#[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_key_session(&self, session_id: &Uuid) -> Result<KeyLoggedInSession, String>;
|
||||||
async fn get_code_session(&self, session_id: &Uuid) -> Result<CodeLoggedInSession, String>;
|
async fn get_code_session(&self, session_id: &Uuid) -> Result<CodeLoggedInSession, String>;
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::shared::models::opaque::PasswordFile;
|
use crate::shared::opaque::PasswordFile;
|
||||||
use crate::server::repository::opaque_repo::{OpaqueDatabaseRepo, AuthRepoError};
|
use crate::server::repository::opaque_repo::{OpaqueDatabaseRepo, AuthRepoError};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use opaque_ke::ServerLogin;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::server::models::{LoginCache, RegCache};
|
use crate::server::models::{LoginCache, RegCache};
|
||||||
use crate::shared::models::opaque::NKodeCipherSuite;
|
use crate::shared::opaque::NKodeCipherSuite;
|
||||||
use crate::server::repository::opaque_repo::OpaqueSessionRepo;
|
use crate::server::repository::opaque_repo::OpaqueSessionRepo;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use async_trait::async_trait;
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use opaque_ke::{CredentialFinalization, CredentialRequest, RegistrationRequest};
|
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::client::opaque::{ClientAuthError, ServerConnectionLogin, ServerConnectionRegister};
|
||||||
use crate::server::app::{Code, Key};
|
use crate::server::app::{Code, Key};
|
||||||
use crate::server::app::ServerApp;
|
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::server::repository::in_memory::in_memory_user_db::InMemoryUserDB;
|
||||||
use crate::shared::models::app::LoggedInSession;
|
use crate::shared::models::app::LoggedInSession;
|
||||||
|
|
||||||
pub type InMemoryKeyServer<'a> = InMemoryServer<'a, Key>;
|
pub type InMemoryKeyServer<'a> = InMemoryServerTransport<'a, Key>;
|
||||||
pub type InMemoryCodeServer<'a> = InMemoryServer<'a, Code>;
|
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>,
|
auth_db: &'a ServerApp<InMemoryOpaqueDB, InMemoryOpaqueSession, InMemoryUserDB>,
|
||||||
_kind: PhantomData<K>,
|
_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 {
|
pub fn new(server_app: &'a ServerApp<InMemoryOpaqueDB, InMemoryOpaqueSession, InMemoryUserDB>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
auth_db: server_app,
|
auth_db: server_app,
|
||||||
@@ -30,17 +30,15 @@ impl<'a, K: CredKind> InMemoryServer<'a, K> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<'a, K> ServerConnectionRegister for InMemoryServer<'a, K>
|
impl<'a, K> ServerConnectionRegister for InMemoryServerTransport<'a, K>
|
||||||
where
|
where
|
||||||
K: CredKind + Sync,
|
K: CredKind,
|
||||||
{
|
{
|
||||||
async fn start(
|
async fn start(
|
||||||
&self,
|
&self,
|
||||||
identifier: &[u8],
|
identifier: &[u8],
|
||||||
message: &RegistrationRequest<NKodeCipherSuite>,
|
message: &RegistrationRequest<NKodeCipherSuite>,
|
||||||
) -> Result<OpaqueRegisterSession, 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_db
|
self.auth_db
|
||||||
.reg_start::<K>(identifier, message.clone())
|
.reg_start::<K>(identifier, message.clone())
|
||||||
.await
|
.await
|
||||||
@@ -59,7 +57,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<'a, K> ServerConnectionLogin for InMemoryServer<'a,K>
|
impl<'a, K> ServerConnectionLogin for InMemoryServerTransport<'a,K>
|
||||||
where
|
where
|
||||||
K: CredKind + Send + Sync,
|
K: CredKind + Send + Sync,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ use std::sync::Arc;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use uuid::Uuid;
|
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::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession};
|
||||||
use crate::shared::models::email::Email;
|
use crate::shared::email::Email;
|
||||||
|
|
||||||
pub struct InMemoryUserDB {
|
pub struct InMemoryUserDB {
|
||||||
key_session: Arc<Mutex<HashMap<Uuid, KeyLoggedInSession>>>,
|
key_session: Arc<Mutex<HashMap<Uuid, KeyLoggedInSession>>>,
|
||||||
@@ -30,7 +30,7 @@ impl Default for InMemoryUserDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl UserAuthStore for InMemoryUserDB {
|
impl UserRepo for InMemoryUserDB {
|
||||||
async fn get_key_session(&self, session_id: &Uuid) -> Result<KeyLoggedInSession, String> {
|
async fn get_key_session(&self, session_id: &Uuid) -> Result<KeyLoggedInSession, String> {
|
||||||
self.key_session.lock().await
|
self.key_session.lock().await
|
||||||
.get(&session_id)
|
.get(&session_id)
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod in_memory;
|
pub mod in_memory;
|
||||||
pub mod opaque_repo;
|
pub mod opaque_repo;
|
||||||
|
pub mod UserRepo;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use async_trait::async_trait;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use opaque_ke::ServerLogin;
|
use opaque_ke::ServerLogin;
|
||||||
use crate::server::models::{LoginCache, RegCache};
|
use crate::server::models::{LoginCache, RegCache};
|
||||||
use crate::shared::models::opaque::{NKodeCipherSuite, PasswordFile};
|
use crate::shared::opaque::{NKodeCipherSuite, PasswordFile};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AuthRepoError {
|
pub enum AuthRepoError {
|
||||||
UserExists,
|
UserExists,
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use email_address::{EmailAddress, Options};
|
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);
|
pub struct Email(String);
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -1 +1,5 @@
|
|||||||
pub mod models;
|
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::from_bytes::FromBytes;
|
||||||
use nkode_rs::nkode_core::policy::NKodePolicy;
|
use nkode_rs::nkode_core::policy::NKodePolicy;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::shared::models::email::Email;
|
use crate::shared::email::Email;
|
||||||
use crate::shared::models::opaque::{OpaqueSessionKey, UserSecretKey};
|
use crate::shared::opaque::{OpaqueSessionKey, UserSecretKey};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LoggedInSession {
|
pub struct LoggedInSession {
|
||||||
@@ -60,11 +60,11 @@ pub struct CodeLoginData {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait AuthAPI {
|
pub trait AuthAPI {
|
||||||
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: &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_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoggedInSession, String>;
|
||||||
async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result<CodeLoggedInSession, 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 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 is_code_registered(&self, key_login_session: &KeyLoggedInSession) -> Result<bool, String>;
|
||||||
async fn get_policy(&self) -> Result<NKodePolicy, String>;
|
async fn get_policy(&self) -> Result<NKodePolicy, String>;
|
||||||
|
|||||||
@@ -1,4 +1 @@
|
|||||||
pub mod opaque;
|
|
||||||
pub mod app;
|
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;
|
pub struct NKodeCipherSuite;
|
||||||
|
|
||||||
impl CipherSuite for 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::app::{ServerApp};
|
||||||
use nkode_protocol::server::repository::in_memory::in_memory_opaque_db::InMemoryOpaqueDB;
|
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::server::repository::in_memory::in_memory_opaque_session::InMemoryOpaqueSession;
|
||||||
use nkode_protocol::shared::models::opaque::NKodeServerSetup;
|
use nkode_protocol::shared::opaque::NKodeServerSetup;
|
||||||
use nkode_protocol::server::repository::in_memory::in_memory_transport::{InMemoryCodeServer, InMemoryKeyServer, InMemoryServer};
|
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;
|
use nkode_protocol::server::repository::in_memory::in_memory_user_db::InMemoryUserDB;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -17,7 +17,7 @@ async fn opaque_key_registration_and_login_roundtrip() {
|
|||||||
InMemoryOpaqueSession::new(),
|
InMemoryOpaqueSession::new(),
|
||||||
InMemoryUserDB::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 = OpaqueAuth::new(&key_server);
|
||||||
let auth_data = OpaqueAuthData::from_secret_key("a@b.com", b"supersecret16bytes");
|
let auth_data = OpaqueAuthData::from_secret_key("a@b.com", b"supersecret16bytes");
|
||||||
auth.register(&auth_data).await.expect("registration should succeed");
|
auth.register(&auth_data).await.expect("registration should succeed");
|
||||||
@@ -58,7 +58,7 @@ async fn cannot_register_code_before_key() {
|
|||||||
InMemoryOpaqueSession::new(),
|
InMemoryOpaqueSession::new(),
|
||||||
InMemoryUserDB::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 = OpaqueAuth::new(&key_server);
|
||||||
let auth_data = OpaqueAuthData::from_code("x@y.com", &[1u64,2,3,4]);
|
let auth_data = OpaqueAuthData::from_code("x@y.com", &[1u64,2,3,4]);
|
||||||
let err = auth.register(&auth_data)
|
let err = auth.register(&auth_data)
|
||||||
|
|||||||
Reference in New Issue
Block a user