security(bootstrap): use SafeCell for token storage instead of zeroize
Some checks failed
ci/woodpecker/pr/server-audit Pipeline was successful
ci/woodpecker/pr/server-vet Pipeline failed
ci/woodpecker/pr/server-lint Pipeline failed
ci/woodpecker/pr/server-test Pipeline was successful

This commit is contained in:
CleverWild
2026-06-19 22:53:28 +02:00
parent c12c12d73e
commit f0456157d5
7 changed files with 46 additions and 50 deletions

View File

@@ -1,7 +1,6 @@
use super::{Credentials, OperatorConnection};
use arbiter_crypto::authn::{self, AuthChallenge};
use arbiter_proto::transport::Bi;
use zeroize::Zeroizing;
use state::{
AuthContext, AuthError, AuthEvents, AuthStateMachine, AuthStates, ChallengeRequest,
@@ -15,7 +14,7 @@ mod state;
pub enum Inbound {
AuthChallengeRequest {
pubkey: authn::PublicKey,
bootstrap_token: Option<Zeroizing<String>>,
bootstrap_token: Option<Vec<u8>>,
},
AuthChallengeSolution {
signature: Vec<u8>,

View File

@@ -9,7 +9,6 @@ use crate::{
};
use arbiter_crypto::authn::{self, AuthChallenge, OPERATOR_CONTEXT};
use arbiter_proto::transport::Bi;
use zeroize::Zeroizing;
use diesel::{ExpressionMethods as _, OptionalExtension as _, QueryDsl};
use diesel_async::RunQueryDsl;
@@ -17,13 +16,12 @@ use tracing::error;
pub(super) struct ChallengeRequest {
pub(super) pubkey: authn::PublicKey,
pub(super) bootstrap_token: Option<Zeroizing<String>>,
pub(super) bootstrap_token: Option<Vec<u8>>,
}
pub struct ChallengeContext {
pub(super) challenge: AuthChallenge,
pub(super) pubkey: authn::PublicKey,
pub(super) bootstrap_token: Option<Zeroizing<String>>,
}
pub(super) struct ChallengeSolution {
@@ -80,11 +78,16 @@ async fn register_key(db: &DatabasePool, pubkey: &authn::PublicKey) -> Result<i3
pub(super) struct AuthContext<'a, T: ?Sized> {
pub(super) conn: &'a mut OperatorConnection,
pub(super) transport: &'a mut T,
bootstrap_token: Option<Vec<u8>>,
}
impl<'a, T: ?Sized> AuthContext<'a, T> {
pub(super) const fn new(conn: &'a mut OperatorConnection, transport: &'a mut T) -> Self {
Self { conn, transport }
Self {
conn,
transport,
bootstrap_token: None,
}
}
}
@@ -109,6 +112,8 @@ where
}
}
self.bootstrap_token = bootstrap_token;
let challenge = AuthChallenge::generate(&mut rand::rng());
self.transport
@@ -121,20 +126,12 @@ where
Error::Transport
})?;
Ok(ChallengeContext {
challenge,
pubkey,
bootstrap_token,
})
Ok(ChallengeContext { challenge, pubkey })
}
async fn verify_solution(
&mut self,
ChallengeContext {
challenge,
pubkey,
bootstrap_token,
}: &ChallengeContext,
ChallengeContext { challenge, pubkey }: &ChallengeContext,
ChallengeSolution { solution }: ChallengeSolution,
) -> Result<Credentials, Self::Error> {
let signature = authn::Signature::try_from(solution.as_slice()).map_err(|()| {
@@ -153,7 +150,7 @@ where
}
// Resolve client id: bootstrap (consume token + register) or lookup
let id = match bootstrap_token.clone() {
let id = match self.bootstrap_token.take() {
Some(token) => {
let token_ok: bool = self
.conn