diff --git a/server/Cargo.lock b/server/Cargo.lock index e36c264..c1be6e9 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -787,6 +787,7 @@ dependencies = [ "tracing", "tracing-subscriber", "x25519-dalek 2.0.1", + "zeroize", ] [[package]] @@ -6261,18 +6262,18 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", diff --git a/server/crates/arbiter-server/Cargo.toml b/server/crates/arbiter-server/Cargo.toml index 7790bd6..6082541 100644 --- a/server/crates/arbiter-server/Cargo.toml +++ b/server/crates/arbiter-server/Cargo.toml @@ -36,6 +36,7 @@ chrono.workspace = true kameo.workspace = true chacha20poly1305 = { version = "0.10.1", features = ["std"] } argon2 = { version = "0.5.3", features = ["zeroize"] } +zeroize = "1.9" restructed = "0.2.2" strum = { version = "0.28.0", features = ["derive"] } pem = "3.0.6" diff --git a/server/crates/arbiter-server/src/actors/bootstrap.rs b/server/crates/arbiter-server/src/actors/bootstrap.rs index 8ec0059..0ed57fa 100644 --- a/server/crates/arbiter-server/src/actors/bootstrap.rs +++ b/server/crates/arbiter-server/src/actors/bootstrap.rs @@ -7,6 +7,7 @@ use kameo::{Actor, messages}; use rand::{RngExt, distr::Alphanumeric, make_rng, rngs::StdRng}; use subtle::ConstantTimeEq as _; use thiserror::Error; +use zeroize::Zeroizing; const TOKEN_LENGTH: usize = 64; @@ -40,7 +41,7 @@ pub enum Error { #[derive(Actor)] pub struct Bootstrapper { - token: Option, + token: Option>, } impl Bootstrapper { @@ -56,7 +57,7 @@ impl Bootstrapper { let token = if row_count == 0 { let token = generate_token().await?; - Some(token) + Some(Zeroizing::new(token)) } else { None }; @@ -65,10 +66,8 @@ impl Bootstrapper { } } -#[messages] impl Bootstrapper { - #[message] - pub fn is_correct_token(&self, token: String) -> bool { + fn is_correct_token(&self, token: &str) -> bool { self.token.as_ref().is_some_and(|expected| { let expected_bytes = expected.as_bytes(); let token_bytes = token.as_bytes(); @@ -77,10 +76,13 @@ impl Bootstrapper { bool::from(choice) }) } +} +#[messages] +impl Bootstrapper { #[message] - pub fn consume_token(&mut self, token: String) -> bool { - if self.is_correct_token(token) { + pub fn consume_token(&mut self, token: Zeroizing) -> bool { + if self.is_correct_token(&token) { self.token = None; true } else { @@ -93,6 +95,6 @@ impl Bootstrapper { impl Bootstrapper { #[message] pub fn get_token(&self) -> Option { - self.token.clone() + self.token.as_ref().map(|token| token.to_string()) } } diff --git a/server/crates/arbiter-server/src/grpc/operator/auth.rs b/server/crates/arbiter-server/src/grpc/operator/auth.rs index fa15310..818f136 100644 --- a/server/crates/arbiter-server/src/grpc/operator/auth.rs +++ b/server/crates/arbiter-server/src/grpc/operator/auth.rs @@ -18,6 +18,7 @@ use arbiter_proto::{ use async_trait::async_trait; use tonic::Status; use tracing::warn; +use zeroize::Zeroizing; pub(super) struct AuthTransportAdapter<'a> { pub(super) bi: &'a mut GrpcBi, @@ -171,7 +172,7 @@ impl Receiver for AuthTransportAdapter<'_> { Some(auth::Inbound::AuthChallengeRequest { pubkey, - bootstrap_token, + bootstrap_token: bootstrap_token.map(Zeroizing::new), }) } AuthRequestPayload::ChallengeSolution(ProtoAuthChallengeSolution { signature }) => { diff --git a/server/crates/arbiter-server/src/peers/operator/auth/mod.rs b/server/crates/arbiter-server/src/peers/operator/auth/mod.rs index 8bea8a0..09006cb 100644 --- a/server/crates/arbiter-server/src/peers/operator/auth/mod.rs +++ b/server/crates/arbiter-server/src/peers/operator/auth/mod.rs @@ -1,6 +1,7 @@ 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, @@ -14,7 +15,7 @@ mod state; pub enum Inbound { AuthChallengeRequest { pubkey: authn::PublicKey, - bootstrap_token: Option, + bootstrap_token: Option>, }, AuthChallengeSolution { signature: Vec, diff --git a/server/crates/arbiter-server/src/peers/operator/auth/state.rs b/server/crates/arbiter-server/src/peers/operator/auth/state.rs index 38f1ecd..d439267 100644 --- a/server/crates/arbiter-server/src/peers/operator/auth/state.rs +++ b/server/crates/arbiter-server/src/peers/operator/auth/state.rs @@ -9,6 +9,7 @@ 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; @@ -16,13 +17,13 @@ use tracing::error; pub(super) struct ChallengeRequest { pub(super) pubkey: authn::PublicKey, - pub(super) bootstrap_token: Option, + pub(super) bootstrap_token: Option>, } pub struct ChallengeContext { pub(super) challenge: AuthChallenge, pub(super) pubkey: authn::PublicKey, - pub(super) bootstrap_token: Option, + pub(super) bootstrap_token: Option>, } pub(super) struct ChallengeSolution { @@ -152,15 +153,13 @@ where } // Resolve client id: bootstrap (consume token + register) or lookup - let id = match bootstrap_token { + let id = match bootstrap_token.clone() { Some(token) => { let token_ok: bool = self .conn .actors .bootstrapper - .ask(ConsumeToken { - token: token.clone(), - }) + .ask(ConsumeToken { token }) .await .map_err(|e| { error!(?e, "Failed to consume bootstrap token");