From bdb9f017575b66aaa27c91ec23d2ce59bcc71ba2 Mon Sep 17 00:00:00 2001 From: hdbg Date: Mon, 16 Feb 2026 21:38:36 +0100 Subject: [PATCH] refactor(server): actors reorganization & linter fixes --- server/crates/arbiter-proto/src/lib.rs | 4 +- .../src/actors/keyholder/encryption.rs | 1 + .../actors/keyholder/{ => encryption}/v1.rs | 14 +++---- .../actors/{keyholder.rs => keyholder/mod.rs} | 5 ++- .../{user_agent.rs => user_agent/mod.rs} | 18 ++++---- .../src/actors/user_agent/state.rs | 41 +++++-------------- server/crates/arbiter-server/src/db.rs | 2 +- 7 files changed, 29 insertions(+), 56 deletions(-) create mode 100644 server/crates/arbiter-server/src/actors/keyholder/encryption.rs rename server/crates/arbiter-server/src/actors/keyholder/{ => encryption}/v1.rs (95%) rename server/crates/arbiter-server/src/actors/{keyholder.rs => keyholder/mod.rs} (99%) rename server/crates/arbiter-server/src/actors/{user_agent.rs => user_agent/mod.rs} (96%) diff --git a/server/crates/arbiter-proto/src/lib.rs b/server/crates/arbiter-proto/src/lib.rs index bce8e36..a738e8f 100644 --- a/server/crates/arbiter-proto/src/lib.rs +++ b/server/crates/arbiter-proto/src/lib.rs @@ -10,10 +10,10 @@ pub mod proto { pub mod transport; -pub static BOOTSTRAP_TOKEN_PATH: &'static str = "bootstrap_token"; +pub static BOOTSTRAP_TOKEN_PATH: &str = "bootstrap_token"; pub fn home_path() -> Result { - static ARBITER_HOME: &'static str = ".arbiter"; + static ARBITER_HOME: &str = ".arbiter"; let home_dir = std::env::home_dir().ok_or(std::io::Error::new( std::io::ErrorKind::PermissionDenied, "can not get home directory", diff --git a/server/crates/arbiter-server/src/actors/keyholder/encryption.rs b/server/crates/arbiter-server/src/actors/keyholder/encryption.rs new file mode 100644 index 0000000..a3a6d96 --- /dev/null +++ b/server/crates/arbiter-server/src/actors/keyholder/encryption.rs @@ -0,0 +1 @@ +pub mod v1; diff --git a/server/crates/arbiter-server/src/actors/keyholder/v1.rs b/server/crates/arbiter-server/src/actors/keyholder/encryption/v1.rs similarity index 95% rename from server/crates/arbiter-server/src/actors/keyholder/v1.rs rename to server/crates/arbiter-server/src/actors/keyholder/encryption/v1.rs index 86878dc..f0d79df 100644 --- a/server/crates/arbiter-server/src/actors/keyholder/v1.rs +++ b/server/crates/arbiter-server/src/actors/keyholder/encryption/v1.rs @@ -42,12 +42,12 @@ impl<'a> TryFrom<&'a [u8]> for Nonce { return Err(()); } let mut nonce = [0u8; NONCE_LENGTH]; - nonce.copy_from_slice(&value); + nonce.copy_from_slice(value); Ok(Self(nonce)) } } -pub struct KeyCell(pub(super) MemSafe); +pub struct KeyCell(pub MemSafe); impl From> for KeyCell { fn from(value: MemSafe) -> Self { Self(value) @@ -85,10 +85,6 @@ impl KeyCell { key.into() } - pub fn into_inner(self) -> MemSafe { - self.0 - } - pub fn encrypt_in_place( &mut self, nonce: &Nonce, @@ -130,7 +126,7 @@ impl KeyCell { let ciphertext = cipher.encrypt( - &nonce, + nonce, Payload { msg: plaintext.as_ref(), aad: associated_data, @@ -142,7 +138,7 @@ impl KeyCell { pub type Salt = [u8; ArgonSalt::RECOMMENDED_LENGTH]; -pub(super) fn generate_salt() -> Salt { +pub fn generate_salt() -> Salt { let mut salt = Salt::default(); let mut rng = StdRng::try_from_rng(&mut SysRng).unwrap(); rng.fill_bytes(&mut salt); @@ -151,7 +147,7 @@ pub(super) fn generate_salt() -> Salt { /// User password might be of different length, have not enough entropy, etc... /// Derive a fixed-length key from the password using Argon2id, which is designed for password hashing and key derivation. -pub(super) fn derive_seal_key(mut password: MemSafe>, salt: &Salt) -> KeyCell { +pub fn derive_seal_key(mut password: MemSafe>, salt: &Salt) -> KeyCell { let params = argon2::Params::new(262_144, 3, 4, None).unwrap(); let hasher = Argon2::new(Algorithm::Argon2id, argon2::Version::V0x13, params); let mut key = MemSafe::new(Key::default()).unwrap(); diff --git a/server/crates/arbiter-server/src/actors/keyholder.rs b/server/crates/arbiter-server/src/actors/keyholder/mod.rs similarity index 99% rename from server/crates/arbiter-server/src/actors/keyholder.rs rename to server/crates/arbiter-server/src/actors/keyholder/mod.rs index 4a0bfbf..87e9e52 100644 --- a/server/crates/arbiter-server/src/actors/keyholder.rs +++ b/server/crates/arbiter-server/src/actors/keyholder/mod.rs @@ -9,15 +9,16 @@ use strum::{EnumDiscriminants, IntoDiscriminant}; use tracing::{error, info}; use crate::{ - actors::keyholder::v1::{KeyCell, Nonce}, db::{ self, models::{self, RootKeyHistory}, schema::{self}, }, }; +use encryption::v1::{self, KeyCell, Nonce}; + +pub mod encryption; -pub mod v1; #[derive(Default, EnumDiscriminants)] #[strum_discriminants(derive(Reply), vis(pub))] diff --git a/server/crates/arbiter-server/src/actors/user_agent.rs b/server/crates/arbiter-server/src/actors/user_agent/mod.rs similarity index 96% rename from server/crates/arbiter-server/src/actors/user_agent.rs rename to server/crates/arbiter-server/src/actors/user_agent/mod.rs index c180b28..098333a 100644 --- a/server/crates/arbiter-server/src/actors/user_agent.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/mod.rs @@ -10,7 +10,7 @@ use arbiter_proto::proto::{ }; use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit}; use diesel::{ExpressionMethods as _, OptionalExtension as _, QueryDsl, dsl::update}; -use diesel_async::{AsyncConnection, RunQueryDsl}; +use diesel_async::{RunQueryDsl}; use ed25519_dalek::VerifyingKey; use kameo::{Actor, actor::ActorRef, error::SendError, messages}; use memsafe::MemSafe; @@ -25,7 +25,7 @@ use crate::{ bootstrap::{Bootstrapper, ConsumeToken}, keyholder::{self, KeyHolder, TryUnseal}, user_agent::state::{ - AuthRequestContext, ChallengeContext, DummyContext, UnsealContext, UserAgentEvents, + ChallengeContext, DummyContext, UnsealContext, UserAgentEvents, UserAgentStateMachine, UserAgentStates, }, }, @@ -129,7 +129,7 @@ impl UserAgentActor { let nonce: Option = { let mut db_conn = self.db.get().await.to_status()?; db_conn - .transaction(|conn| { + .exclusive_transaction(|conn| { Box::pin(async move { let current_nonce = schema::useragent_client::table .filter( @@ -162,7 +162,7 @@ impl UserAgentActor { let challenge = auth::AuthChallenge { pubkey: pubkey_bytes, - nonce: nonce, + nonce, }; self.transition(UserAgentEvents::SentChallenge(ChallengeContext { @@ -237,7 +237,6 @@ impl UserAgentActor { let client_public_key = PublicKey::from(client_pubkey_bytes); self.transition(UserAgentEvents::UnsealRequest(UnsealContext { - server_public_key: public_key, secret: Mutex::new(Some(secret)), client_public_key, }))?; @@ -325,9 +324,9 @@ impl UserAgentActor { Err(err) => { error!(?err, "Failed to decrypt unseal key"); self.transition(UserAgentEvents::ReceivedInvalidKey)?; - return Ok(unseal_response(UserAgentResponsePayload::UnsealResult( + Ok(unseal_response(UserAgentResponsePayload::UnsealResult( UnsealResult::InvalidKey.into(), - ))); + ))) } } } @@ -342,10 +341,7 @@ impl UserAgentActor { Status::invalid_argument("Failed to convert pubkey to VerifyingKey") })?; - self.transition(UserAgentEvents::AuthRequest(AuthRequestContext { - pubkey, - bootstrap_token: req.bootstrap_token.clone(), - }))?; + self.transition(UserAgentEvents::AuthRequest)?; match req.bootstrap_token { Some(token) => self.auth_with_bootstrap_token(pubkey, token).await, diff --git a/server/crates/arbiter-server/src/actors/user_agent/state.rs b/server/crates/arbiter-server/src/actors/user_agent/state.rs index 866fa6e..0ff5826 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/state.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/state.rs @@ -12,17 +12,9 @@ pub struct ChallengeContext { pub key: VerifyingKey, } -// Request context with deserialized public key for state machine. -// This intermediate struct is needed because the state machine branches depending on presence of bootstrap token, -// but we want to have the deserialized key in both branches. -#[derive(Clone, Debug)] -pub struct AuthRequestContext { - pub pubkey: VerifyingKey, - pub bootstrap_token: Option, -} + pub struct UnsealContext { - pub server_public_key: PublicKey, pub client_public_key: PublicKey, pub secret: Mutex>, } @@ -33,10 +25,10 @@ smlang::statemachine!( name: UserAgent, custom_error: false, transitions: { - *Init + AuthRequest(AuthRequestContext) / auth_request_context = ReceivedAuthRequest(AuthRequestContext), - ReceivedAuthRequest(AuthRequestContext) + ReceivedBootstrapToken = Idle, + *Init + AuthRequest = ReceivedAuthRequest, + ReceivedAuthRequest + ReceivedBootstrapToken = Idle, - ReceivedAuthRequest(AuthRequestContext) + SentChallenge(ChallengeContext) / move_challenge = WaitingForChallengeSolution(ChallengeContext), + ReceivedAuthRequest + SentChallenge(ChallengeContext) / move_challenge = WaitingForChallengeSolution(ChallengeContext), WaitingForChallengeSolution(ChallengeContext) + ReceivedGoodSolution = Idle, WaitingForChallengeSolution(ChallengeContext) + ReceivedBadSolution = AuthError, // block further transitions, but connection should close anyway @@ -49,28 +41,15 @@ smlang::statemachine!( pub struct DummyContext; impl UserAgentStateMachineContext for DummyContext { - #[allow(missing_docs)] - #[allow(clippy::unused_unit)] - fn move_challenge( - &mut self, - _state_data: &AuthRequestContext, - event_data: ChallengeContext, - ) -> Result { - Ok(event_data) - } - - #[allow(missing_docs)] - #[allow(clippy::unused_unit)] - fn auth_request_context( - &mut self, - event_data: AuthRequestContext, - ) -> Result { - Ok(event_data) - } - #[allow(missing_docs)] #[allow(clippy::unused_unit)] fn generate_temp_keypair(&mut self, event_data: UnsealContext) -> Result { Ok(event_data) } + + #[allow(missing_docs)] + #[allow(clippy::unused_unit)] + fn move_challenge< >(&mut self,event_data:ChallengeContext) -> Result { + Ok(event_data) + } } diff --git a/server/crates/arbiter-server/src/db.rs b/server/crates/arbiter-server/src/db.rs index 16567c6..5bb8e9e 100644 --- a/server/crates/arbiter-server/src/db.rs +++ b/server/crates/arbiter-server/src/db.rs @@ -21,7 +21,7 @@ pub type DatabasePool = diesel_async::pooled_connection::bb8::Pool