refactor(server): migrated auth to ml-dsa

This commit is contained in:
hdbg
2026-04-07 11:43:21 +02:00
committed by Skipper
parent 13fb31f841
commit 4c2e00b56d
25 changed files with 457 additions and 414 deletions

View File

@@ -1,5 +1,5 @@
use arbiter_proto::{
ClientMetadata, format_challenge,
CLIENT_CONTEXT, ClientMetadata,
transport::{Bi, expect_message},
};
use chrono::Utc;
@@ -8,7 +8,6 @@ use diesel::{
dsl::insert_into, update,
};
use diesel_async::RunQueryDsl as _;
use ed25519_dalek::{Signature, VerifyingKey};
use kameo::{actor::ActorRef, error::SendError};
use tracing::error;
@@ -18,6 +17,7 @@ use crate::{
flow_coordinator::{self, RequestClientApproval},
keyholder::KeyHolder,
},
crypto::authn,
crypto::integrity::{self, AttestationStatus},
db::{
self,
@@ -26,6 +26,8 @@ use crate::{
},
};
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
pub enum Error {
#[error("Database pool unavailable")]
@@ -62,17 +64,20 @@ pub enum ApproveError {
#[derive(Debug, Clone)]
pub enum Inbound {
AuthChallengeRequest {
pubkey: VerifyingKey,
pubkey: authn::PublicKey,
metadata: ClientMetadata,
},
AuthChallengeSolution {
signature: Signature,
signature: authn::Signature,
},
}
#[derive(Debug, Clone)]
pub enum Outbound {
AuthChallenge { pubkey: VerifyingKey, nonce: i32 },
AuthChallenge {
pubkey: authn::PublicKey,
nonce: i32,
},
AuthSuccess,
}
@@ -80,9 +85,9 @@ pub enum Outbound {
/// Returns `None` if the pubkey is not registered.
async fn get_current_nonce_and_id(
db: &db::DatabasePool,
pubkey: &VerifyingKey,
pubkey: &authn::PublicKey,
) -> Result<Option<(i32, i32)>, Error> {
let pubkey_bytes = pubkey.as_bytes().to_vec();
let pubkey_bytes = pubkey.to_bytes();
let mut conn = db.get().await.map_err(|e| {
error!(error = ?e, "Database pool error");
Error::DatabasePoolUnavailable
@@ -102,19 +107,17 @@ async fn get_current_nonce_and_id(
async fn verify_integrity(
db: &db::DatabasePool,
keyholder: &ActorRef<KeyHolder>,
pubkey: &VerifyingKey,
pubkey: &authn::PublicKey,
) -> Result<(), Error> {
let mut db_conn = db.get().await.map_err(|e| {
error!(error = ?e, "Database pool error");
Error::DatabasePoolUnavailable
})?;
let (id, nonce) = get_current_nonce_and_id(db, pubkey)
.await?
.ok_or_else(|| {
error!("Client not found during integrity verification");
Error::DatabaseOperationFailed
})?;
let (id, nonce) = get_current_nonce_and_id(db, pubkey).await?.ok_or_else(|| {
error!("Client not found during integrity verification");
Error::DatabaseOperationFailed
})?;
let attestation = integrity::verify_entity(
&mut db_conn,
@@ -144,9 +147,9 @@ async fn verify_integrity(
async fn create_nonce(
db: &db::DatabasePool,
keyholder: &ActorRef<KeyHolder>,
pubkey: &VerifyingKey,
pubkey: &authn::PublicKey,
) -> Result<i32, Error> {
let pubkey_bytes = pubkey.as_bytes().to_vec();
let pubkey_bytes = pubkey.to_bytes();
let pubkey = pubkey.clone();
let mut conn = db.get().await.map_err(|e| {
@@ -212,7 +215,7 @@ async fn approve_new_client(
async fn insert_client(
db: &db::DatabasePool,
keyholder: &ActorRef<KeyHolder>,
pubkey: &VerifyingKey,
pubkey: &authn::PublicKey,
metadata: &ClientMetadata,
) -> Result<i32, Error> {
use crate::db::schema::{client_metadata, program_client};
@@ -242,7 +245,7 @@ async fn insert_client(
let client_id = insert_into(program_client::table)
.values((
program_client::public_key.eq(pubkey.as_bytes().to_vec()),
program_client::public_key.eq(pubkey.to_bytes()),
program_client::metadata_id.eq(metadata_id),
program_client::nonce.eq(NONCE_START),
))
@@ -345,14 +348,14 @@ async fn sync_client_metadata(
async fn challenge_client<T>(
transport: &mut T,
pubkey: VerifyingKey,
pubkey: authn::PublicKey,
nonce: i32,
) -> Result<(), Error>
where
T: Bi<Inbound, Result<Outbound, Error>> + ?Sized,
{
transport
.send(Ok(Outbound::AuthChallenge { pubkey, nonce }))
.send(Ok(Outbound::AuthChallenge { pubkey: pubkey.clone(), nonce }))
.await
.map_err(|e| {
error!(error = ?e, "Failed to send auth challenge");
@@ -369,12 +372,10 @@ where
Error::Transport
})?;
let formatted = format_challenge(nonce, pubkey.as_bytes());
pubkey.verify_strict(&formatted, &signature).map_err(|_| {
if !pubkey.verify(nonce, CLIENT_CONTEXT, &signature) {
error!("Challenge solution verification failed");
Error::InvalidChallengeSolution
})?;
return Err(Error::InvalidChallengeSolution);
}
Ok(())
}
@@ -396,7 +397,7 @@ where
approve_new_client(
&props.actors,
ClientProfile {
pubkey,
pubkey: pubkey.clone(),
metadata: metadata.clone(),
},
)

View File

@@ -4,18 +4,19 @@ use tracing::{error, info};
use crate::{
actors::{GlobalActors, client::session::ClientSession},
crypto::authn,
crypto::integrity::{Integrable, hashing::Hashable},
db,
};
#[derive(Debug, Clone)]
pub struct ClientProfile {
pub pubkey: ed25519_dalek::VerifyingKey,
pub pubkey: authn::PublicKey,
pub metadata: ClientMetadata,
}
pub struct ClientCredentials {
pub pubkey: ed25519_dalek::VerifyingKey,
pub pubkey: authn::PublicKey,
pub nonce: i32,
}
@@ -25,7 +26,7 @@ impl Integrable for ClientCredentials {
impl Hashable for ClientCredentials {
fn hash<H: sha2::Digest>(&self, hasher: &mut H) {
hasher.update(self.pubkey.as_bytes());
hasher.update(self.pubkey.to_bytes());
self.nonce.hash(hasher);
}
}
@@ -48,7 +49,12 @@ pub async fn connect_client<T>(mut props: ClientConnection, transport: &mut T)
where
T: Bi<auth::Inbound, Result<auth::Outbound, auth::Error>> + Send + ?Sized,
{
match auth::authenticate(&mut props, transport).await {
let fut = auth::authenticate(&mut props, transport);
println!(
"authenticate future size: {}",
std::mem::size_of_val(&fut)
);
match fut.await {
Ok(client_id) => {
ClientSession::spawn(ClientSession::new(props, client_id));
info!("Client authenticated, session started");