outline opaque auth

This commit is contained in:
2025-12-12 21:27:28 -06:00
parent 3865508117
commit 3af1128c70
6 changed files with 258 additions and 2 deletions

92
src/client.rs Normal file
View File

@@ -0,0 +1,92 @@
use opaque_ke::argon2::password_hash::rand_core::OsRng;
use opaque_ke::{
CredentialFinalization, CredentialRequest, RegistrationRequest,
ClientLogin, ClientLoginFinishParameters, ClientRegistration,
ClientRegistrationFinishParameters
};
use uuid::Uuid;
use crate::protocol::{PasswordFile, KeyLoginPhaseISession, KeyRegisterSession, NKodeCipherSuite};
trait ServerConnectionRegister {
async fn start(
&mut self,
identifier: &[u8],
message: &RegistrationRequest<NKodeCipherSuite>
) -> Result<KeyRegisterSession, String>;
async fn finish(
&mut self,
session_id: &Uuid,
password_file: PasswordFile,
) -> Result<(), String>;
}
trait ServerConnectionLogin {
async fn start(
&mut self,
identifier: &[u8],
request_bytes: &CredentialRequest<NKodeCipherSuite>
) -> Result<KeyLoginPhaseISession, String>;
async fn finish(
&mut self,
session_id: &Uuid,
message: &CredentialFinalization<NKodeCipherSuite>
) -> Result<(), String>;
}
struct AuthenticationData {
identifier: Vec<u8>,
secret: Vec<u8>,
}
impl AuthenticationData {
fn from_secret_key(email: &String, secret_key: &[u8]) -> Self {
Self {
identifier: email.as_bytes().to_vec(),
secret: secret_key.to_vec(),
}
}
fn from_code(email: &String, code: &[usize]) -> Self {
let mut secret = Vec::with_capacity(code.len() * 8);
for &n in code {
// fixed-width so it's stable across 32-bit vs 64-bit platforms
secret.extend_from_slice(&(n as u64).to_le_bytes());
}
Self {
identifier: email.as_bytes().to_vec(),
secret,
}
}
}
struct OpaqueAuthentication;
impl OpaqueAuthentication {
async fn register(
key_data: &AuthenticationData,
server: &mut impl ServerConnectionRegister
) -> Result<(), String>
{
let mut client_rng = OsRng;
let client_reg_start = ClientRegistration::<NKodeCipherSuite>::start(&mut client_rng, &key_data.secret).expect("error starting registration");
let server_response = server.start(&key_data.identifier, &client_reg_start.message).await.expect("error getting server response");
let client_finish = client_reg_start.state.finish(&mut client_rng, &key_data.secret, server_response.response().clone(), ClientRegistrationFinishParameters::default()).expect("");
server.finish(server_response.session_id(), client_finish.message.serialize()).await.expect("server to finish secret reg without error");
Ok(())
}
async fn login(
auth_data: &AuthenticationData,
server: &mut impl ServerConnectionLogin
) -> Result<Vec<u8>, String>
{
let mut client_rng = OsRng;
let client_start = ClientLogin::<NKodeCipherSuite>::start(&mut client_rng, &auth_data.secret).expect("client secret key login to start result");
let server_response = server.start(&auth_data.identifier, &client_start.message).await.expect("server secret key login start response");
let client_finish = client_start.state.finish(&mut client_rng, &auth_data.secret, server_response.response().clone(), ClientLoginFinishParameters::default()).expect("");
server.finish(server_response.session_id(), &client_finish.message).await.expect("server secret key login to finish");
Ok(client_finish.session_key.to_vec())
}
}

View File

@@ -1 +1,3 @@
mod protocol;
mod client;
mod server;

View File

@@ -8,10 +8,11 @@ use opaque_ke::generic_array::GenericArray;
use uuid::Uuid;
use opaque_ke::rand::rngs::OsRng;
use std::marker::PhantomData;
use getset::Getters;
pub const NONCE_SIZE: usize = 12;
pub const SECRET_KEY_SIZE: usize = 16;
pub const SESSION_KEY_SIZE: usize = 32;
pub const SECRET_KEY_SIZE: usize = 16;
pub struct NKodeCipherSuite;
@@ -23,12 +24,16 @@ impl CipherSuite for NKodeCipherSuite {
pub type NKodeServerSetup = ServerSetup<NKodeCipherSuite, PrivateKey<Ristretto255>, OprfSeed<Sha512>>;
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
pub struct RegisterSession {
#[get = "pub"]
response: RegistrationResponse<NKodeCipherSuite>,
#[get = "pub"]
session_id: Uuid
}
type PasswordFile = GenericArray<u8, RegistrationUploadLen<NKodeCipherSuite>>;
pub type PasswordFile = GenericArray<u8, RegistrationUploadLen<NKodeCipherSuite>>;
pub async fn register_secret_key<R,S>(
@@ -48,8 +53,11 @@ where
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
pub struct LoginSession {
#[get = "pub"]
response: CredentialResponse<NKodeCipherSuite>,
#[get = "pub"]
session_id: Uuid
}
@@ -66,6 +74,7 @@ where
server.finish(&server_response.session_id, client_finish.message).await.expect("server secret key login to finish");
Ok(client_finish.session_key.to_vec())
}
pub trait Repo {}
pub trait Sessions {}

117
src/server.rs Normal file
View File

@@ -0,0 +1,117 @@
use std::marker::PhantomData;
use opaque_ke::{CredentialFinalization, CredentialRequest, Identifiers, RegistrationRequest};
use crate::protocol::{KeyLoginPhaseISession, NKodeCipherSuite, PasswordFile};
use uuid::Uuid;
#[derive(Debug)]
enum AuthRepoError {
UserExists,
KeyNotRegistered,
CodeNotRegistered
}
trait AuthRepo {
fn new_key(email: String, password_file: PasswordFile) -> Result<(), AuthRepoError>;
fn new_code(email: String, password_file: PasswordFile) -> Result<(), AuthRepoError>;
fn has_code(email: String) -> bool;
fn has_key(email: String) -> bool;
fn get_key_passcode_file(email: String) -> Result<PasswordFile, AuthRepoError>;
fn get_code_passcode_file(email: String) -> Result<PasswordFile, AuthRepoError>;
}
struct RegSession;
struct LoginSession;
trait AuthSession {
fn new_reg_session(identifier: &[u8], request: &RegistrationRequest<NKodeCipherSuite>) -> Result<RegSession, String>;
fn get_reg_session(session_id: Uuid) -> Result<RegSession, String>;
fn new_login_session(identifier: &[u8], request: &CredentialRequest<NKodeCipherSuite>) -> Result<LoginSession, String>;
fn get_login_session(session_id: Uuid) -> Result<LoginSession, String>;
}
struct OpaqueAuth<State,R: AuthRepo, S: AuthSession> {
user_repo: R,
session: S,
_state: PhantomData<State>
}
struct KeyAuthRegistration;
impl<R: AuthRepo,S: AuthSession> OpaqueAuth<KeyAuthRegistration, R, S>
{
async fn start(
&mut self,
identifier: &[u8],
message: &RegistrationRequest<NKodeCipherSuite>
) -> Result<RegSession, String> {
todo!()
}
async fn finish(
&mut self,
session_id: &Uuid,
password_file: PasswordFile,
) -> Result<(), String> {
todo!()
}
}
struct KeyAuthLogin;
impl<R: AuthRepo,S: AuthSession> OpaqueAuth<KeyAuthLogin, R, S>
{
async fn start(
&mut self,
identifier: &[u8],
request_bytes: &CredentialRequest<NKodeCipherSuite>
) -> Result<LoginSession, String> {
todo!()
}
async fn finish(
&mut self,
session_id: &Uuid,
message: &CredentialFinalization<NKodeCipherSuite>
) -> Result<(), String> {
todo!()
}
}
struct CodeAuthRegistration;
impl<R: AuthRepo,S: AuthSession> OpaqueAuth<CodeAuthRegistration, R, S>
{
async fn start(
&mut self,
identifier: &[u8],
message: &RegistrationRequest<NKodeCipherSuite>
) -> Result<RegSession, String> {
todo!()
}
async fn finish(
&mut self,
session_id: &Uuid,
password_file: PasswordFile,
) -> Result<(), String> {
todo!()
}
}
struct CodeAuthLogin;
impl<R: AuthRepo,S: AuthSession> OpaqueAuth<CodeAuthLogin, R, S>
{
async fn start(
&mut self,
identifier: &[u8],
request_bytes: &CredentialRequest<NKodeCipherSuite>
) -> Result<LoginSession, String> {
todo!()
}
async fn finish(
&mut self,
session_id: &Uuid,
message: &CredentialFinalization<NKodeCipherSuite>
) -> Result<(), String> {
todo!()
}
}