From 1585f90cae0af5e6bfe170b48c9b5ffe0fa83151 Mon Sep 17 00:00:00 2001 From: hdbg Date: Tue, 7 Apr 2026 23:54:29 +0200 Subject: [PATCH] refactor(server): reorganized client/user_agent actors into separate module `peers` and added event `MessageBus` --- AGENTS.md | 2 +- CLAUDE.md | 2 +- server/Cargo.lock | 24 ++- server/Cargo.toml | 2 +- server/crates/arbiter-server/Cargo.toml | 1 + .../arbiter-server/src/actors/evm/mod.rs | 36 ++--- .../client_connect_approval.rs | 7 +- .../src/actors/flow_coordinator/mod.rs | 6 +- .../crates/arbiter-server/src/actors/mod.rs | 26 ++-- .../src/actors/{keyholder => vault}/mod.rs | 138 ++++++++++-------- .../arbiter-server/src/crypto/integrity/v1.rs | 71 ++++----- server/crates/arbiter-server/src/evm/mod.rs | 16 +- .../crates/arbiter-server/src/grpc/client.rs | 2 +- .../arbiter-server/src/grpc/client/auth.rs | 2 +- .../arbiter-server/src/grpc/client/evm.rs | 2 +- .../arbiter-server/src/grpc/client/vault.rs | 12 +- server/crates/arbiter-server/src/grpc/mod.rs | 2 +- .../arbiter-server/src/grpc/user_agent.rs | 2 +- .../src/grpc/user_agent/auth.rs | 2 +- .../arbiter-server/src/grpc/user_agent/evm.rs | 2 +- .../src/grpc/user_agent/sdk_client.rs | 2 +- .../src/grpc/user_agent/vault.rs | 12 +- server/crates/arbiter-server/src/lib.rs | 1 + .../src/{actors => peers}/client/auth.rs | 30 ++-- .../src/{actors => peers}/client/mod.rs | 4 +- .../src/{actors => peers}/client/session.rs | 13 +- server/crates/arbiter-server/src/peers/mod.rs | 2 + .../src/{actors => peers}/user_agent/auth.rs | 6 +- .../user_agent/auth/state.rs | 23 +-- .../src/{actors => peers}/user_agent/mod.rs | 4 +- .../{actors => peers}/user_agent/session.rs | 8 +- .../user_agent/session/connection.rs | 43 +++--- .../user_agent/session/state.rs | 0 .../arbiter-server/tests/client/auth.rs | 9 +- .../crates/arbiter-server/tests/common/mod.rs | 6 +- .../crates/arbiter-server/tests/keyholder.rs | 8 - .../arbiter-server/tests/user_agent/auth.rs | 17 ++- .../arbiter-server/tests/user_agent/unseal.rs | 11 +- server/crates/arbiter-server/tests/vault.rs | 8 + .../tests/{keyholder => vault}/concurrency.rs | 14 +- .../tests/{keyholder => vault}/lifecycle.rs | 26 ++-- .../tests/{keyholder => vault}/storage.rs | 14 +- 42 files changed, 332 insertions(+), 286 deletions(-) rename server/crates/arbiter-server/src/actors/{keyholder => vault}/mod.rs (84%) rename server/crates/arbiter-server/src/{actors => peers}/client/auth.rs (94%) rename server/crates/arbiter-server/src/{actors => peers}/client/mod.rs (95%) rename server/crates/arbiter-server/src/{actors => peers}/client/session.rs (89%) create mode 100644 server/crates/arbiter-server/src/peers/mod.rs rename server/crates/arbiter-server/src/{actors => peers}/user_agent/auth.rs (96%) rename server/crates/arbiter-server/src/{actors => peers}/user_agent/auth/state.rs (94%) rename server/crates/arbiter-server/src/{actors => peers}/user_agent/mod.rs (95%) rename server/crates/arbiter-server/src/{actors => peers}/user_agent/session.rs (96%) rename server/crates/arbiter-server/src/{actors => peers}/user_agent/session/connection.rs (93%) rename server/crates/arbiter-server/src/{actors => peers}/user_agent/session/state.rs (100%) delete mode 100644 server/crates/arbiter-server/tests/keyholder.rs create mode 100644 server/crates/arbiter-server/tests/vault.rs rename server/crates/arbiter-server/tests/{keyholder => vault}/concurrency.rs (91%) rename server/crates/arbiter-server/tests/{keyholder => vault}/lifecycle.rs (76%) rename server/crates/arbiter-server/tests/{keyholder => vault}/storage.rs (91%) diff --git a/AGENTS.md b/AGENTS.md index fb2d230..d935f50 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -66,7 +66,7 @@ cargo insta review The server is actor-based using the **kameo** crate. All long-lived state lives in `GlobalActors`: - **`Bootstrapper`** — Manages the one-time bootstrap token written to `~/.arbiter/bootstrap_token` on first run. -- **`KeyHolder`** — Holds the encrypted root key and manages the Sealed/Unsealed vault state machine. On unseal, decrypts the root key into a `memsafe` hardened memory cell. +- **`Vault`** — Holds the encrypted root key and manages the Sealed/Unsealed vault state machine. On unseal, decrypts the root key into a `memsafe` hardened memory cell. - **`FlowCoordinator`** — Coordinates cross-connection flow between user agents and SDK clients. - **`EvmActor`** — Handles EVM transaction policy enforcement and signing. diff --git a/CLAUDE.md b/CLAUDE.md index c3c3357..8305248 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -66,7 +66,7 @@ cargo insta review The server is actor-based using the **kameo** crate. All long-lived state lives in `GlobalActors`: - **`Bootstrapper`** — Manages the one-time bootstrap token written to `~/.arbiter/bootstrap_token` on first run. -- **`KeyHolder`** — Holds the encrypted root key and manages the Sealed/Unsealed vault state machine. On unseal, decrypts the root key into a `memsafe` hardened memory cell. +- **`Vault`** — Holds the encrypted root key and manages the Sealed/Unsealed vault state machine. On unseal, decrypts the root key into a `memsafe` hardened memory cell. - **`FlowCoordinator`** — Coordinates cross-connection flow between user agents and SDK clients. - **`EvmActor`** — Handles EVM transaction policy enforcement and signing. diff --git a/server/Cargo.lock b/server/Cargo.lock index 5b89b7e..d692f5b 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -753,6 +753,7 @@ dependencies = [ "insta", "k256", "kameo", + "kameo_actors", "ml-dsa", "mutants", "pem", @@ -2962,9 +2963,9 @@ dependencies = [ [[package]] name = "kameo" -version = "0.19.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c4af7638c67029fd6821d02813c3913c803784648725d4df4082c9b91d7cbb1" +checksum = "b1dfd134d7a2c6ec05ee696dcbf3f7a034bdb97ecc623e981014652dcd124d77" dependencies = [ "downcast-rs", "dyn-clone", @@ -2976,10 +2977,23 @@ dependencies = [ ] [[package]] -name = "kameo_macros" -version = "0.19.0" +name = "kameo_actors" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13c324e2d8c8e126e63e66087448b4267e263e6cb8770c56d10a9d0d279d9e2" +checksum = "220bdd75769f0a9b752a91123e58bf42f595e3c36ff4b13818d7a87d962076e6" +dependencies = [ + "futures", + "glob", + "kameo", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "kameo_macros" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16c9002c9ecd16e1636f566c0bf62db48e70d86ed0d0a91b398955e883217a23" dependencies = [ "heck", "proc-macro2", diff --git a/server/Cargo.toml b/server/Cargo.toml index 98018a0..7727573 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -26,7 +26,7 @@ thiserror = "2.0.18" async-trait = "0.1.89" futures = "0.3.32" tokio-stream = { version = "0.1.18", features = ["full"] } -kameo = "0.19.2" +kameo = "0.20" prost-types = { version = "0.14.3", features = ["chrono"] } x25519-dalek = { version = "2.0.1", features = ["getrandom"] } rstest = "0.26.1" diff --git a/server/crates/arbiter-server/Cargo.toml b/server/crates/arbiter-server/Cargo.toml index c563ee0..f7272f6 100644 --- a/server/crates/arbiter-server/Cargo.toml +++ b/server/crates/arbiter-server/Cargo.toml @@ -58,6 +58,7 @@ ml-dsa.workspace = true ed25519-dalek.workspace = true x25519-dalek.workspace = true k256.workspace = true +kameo_actors = "0.5.0" [dev-dependencies] insta = "1.46.3" diff --git a/server/crates/arbiter-server/src/actors/evm/mod.rs b/server/crates/arbiter-server/src/actors/evm/mod.rs index c31cdd0..5520ca1 100644 --- a/server/crates/arbiter-server/src/actors/evm/mod.rs +++ b/server/crates/arbiter-server/src/actors/evm/mod.rs @@ -7,7 +7,7 @@ use kameo::{Actor, actor::ActorRef, messages}; use rand::{SeedableRng, rng, rngs::StdRng}; use crate::{ - actors::keyholder::{CreateNew, Decrypt, KeyHolder}, + actors::vault::{CreateNew, Decrypt, Vault}, crypto::integrity, db::{ DatabaseError, DatabasePool, @@ -34,11 +34,11 @@ pub enum SignTransactionError { #[error("Database error: {0}")] Database(#[from] DatabaseError), - #[error("Keyholder error: {0}")] - Keyholder(#[from] crate::actors::keyholder::Error), + #[error("Vault error: {0}")] + Vault(#[from] crate::actors::vault::Error), - #[error("Keyholder mailbox error")] - KeyholderSend, + #[error("Vault mailbox error")] + VaultSend, #[error("Signing error: {0}")] Signing(#[from] alloy::signers::Error), @@ -49,11 +49,11 @@ pub enum SignTransactionError { #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("Keyholder error: {0}")] - Keyholder(#[from] crate::actors::keyholder::Error), + #[error("Vault error: {0}")] + Vault(#[from] crate::actors::vault::Error), - #[error("Keyholder mailbox error")] - KeyholderSend, + #[error("Vault mailbox error")] + VaultSend, #[error("Database error: {0}")] Database(#[from] DatabaseError), @@ -64,20 +64,20 @@ pub enum Error { #[derive(Actor)] pub struct EvmActor { - pub keyholder: ActorRef, + pub vault: ActorRef, pub db: DatabasePool, pub rng: StdRng, pub engine: evm::Engine, } impl EvmActor { - pub fn new(keyholder: ActorRef, db: DatabasePool) -> Self { + pub fn new(vault: ActorRef, db: DatabasePool) -> Self { // is it safe to seed rng from system once? // todo: audit let rng = StdRng::from_rng(&mut rng()); - let engine = evm::Engine::new(db.clone(), keyholder.clone()); + let engine = evm::Engine::new(db.clone(), vault.clone()); Self { - keyholder, + vault, db, rng, engine, @@ -94,10 +94,10 @@ impl EvmActor { let plaintext = key_cell.read_inline(|reader| SafeCell::new(reader.to_vec())); let aead_id: i32 = self - .keyholder + .vault .ask(CreateNew { plaintext }) .await - .map_err(|_| Error::KeyholderSend)?; + .map_err(|_| Error::VaultSend)?; let mut conn = self.db.get().await.map_err(DatabaseError::from)?; let wallet_id = insert_into(schema::evm_wallet::table) @@ -160,7 +160,7 @@ impl EvmActor { #[message] pub async fn useragent_delete_grant(&mut self, _grant_id: i32) -> Result<(), Error> { // let mut conn = self.db.get().await.map_err(DatabaseError::from)?; - // let keyholder = self.keyholder.clone(); + // let vault = self.vault.clone(); // diesel_async::AsyncConnection::transaction(&mut conn, |conn| { // Box::pin(async move { @@ -254,12 +254,12 @@ impl EvmActor { drop(conn); let raw_key: SafeCell> = self - .keyholder + .vault .ask(Decrypt { aead_id: wallet.aead_encrypted_id, }) .await - .map_err(|_| SignTransactionError::KeyholderSend)?; + .map_err(|_| SignTransactionError::VaultSend)?; let signer = safe_signer::SafeSigner::from_cell(raw_key)?; diff --git a/server/crates/arbiter-server/src/actors/flow_coordinator/client_connect_approval.rs b/server/crates/arbiter-server/src/actors/flow_coordinator/client_connect_approval.rs index c5b20c3..293a463 100644 --- a/server/crates/arbiter-server/src/actors/flow_coordinator/client_connect_approval.rs +++ b/server/crates/arbiter-server/src/actors/flow_coordinator/client_connect_approval.rs @@ -6,10 +6,9 @@ use kameo::{ reply::ReplySender, }; -use crate::actors::{ - client::ClientProfile, - flow_coordinator::ApprovalError, - user_agent::{UserAgentSession, session::BeginNewClientApproval}, +use crate::{ + actors::flow_coordinator::ApprovalError, + peers::{client::ClientProfile, user_agent::{UserAgentSession, session::BeginNewClientApproval}}, }; pub struct Args { diff --git a/server/crates/arbiter-server/src/actors/flow_coordinator/mod.rs b/server/crates/arbiter-server/src/actors/flow_coordinator/mod.rs index 2e0aa9a..7261f7f 100644 --- a/server/crates/arbiter-server/src/actors/flow_coordinator/mod.rs +++ b/server/crates/arbiter-server/src/actors/flow_coordinator/mod.rs @@ -9,11 +9,7 @@ use kameo::{ }; use tracing::info; -use crate::actors::{ - client::{ClientProfile, session::ClientSession}, - flow_coordinator::client_connect_approval::ClientApprovalController, - user_agent::session::UserAgentSession, -}; +use crate::{actors::flow_coordinator::client_connect_approval::ClientApprovalController, peers::{client::{ClientProfile, session::ClientSession}, user_agent::UserAgentSession}}; pub mod client_connect_approval; diff --git a/server/crates/arbiter-server/src/actors/mod.rs b/server/crates/arbiter-server/src/actors/mod.rs index 8ff1fce..155d34d 100644 --- a/server/crates/arbiter-server/src/actors/mod.rs +++ b/server/crates/arbiter-server/src/actors/mod.rs @@ -1,47 +1,53 @@ use kameo::actor::{ActorRef, Spawn}; +use kameo_actors::{DeliveryStrategy, message_bus::MessageBus}; use thiserror::Error; use crate::{ actors::{ bootstrap::Bootstrapper, evm::EvmActor, flow_coordinator::FlowCoordinator, - keyholder::KeyHolder, + vault::Vault, }, db, }; pub mod bootstrap; -pub mod client; -mod evm; +pub mod evm; pub mod flow_coordinator; -pub mod keyholder; -pub mod user_agent; +pub mod vault; #[derive(Error, Debug)] pub enum SpawnError { #[error("Failed to spawn Bootstrapper actor")] Bootstrapper(#[from] bootstrap::Error), - #[error("Failed to spawn KeyHolder actor")] - KeyHolder(#[from] keyholder::Error), + #[error("Failed to spawn Vault actor")] + Vault(#[from] vault::Error), } /// Long-lived actors that are shared across all connections and handle global state and operations #[derive(Clone)] pub struct GlobalActors { - pub key_holder: ActorRef, + pub vault: ActorRef, pub bootstrapper: ActorRef, pub flow_coordinator: ActorRef, pub evm: ActorRef, + pub events: ActorRef, } impl GlobalActors { + pub fn spawn_message_bus() -> ActorRef { + MessageBus::spawn(MessageBus::new(DeliveryStrategy::Guaranteed)) + } + pub async fn spawn(db: db::DatabasePool) -> Result { - let key_holder = KeyHolder::spawn(KeyHolder::new(db.clone()).await?); + let message_bus = Self::spawn_message_bus(); + let key_holder = Vault::spawn(Vault::new(db.clone(), message_bus.clone()).await?); Ok(Self { bootstrapper: Bootstrapper::spawn(Bootstrapper::new(&db).await?), evm: EvmActor::spawn(EvmActor::new(key_holder.clone(), db)), - key_holder, + vault: key_holder, flow_coordinator: FlowCoordinator::spawn(FlowCoordinator::default()), + events: message_bus, }) } } diff --git a/server/crates/arbiter-server/src/actors/keyholder/mod.rs b/server/crates/arbiter-server/src/actors/vault/mod.rs similarity index 84% rename from server/crates/arbiter-server/src/actors/keyholder/mod.rs rename to server/crates/arbiter-server/src/actors/vault/mod.rs index 64387bc..7a4f456 100644 --- a/server/crates/arbiter-server/src/actors/keyholder/mod.rs +++ b/server/crates/arbiter-server/src/actors/vault/mod.rs @@ -5,7 +5,8 @@ use diesel::{ }; use diesel_async::{AsyncConnection, RunQueryDsl}; use hmac::Mac as _; -use kameo::{Actor, Reply, messages}; +use kameo::{Actor, Reply, actor::ActorRef, messages}; +use kameo_actors::message_bus::{MessageBus, Publish}; use strum::{EnumDiscriminants, IntoDiscriminant}; use tracing::{error, info}; @@ -21,26 +22,26 @@ use crate::db::{ }; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; -#[derive(Default, EnumDiscriminants)] -#[strum_discriminants(derive(Reply), vis(pub), name(KeyHolderState))] -enum State { - #[default] - Unbootstrapped, - Sealed { - root_key_history_id: i32, - }, - Unsealed { - root_key_history_id: i32, - root_key: KeyCell, - }, +pub mod events { + + #[derive(Clone, Copy)] + pub struct VaultBootstrapped; + + #[derive(Clone, Copy)] + pub struct VaultUnsealed; + + #[derive(Clone, Copy)] + pub struct VaultResealed; } #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("Keyholder is already bootstrapped")] + #[error("Vault is already bootstrapped")] AlreadyBootstrapped, - #[error("Keyholder is not bootstrapped")] + #[error("Vault is not bootstrapped")] NotBootstrapped, + #[error("Vault is sealed")] + Sealed, #[error("Invalid key provided")] InvalidKey, @@ -60,18 +61,35 @@ pub enum Error { BrokenDatabase, } +struct Unsealed { + root_key_history_id: i32, + root_key: KeyCell, +} + +#[derive(Default, EnumDiscriminants)] +#[strum_discriminants(derive(Reply), vis(pub), name(VaultState))] +enum State { + #[default] + Unbootstrapped, + Sealed { + root_key_history_id: i32, + }, + Unsealed(Unsealed), +} + /// Manages vault root key and tracks current state of the vault (bootstrapped/unbootstrapped, sealed/unsealed). /// Provides API for encrypting and decrypting data using the vault root key. /// Abstraction over database to make sure nonces are never reused and encryption keys are never exposed in plaintext outside of this actor. #[derive(Actor)] -pub struct KeyHolder { +pub struct Vault { db: db::DatabasePool, state: State, + events: ActorRef, } #[messages] -impl KeyHolder { - pub async fn new(db: db::DatabasePool) -> Result { +impl Vault { + pub async fn new(db: db::DatabasePool, events: ActorRef) -> Result { let state = { let mut conn = db.get().await?; @@ -89,10 +107,10 @@ impl KeyHolder { } }; - Ok(Self { db, state }) + Ok(Self { db, state, events }) } - // Exclusive transaction to avoid race condtions if multiple keyholders write + // Exclusive transaction to avoid race condtions if multiple vaults write // additional layer of protection against nonce-reuse async fn get_new_nonce(pool: &db::DatabasePool, root_key_id: i32) -> Result { let mut conn = pool.get().await?; @@ -129,6 +147,14 @@ impl KeyHolder { Ok(nonce) } + fn expect_unsealed<'a>(state: &'a mut State) -> Result<&'a mut Unsealed, Error> { + match state { + State::Unsealed(unsealed) => Ok(unsealed), + State::Unbootstrapped => Err(Error::NotBootstrapped), + State::Sealed { .. } => Err(Error::Sealed), + } + } + #[message] pub async fn bootstrap(&mut self, seal_key_raw: SafeCell>) -> Result<(), Error> { if !matches!(self.state, State::Unbootstrapped) { @@ -181,12 +207,13 @@ impl KeyHolder { }) .await?; - self.state = State::Unsealed { + self.state = State::Unsealed(Unsealed { root_key, root_key_history_id, - }; + }); - info!("Keyholder bootstrapped successfully"); + info!("Vault bootstrapped successfully"); + self.events.tell(Publish(events::VaultBootstrapped)).await; Ok(()) } @@ -233,24 +260,23 @@ impl KeyHolder { Error::InvalidKey })?; - self.state = State::Unsealed { + self.state = State::Unsealed(Unsealed { root_key_history_id: current_key.id, root_key: KeyCell::try_from(root_key).map_err(|err| { error!(?err, "Broken database: invalid encryption key size"); Error::BrokenDatabase })?, - }; + }); - info!("Keyholder unsealed successfully"); + info!("Vault unsealed successfully"); + self.events.tell(Publish(events::VaultUnsealed)).await; Ok(()) } #[message] pub async fn decrypt(&mut self, aead_id: i32) -> Result>, Error> { - let State::Unsealed { root_key, .. } = &mut self.state else { - return Err(Error::NotBootstrapped); - }; + let Unsealed { root_key, .. } = Self::expect_unsealed(&mut self.state)?; let row: models::AeadEncrypted = { let mut conn = self.db.get().await?; @@ -278,14 +304,10 @@ impl KeyHolder { // Creates new `aead_encrypted` entry in the database and returns it's ID #[message] pub async fn create_new(&mut self, mut plaintext: SafeCell>) -> Result { - let State::Unsealed { + let Unsealed { root_key, root_key_history_id, - .. - } = &mut self.state - else { - return Err(Error::NotBootstrapped); - }; + } = Self::expect_unsealed(&mut self.state)?; // Order matters here - `get_new_nonce` acquires connection, so we need to call it before next acquire // Borrow checker note: &mut borrow a few lines above is disjoint from this field @@ -315,19 +337,16 @@ impl KeyHolder { } #[message] - pub fn get_state(&self) -> KeyHolderState { + pub fn get_state(&self) -> VaultState { self.state.discriminant() } #[message] pub fn sign_integrity(&mut self, mac_input: Vec) -> Result<(i32, Vec), Error> { - let State::Unsealed { + let Unsealed { root_key, root_key_history_id, - } = &mut self.state - else { - return Err(Error::NotBootstrapped); - }; + } = Self::expect_unsealed(&mut self.state)?; let mut hmac = root_key .0 @@ -349,13 +368,10 @@ impl KeyHolder { expected_mac: Vec, key_version: i32, ) -> Result { - let State::Unsealed { + let Unsealed { root_key, root_key_history_id, - } = &mut self.state - else { - return Err(Error::NotBootstrapped); - }; + } = Self::expect_unsealed(&mut self.state)?; if *root_key_history_id != key_version { return Ok(false); @@ -374,17 +390,16 @@ impl KeyHolder { } #[message] - pub fn seal(&mut self) -> Result<(), Error> { - let State::Unsealed { + pub async fn seal(&mut self) -> Result<(), Error> { + let Unsealed { root_key_history_id, .. - } = &self.state - else { - return Err(Error::NotBootstrapped); - }; + } = Self::expect_unsealed(&mut self.state)?; + self.state = State::Sealed { root_key_history_id: *root_key_history_id, }; + self.events.tell(Publish(events::VaultResealed)).await; Ok(()) } } @@ -395,13 +410,18 @@ mod tests { use diesel_async::RunQueryDsl; - use crate::db::{self}; + use crate::{ + actors::GlobalActors, + db::{self}, + }; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use super::*; - async fn bootstrapped_actor(db: &db::DatabasePool) -> KeyHolder { - let mut actor = KeyHolder::new(db.clone()).await.unwrap(); + async fn bootstrapped_actor(db: &db::DatabasePool) -> Vault { + let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()) + .await + .unwrap(); let seal_key = SafeCell::new(b"test-seal-key".to_vec()); actor.bootstrap(seal_key).await.unwrap(); actor @@ -413,17 +433,17 @@ mod tests { let db = db::create_test_pool().await; let mut actor = bootstrapped_actor(&db).await; let root_key_history_id = match actor.state { - State::Unsealed { + State::Unsealed(Unsealed { root_key_history_id, .. - } => root_key_history_id, + }) => root_key_history_id, _ => panic!("expected unsealed state"), }; - let n1 = KeyHolder::get_new_nonce(&db, root_key_history_id) + let n1 = Vault::get_new_nonce(&db, root_key_history_id) .await .unwrap(); - let n2 = KeyHolder::get_new_nonce(&db, root_key_history_id) + let n2 = Vault::get_new_nonce(&db, root_key_history_id) .await .unwrap(); assert!(n2.to_vec() > n1.to_vec(), "nonce must increase"); diff --git a/server/crates/arbiter-server/src/crypto/integrity/v1.rs b/server/crates/arbiter-server/src/crypto/integrity/v1.rs index 4b67217..87199d3 100644 --- a/server/crates/arbiter-server/src/crypto/integrity/v1.rs +++ b/server/crates/arbiter-server/src/crypto/integrity/v1.rs @@ -1,4 +1,4 @@ -use crate::{actors::keyholder, crypto::integrity::hashing::Hashable}; +use crate::{actors::vault, crypto::integrity::hashing::Hashable}; use arbiter_crypto::safecell::SafeCellHandle as _; use hmac::{Hmac, Mac as _}; use sha2::Sha256; @@ -11,7 +11,7 @@ use sha2::Digest as _; pub mod hashing; use crate::{ - actors::keyholder::{KeyHolder, SignIntegrity, VerifyIntegrity}, + actors::vault::{SignIntegrity, Vault, VerifyIntegrity}, db::{ self, models::{IntegrityEnvelope, NewIntegrityEnvelope}, @@ -24,11 +24,11 @@ pub enum Error { #[error("Database error: {0}")] Database(#[from] db::DatabaseError), - #[error("KeyHolder error: {0}")] - Keyholder(#[from] keyholder::Error), + #[error("Vault error: {0}")] + Vault(#[from] vault::Error), - #[error("KeyHolder mailbox error")] - KeyholderSend, + #[error("Vault mailbox error")] + VaultSend, #[error("Integrity envelope is missing for entity {entity_kind}")] MissingEnvelope { entity_kind: &'static str }, @@ -105,7 +105,7 @@ impl IntoId for &'_ [u8] { pub async fn sign_entity( conn: &mut impl AsyncConnection, - keyholder: &ActorRef, + vault: &ActorRef, entity: &E, entity_id: impl IntoId, ) -> Result<(), Error> { @@ -115,13 +115,14 @@ pub async fn sign_entity( let mac_input = build_mac_input(E::KIND, &entity_id, E::VERSION, &payload_hash); - let (key_version, mac) = keyholder - .ask(SignIntegrity { mac_input }) - .await - .map_err(|err| match err { - kameo::error::SendError::HandlerError(inner) => Error::Keyholder(inner), - _ => Error::KeyholderSend, - })?; + let (key_version, mac) = + vault + .ask(SignIntegrity { mac_input }) + .await + .map_err(|err| match err { + kameo::error::SendError::HandlerError(inner) => Error::Vault(inner), + _ => Error::VaultSend, + })?; insert_into(integrity_envelope::table) .values(NewIntegrityEnvelope { @@ -150,7 +151,7 @@ pub async fn sign_entity( pub async fn verify_entity( conn: &mut impl AsyncConnection, - keyholder: &ActorRef, + vault: &ActorRef, entity: &E, entity_id: impl IntoId, ) -> Result { @@ -178,7 +179,7 @@ pub async fn verify_entity( let payload_hash = payload_hash(&entity); let mac_input = build_mac_input(E::KIND, &entity_id, envelope.payload_version, &payload_hash); - let result = keyholder + let result = vault .ask(VerifyIntegrity { mac_input, expected_mac: envelope.mac, @@ -191,10 +192,10 @@ pub async fn verify_entity( Ok(false) => Err(Error::MacMismatch { entity_kind: E::KIND, }), - Err(SendError::HandlerError(keyholder::Error::NotBootstrapped)) => { + Err(SendError::HandlerError(vault::Error::NotBootstrapped)) => { Ok(AttestationStatus::Unavailable) } - Err(_) => Err(Error::KeyholderSend), + Err(_) => Err(Error::VaultSend), } } @@ -203,19 +204,17 @@ mod tests { use diesel::{ExpressionMethods as _, QueryDsl}; use diesel_async::RunQueryDsl; use kameo::{actor::ActorRef, prelude::Spawn}; - + use sha2::Digest; - - use crate::{ - actors::keyholder::{Bootstrap, KeyHolder}, + actors::{GlobalActors, vault::{Bootstrap, Vault}}, db::{self, schema}, }; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; - use super::{Error, Integrable, sign_entity, verify_entity}; use super::hashing::Hashable; + use super::{Error, Integrable, sign_entity, verify_entity}; #[derive(Clone)] struct DummyEntity { @@ -233,8 +232,12 @@ mod tests { const KIND: &'static str = "dummy_entity"; } - async fn bootstrapped_keyholder(db: &db::DatabasePool) -> ActorRef { - let actor = KeyHolder::spawn(KeyHolder::new(db.clone()).await.unwrap()); + async fn bootstrapped_vault(db: &db::DatabasePool) -> ActorRef { + let actor = Vault::spawn( + Vault::new(db.clone(), GlobalActors::spawn_message_bus()) + .await + .unwrap(), + ); actor .ask(Bootstrap { seal_key_raw: SafeCell::new(b"integrity-test-seal-key".to_vec()), @@ -247,7 +250,7 @@ mod tests { #[tokio::test] async fn sign_writes_envelope_and_verify_passes() { let db = db::create_test_pool().await; - let keyholder = bootstrapped_keyholder(&db).await; + let vault = bootstrapped_vault(&db).await; let mut conn = db.get().await.unwrap(); const ENTITY_ID: &[u8] = b"entity-id-7"; @@ -257,7 +260,7 @@ mod tests { payload: b"payload-v1".to_vec(), }; - sign_entity(&mut conn, &keyholder, &entity, ENTITY_ID) + sign_entity(&mut conn, &vault, &entity, ENTITY_ID) .await .unwrap(); @@ -270,7 +273,7 @@ mod tests { .unwrap(); assert_eq!(count, 1, "envelope row must be created exactly once"); - verify_entity(&mut conn, &keyholder, &entity, ENTITY_ID) + verify_entity(&mut conn, &vault, &entity, ENTITY_ID) .await .unwrap(); } @@ -278,7 +281,7 @@ mod tests { #[tokio::test] async fn tampered_mac_fails_verification() { let db = db::create_test_pool().await; - let keyholder = bootstrapped_keyholder(&db).await; + let vault = bootstrapped_vault(&db).await; let mut conn = db.get().await.unwrap(); const ENTITY_ID: &[u8] = b"entity-id-11"; @@ -288,7 +291,7 @@ mod tests { payload: b"payload-v1".to_vec(), }; - sign_entity(&mut conn, &keyholder, &entity, ENTITY_ID) + sign_entity(&mut conn, &vault, &entity, ENTITY_ID) .await .unwrap(); @@ -300,7 +303,7 @@ mod tests { .await .unwrap(); - let err = verify_entity(&mut conn, &keyholder, &entity, ENTITY_ID) + let err = verify_entity(&mut conn, &vault, &entity, ENTITY_ID) .await .unwrap_err(); assert!(matches!(err, Error::MacMismatch { .. })); @@ -309,7 +312,7 @@ mod tests { #[tokio::test] async fn changed_payload_fails_verification() { let db = db::create_test_pool().await; - let keyholder = bootstrapped_keyholder(&db).await; + let vault = bootstrapped_vault(&db).await; let mut conn = db.get().await.unwrap(); const ENTITY_ID: &[u8] = b"entity-id-21"; @@ -319,7 +322,7 @@ mod tests { payload: b"payload-v1".to_vec(), }; - sign_entity(&mut conn, &keyholder, &entity, ENTITY_ID) + sign_entity(&mut conn, &vault, &entity, ENTITY_ID) .await .unwrap(); @@ -328,7 +331,7 @@ mod tests { ..entity }; - let err = verify_entity(&mut conn, &keyholder, &tampered, ENTITY_ID) + let err = verify_entity(&mut conn, &vault, &tampered, ENTITY_ID) .await .unwrap_err(); assert!(matches!(err, Error::MacMismatch { .. })); diff --git a/server/crates/arbiter-server/src/evm/mod.rs b/server/crates/arbiter-server/src/evm/mod.rs index 15ac999..0572e1f 100644 --- a/server/crates/arbiter-server/src/evm/mod.rs +++ b/server/crates/arbiter-server/src/evm/mod.rs @@ -11,7 +11,7 @@ use diesel_async::{AsyncConnection, RunQueryDsl}; use kameo::actor::ActorRef; use crate::{ - actors::keyholder::KeyHolder, + actors::vault::Vault, crypto::integrity, db::{ self, DatabaseError, @@ -138,7 +138,7 @@ async fn check_shared_constraints( // Supporting only EIP-1559 transactions for now, but we can easily extend this to support legacy transactions if needed pub struct Engine { db: db::DatabasePool, - keyholder: ActorRef, + vault: ActorRef, } impl Engine { @@ -158,7 +158,7 @@ impl Engine { .map_err(DatabaseError::from)? .ok_or(PolicyError::NoMatchingGrant)?; - integrity::verify_entity(&mut conn, &self.keyholder, &grant.settings, grant.id).await?; + integrity::verify_entity(&mut conn, &self.vault, &grant.settings, grant.id).await?; let mut violations = check_shared_constraints( &context, @@ -207,8 +207,8 @@ impl Engine { } impl Engine { - pub fn new(db: db::DatabasePool, keyholder: ActorRef) -> Self { - Self { db, keyholder } + pub fn new(db: db::DatabasePool, vault: ActorRef) -> Self { + Self { db, vault } } pub async fn create_grant( @@ -219,7 +219,7 @@ impl Engine { P::Settings: Clone, { let mut conn = self.db.get().await?; - let keyholder = self.keyholder.clone(); + let vault = self.vault.clone(); let id = conn .transaction(|conn| { @@ -258,7 +258,7 @@ impl Engine { P::create_grant(&basic_grant, &full_grant.specific, conn).await?; - integrity::sign_entity(conn, &keyholder, &full_grant, basic_grant.id) + integrity::sign_entity(conn, &vault, &full_grant, basic_grant.id) .await .map_err(|_| diesel::result::Error::RollbackTransaction)?; @@ -283,7 +283,7 @@ impl Engine { // Verify integrity of all grants before returning any results for grant in &all_grants { - integrity::verify_entity(conn, &self.keyholder, &grant.settings, grant.id).await?; + integrity::verify_entity(conn, &self.vault, &grant.settings, grant.id).await?; } Ok(all_grants.into_iter().map(|g| Grant { diff --git a/server/crates/arbiter-server/src/grpc/client.rs b/server/crates/arbiter-server/src/grpc/client.rs index 52fa1ea..3a98d3f 100644 --- a/server/crates/arbiter-server/src/grpc/client.rs +++ b/server/crates/arbiter-server/src/grpc/client.rs @@ -10,7 +10,7 @@ use tonic::Status; use tracing::{info, warn}; use crate::{ - actors::client::{ClientConnection, session::ClientSession}, + peers::client::{ClientConnection, session::ClientSession}, grpc::request_tracker::RequestTracker, }; diff --git a/server/crates/arbiter-server/src/grpc/client/auth.rs b/server/crates/arbiter-server/src/grpc/client/auth.rs index 4a7b944..8ac14a5 100644 --- a/server/crates/arbiter-server/src/grpc/client/auth.rs +++ b/server/crates/arbiter-server/src/grpc/client/auth.rs @@ -22,7 +22,7 @@ use tonic::Status; use tracing::warn; use crate::{ - actors::client::{self, ClientConnection, auth}, + peers::client::{self, ClientConnection, auth}, grpc::request_tracker::RequestTracker, }; diff --git a/server/crates/arbiter-server/src/grpc/client/evm.rs b/server/crates/arbiter-server/src/grpc/client/evm.rs index 5b5ba2e..7d58448 100644 --- a/server/crates/arbiter-server/src/grpc/client/evm.rs +++ b/server/crates/arbiter-server/src/grpc/client/evm.rs @@ -16,7 +16,7 @@ use tonic::Status; use tracing::warn; use crate::{ - actors::client::session::{ClientSession, HandleSignTransaction, SignTransactionRpcError}, + peers::client::session::{ClientSession, HandleSignTransaction, SignTransactionRpcError}, grpc::{ Convert, TryConvert, common::inbound::{RawEvmAddress, RawEvmTransaction}, diff --git a/server/crates/arbiter-server/src/grpc/client/vault.rs b/server/crates/arbiter-server/src/grpc/client/vault.rs index b5e98e7..91fc44a 100644 --- a/server/crates/arbiter-server/src/grpc/client/vault.rs +++ b/server/crates/arbiter-server/src/grpc/client/vault.rs @@ -12,9 +12,9 @@ use kameo::{actor::ActorRef, error::SendError}; use tonic::Status; use tracing::warn; -use crate::actors::{ - client::session::{ClientSession, Error, HandleQueryVaultState}, - keyholder::KeyHolderState, +use crate::{ + peers::client::session::{ClientSession, Error, HandleQueryVaultState}, + actors::vault::VaultState, }; pub(super) async fn dispatch( @@ -30,9 +30,9 @@ pub(super) async fn dispatch( match payload { VaultRequestPayload::QueryState(_) => { let state = match actor.ask(HandleQueryVaultState {}).await { - Ok(KeyHolderState::Unbootstrapped) => ProtoVaultState::Unbootstrapped, - Ok(KeyHolderState::Sealed) => ProtoVaultState::Sealed, - Ok(KeyHolderState::Unsealed) => ProtoVaultState::Unsealed, + Ok(VaultState::Unbootstrapped) => ProtoVaultState::Unbootstrapped, + Ok(VaultState::Sealed) => ProtoVaultState::Sealed, + Ok(VaultState::Unsealed) => ProtoVaultState::Unsealed, Err(SendError::HandlerError(Error::Internal)) => ProtoVaultState::Error, Err(err) => { warn!(error = ?err, "Failed to query vault state"); diff --git a/server/crates/arbiter-server/src/grpc/mod.rs b/server/crates/arbiter-server/src/grpc/mod.rs index 7181594..5bd2bf1 100644 --- a/server/crates/arbiter-server/src/grpc/mod.rs +++ b/server/crates/arbiter-server/src/grpc/mod.rs @@ -10,7 +10,7 @@ use tonic::{Request, Response, Status, async_trait}; use tracing::info; use crate::{ - actors::{client::ClientConnection, user_agent::UserAgentConnection}, + peers::{client::ClientConnection, user_agent::UserAgentConnection}, grpc::user_agent::start, }; diff --git a/server/crates/arbiter-server/src/grpc/user_agent.rs b/server/crates/arbiter-server/src/grpc/user_agent.rs index bc00687..a869c24 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent.rs @@ -14,7 +14,7 @@ use tonic::Status; use tracing::{error, info, warn}; use crate::{ - actors::user_agent::{OutOfBand, UserAgentConnection, UserAgentSession}, + peers::user_agent::{OutOfBand, UserAgentConnection, UserAgentSession}, grpc::request_tracker::RequestTracker, }; diff --git a/server/crates/arbiter-server/src/grpc/user_agent/auth.rs b/server/crates/arbiter-server/src/grpc/user_agent/auth.rs index e2625e0..364408e 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent/auth.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent/auth.rs @@ -18,7 +18,7 @@ use tonic::Status; use tracing::warn; use crate::{ - actors::user_agent::{UserAgentConnection, auth}, + peers::user_agent::{UserAgentConnection, auth}, grpc::request_tracker::RequestTracker, }; diff --git a/server/crates/arbiter-server/src/grpc/user_agent/evm.rs b/server/crates/arbiter-server/src/grpc/user_agent/evm.rs index 28725c2..a73880b 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent/evm.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent/evm.rs @@ -23,7 +23,7 @@ use tonic::Status; use tracing::warn; use crate::{ - actors::user_agent::{ + peers::user_agent::{ UserAgentSession, session::connection::{ GrantMutationError, HandleEvmWalletCreate, HandleEvmWalletList, HandleGrantCreate, diff --git a/server/crates/arbiter-server/src/grpc/user_agent/sdk_client.rs b/server/crates/arbiter-server/src/grpc/user_agent/sdk_client.rs index b0d832f..8d25818 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent/sdk_client.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent/sdk_client.rs @@ -21,7 +21,7 @@ use tonic::Status; use tracing::{info, warn}; use crate::{ - actors::user_agent::{ + peers::user_agent::{ OutOfBand, UserAgentSession, session::connection::{ HandleGrantEvmWalletAccess, HandleListWalletAccess, HandleNewClientApprove, diff --git a/server/crates/arbiter-server/src/grpc/user_agent/vault.rs b/server/crates/arbiter-server/src/grpc/user_agent/vault.rs index 8a2940f..f645c2f 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent/vault.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent/vault.rs @@ -20,9 +20,9 @@ use kameo::{actor::ActorRef, error::SendError}; use tonic::Status; use tracing::warn; -use crate::actors::{ - keyholder::KeyHolderState, - user_agent::{ +use crate::{ + actors::vault::VaultState, + peers::user_agent::{ UserAgentSession, session::connection::{ BootstrapError, HandleBootstrapEncryptedKey, HandleQueryVaultState, @@ -166,9 +166,9 @@ async fn handle_query_vault_state( actor: &ActorRef, ) -> Result, Status> { let state = match actor.ask(HandleQueryVaultState {}).await { - Ok(KeyHolderState::Unbootstrapped) => ProtoVaultState::Unbootstrapped, - Ok(KeyHolderState::Sealed) => ProtoVaultState::Sealed, - Ok(KeyHolderState::Unsealed) => ProtoVaultState::Unsealed, + Ok(VaultState::Unbootstrapped) => ProtoVaultState::Unbootstrapped, + Ok(VaultState::Sealed) => ProtoVaultState::Sealed, + Ok(VaultState::Unsealed) => ProtoVaultState::Unsealed, Err(err) => { warn!(error = ?err, "Failed to query vault state"); ProtoVaultState::Error diff --git a/server/crates/arbiter-server/src/lib.rs b/server/crates/arbiter-server/src/lib.rs index 8bb07c8..20444b1 100644 --- a/server/crates/arbiter-server/src/lib.rs +++ b/server/crates/arbiter-server/src/lib.rs @@ -2,6 +2,7 @@ use crate::context::ServerContext; pub mod actors; +pub mod peers; pub mod context; pub mod crypto; pub mod db; diff --git a/server/crates/arbiter-server/src/actors/client/auth.rs b/server/crates/arbiter-server/src/peers/client/auth.rs similarity index 94% rename from server/crates/arbiter-server/src/actors/client/auth.rs rename to server/crates/arbiter-server/src/peers/client/auth.rs index a9ff7c2..e21068e 100644 --- a/server/crates/arbiter-server/src/actors/client/auth.rs +++ b/server/crates/arbiter-server/src/peers/client/auth.rs @@ -14,9 +14,7 @@ use tracing::error; use crate::{ actors::{ - client::{ClientConnection, ClientCredentials, ClientProfile}, - flow_coordinator::{self, RequestClientApproval}, - keyholder::KeyHolder, + GlobalActors, flow_coordinator::{self, RequestClientApproval}, vault::Vault }, crypto::integrity::{self, AttestationStatus}, db::{ @@ -26,6 +24,8 @@ use crate::{ }, }; +use super::{ClientConnection, ClientCredentials, ClientProfile}; + #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] pub enum Error { #[error("Database pool unavailable")] @@ -104,7 +104,7 @@ async fn get_current_nonce_and_id( async fn verify_integrity( db: &db::DatabasePool, - keyholder: &ActorRef, + vault: &ActorRef, pubkey: &authn::PublicKey, ) -> Result<(), Error> { let mut db_conn = db.get().await.map_err(|e| { @@ -119,7 +119,7 @@ async fn verify_integrity( let attestation = integrity::verify_entity( &mut db_conn, - keyholder, + vault, &ClientCredentials { pubkey: pubkey.clone(), nonce, @@ -144,7 +144,7 @@ async fn verify_integrity( /// Returns the new nonce, which is used as the challenge nonce. async fn create_nonce( db: &db::DatabasePool, - keyholder: &ActorRef, + vault: &ActorRef, pubkey: &authn::PublicKey, ) -> Result { let pubkey_bytes = pubkey.to_bytes(); @@ -156,7 +156,7 @@ async fn create_nonce( })?; conn.exclusive_transaction(|conn| { - let keyholder = keyholder.clone(); + let vault = vault.clone(); let pubkey = pubkey.clone(); Box::pin(async move { let (id, new_nonce): (i32, i32) = update(program_client::table) @@ -168,7 +168,7 @@ async fn create_nonce( integrity::sign_entity( conn, - &keyholder, + &vault, &ClientCredentials { pubkey: pubkey.clone(), nonce: new_nonce, @@ -188,7 +188,7 @@ async fn create_nonce( } async fn approve_new_client( - actors: &crate::actors::GlobalActors, + actors: &GlobalActors, profile: ClientProfile, ) -> Result<(), Error> { let result = actors @@ -212,7 +212,7 @@ async fn approve_new_client( async fn insert_client( db: &db::DatabasePool, - keyholder: &ActorRef, + vault: &ActorRef, pubkey: &authn::PublicKey, metadata: &ClientMetadata, ) -> Result { @@ -226,7 +226,7 @@ async fn insert_client( })?; conn.exclusive_transaction(|conn| { - let keyholder = keyholder.clone(); + let vault = vault.clone(); let pubkey = pubkey.clone(); Box::pin(async move { const NONCE_START: i32 = 1; @@ -254,7 +254,7 @@ async fn insert_client( integrity::sign_entity( conn, - &keyholder, + &vault, &ClientCredentials { pubkey: pubkey.clone(), nonce: NONCE_START, @@ -391,7 +391,7 @@ where let client_id = match get_current_nonce_and_id(&props.db, &pubkey).await? { Some((id, _)) => { - verify_integrity(&props.db, &props.actors.key_holder, &pubkey).await?; + verify_integrity(&props.db, &props.actors.vault, &pubkey).await?; id } None => { @@ -403,12 +403,12 @@ where }, ) .await?; - insert_client(&props.db, &props.actors.key_holder, &pubkey, &metadata).await? + insert_client(&props.db, &props.actors.vault, &pubkey, &metadata).await? } }; sync_client_metadata(&props.db, client_id, &metadata).await?; - let challenge_nonce = create_nonce(&props.db, &props.actors.key_holder, &pubkey).await?; + let challenge_nonce = create_nonce(&props.db, &props.actors.vault, &pubkey).await?; challenge_client(transport, pubkey, challenge_nonce).await?; transport diff --git a/server/crates/arbiter-server/src/actors/client/mod.rs b/server/crates/arbiter-server/src/peers/client/mod.rs similarity index 95% rename from server/crates/arbiter-server/src/actors/client/mod.rs rename to server/crates/arbiter-server/src/peers/client/mod.rs index 03b8861..b058b94 100644 --- a/server/crates/arbiter-server/src/actors/client/mod.rs +++ b/server/crates/arbiter-server/src/peers/client/mod.rs @@ -4,9 +4,9 @@ use kameo::actor::Spawn; use tracing::{error, info}; use crate::{ - actors::{GlobalActors, client::session::ClientSession}, + actors::GlobalActors, crypto::integrity::{Integrable, hashing::Hashable}, - db, + db, peers::client::session::ClientSession, }; #[derive(Debug, Clone)] diff --git a/server/crates/arbiter-server/src/actors/client/session.rs b/server/crates/arbiter-server/src/peers/client/session.rs similarity index 89% rename from server/crates/arbiter-server/src/actors/client/session.rs rename to server/crates/arbiter-server/src/peers/client/session.rs index 184c650..d09d844 100644 --- a/server/crates/arbiter-server/src/actors/client/session.rs +++ b/server/crates/arbiter-server/src/peers/client/session.rs @@ -6,15 +6,16 @@ use alloy::{consensus::TxEip1559, primitives::Address, signers::Signature}; use crate::{ actors::{ GlobalActors, - client::ClientConnection, evm::{ClientSignTransaction, SignTransactionError}, flow_coordinator::RegisterClient, - keyholder::KeyHolderState, + vault::VaultState, }, db, evm::VetError, }; +use super::ClientConnection; + pub struct ClientSession { props: ClientConnection, client_id: i32, @@ -29,13 +30,13 @@ impl ClientSession { #[messages] impl ClientSession { #[message] - pub(crate) async fn handle_query_vault_state(&mut self) -> Result { - use crate::actors::keyholder::GetState; + pub(crate) async fn handle_query_vault_state(&mut self) -> Result { + use crate::actors::vault::GetState; - let vault_state = match self.props.actors.key_holder.ask(GetState {}).await { + let vault_state = match self.props.actors.vault.ask(GetState {}).await { Ok(state) => state, Err(err) => { - error!(?err, actor = "client", "keyholder.query.failed"); + error!(?err, actor = "client", "vault.query.failed"); return Err(Error::Internal); } }; diff --git a/server/crates/arbiter-server/src/peers/mod.rs b/server/crates/arbiter-server/src/peers/mod.rs new file mode 100644 index 0000000..c4d3e03 --- /dev/null +++ b/server/crates/arbiter-server/src/peers/mod.rs @@ -0,0 +1,2 @@ +pub mod user_agent; +pub mod client; \ No newline at end of file diff --git a/server/crates/arbiter-server/src/actors/user_agent/auth.rs b/server/crates/arbiter-server/src/peers/user_agent/auth.rs similarity index 96% rename from server/crates/arbiter-server/src/actors/user_agent/auth.rs rename to server/crates/arbiter-server/src/peers/user_agent/auth.rs index 00d2d55..2972d0c 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/auth.rs +++ b/server/crates/arbiter-server/src/peers/user_agent/auth.rs @@ -2,13 +2,11 @@ use arbiter_crypto::authn; use arbiter_proto::transport::Bi; use tracing::error; -use crate::actors::user_agent::{ - UserAgentConnection, - auth::state::{AuthContext, AuthStateMachine}, -}; mod state; use state::*; +use super::UserAgentConnection; + #[derive(Debug, Clone)] pub enum Inbound { AuthChallengeRequest { diff --git a/server/crates/arbiter-server/src/actors/user_agent/auth/state.rs b/server/crates/arbiter-server/src/peers/user_agent/auth/state.rs similarity index 94% rename from server/crates/arbiter-server/src/actors/user_agent/auth/state.rs rename to server/crates/arbiter-server/src/peers/user_agent/auth/state.rs index 60bcf6f..8a3abe6 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/auth/state.rs +++ b/server/crates/arbiter-server/src/peers/user_agent/auth/state.rs @@ -4,13 +4,14 @@ use diesel::{ExpressionMethods as _, OptionalExtension as _, QueryDsl, update}; use diesel_async::{AsyncConnection, RunQueryDsl}; use kameo::actor::ActorRef; use tracing::error; +use super::super::{UserAgentCredentials, UserAgentConnection}; use super::Error; +use crate::peers::user_agent::auth::Outbound; use crate::{ actors::{ bootstrap::ConsumeToken, - keyholder::KeyHolder, - user_agent::{UserAgentConnection, UserAgentCredentials, auth::Outbound}, + vault::Vault, }, crypto::integrity, db::{DatabasePool, schema::useragent_client}, @@ -77,7 +78,7 @@ async fn get_current_nonce_and_id( async fn verify_integrity( db: &DatabasePool, - keyholder: &ActorRef, + vault: &ActorRef, pubkey: &authn::PublicKey, ) -> Result<(), Error> { let mut db_conn = db.get().await.map_err(|e| { @@ -89,7 +90,7 @@ async fn verify_integrity( let _result = integrity::verify_entity( &mut db_conn, - keyholder, + vault, &UserAgentCredentials { pubkey: pubkey.clone(), nonce, @@ -107,7 +108,7 @@ async fn verify_integrity( async fn create_nonce( db: &DatabasePool, - keyholder: &ActorRef, + vault: &ActorRef, pubkey: &authn::PublicKey, ) -> Result { let mut db_conn = db.get().await.map_err(|e| { @@ -130,7 +131,7 @@ async fn create_nonce( integrity::sign_entity( conn, - keyholder, + vault, &UserAgentCredentials { pubkey: pubkey.clone(), nonce: new_nonce, @@ -152,7 +153,7 @@ async fn create_nonce( async fn register_key( db: &DatabasePool, - keyholder: &ActorRef, + vault: &ActorRef, pubkey: &authn::PublicKey, ) -> Result<(), Error> { let pubkey_bytes = pubkey.to_bytes(); @@ -183,7 +184,7 @@ async fn register_key( nonce: NONCE_START, }; - integrity::sign_entity(conn, keyholder, &entity, id) + integrity::sign_entity(conn, vault, &entity, id) .await .map_err(|e| { error!(error = ?e, "Failed to sign integrity tag for new user-agent key"); @@ -219,9 +220,9 @@ where &mut self, ChallengeRequest { pubkey }: ChallengeRequest, ) -> Result { - verify_integrity(&self.conn.db, &self.conn.actors.key_holder, &pubkey).await?; + verify_integrity(&self.conn.db, &self.conn.actors.vault, &pubkey).await?; - let nonce = create_nonce(&self.conn.db, &self.conn.actors.key_holder, &pubkey).await?; + let nonce = create_nonce(&self.conn.db, &self.conn.actors.vault, &pubkey).await?; self.transport .send(Ok(Outbound::AuthChallenge { nonce })) @@ -263,7 +264,7 @@ where match token_ok { true => { - register_key(&self.conn.db, &self.conn.actors.key_holder, &pubkey).await?; + register_key(&self.conn.db, &self.conn.actors.vault, &pubkey).await?; self.transport .send(Ok(Outbound::AuthSuccess)) .await diff --git a/server/crates/arbiter-server/src/actors/user_agent/mod.rs b/server/crates/arbiter-server/src/peers/user_agent/mod.rs similarity index 95% rename from server/crates/arbiter-server/src/actors/user_agent/mod.rs rename to server/crates/arbiter-server/src/peers/user_agent/mod.rs index ac571d9..83fdbbb 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/mod.rs +++ b/server/crates/arbiter-server/src/peers/user_agent/mod.rs @@ -1,7 +1,7 @@ use crate::{ - actors::{GlobalActors, client::ClientProfile}, + actors::GlobalActors, crypto::integrity::Integrable, - db, + db, peers::client::ClientProfile, }; use arbiter_crypto::authn; diff --git a/server/crates/arbiter-server/src/actors/user_agent/session.rs b/server/crates/arbiter-server/src/peers/user_agent/session.rs similarity index 96% rename from server/crates/arbiter-server/src/actors/user_agent/session.rs rename to server/crates/arbiter-server/src/peers/user_agent/session.rs index d3410bd..23a2fb1 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/session.rs +++ b/server/crates/arbiter-server/src/peers/user_agent/session.rs @@ -8,14 +8,12 @@ use kameo::{Actor, actor::ActorRef, messages}; use thiserror::Error; use tracing::error; -use crate::actors::{ - client::ClientProfile, - flow_coordinator::{RegisterUserAgent, client_connect_approval::ClientApprovalController}, - user_agent::{OutOfBand, UserAgentConnection}, -}; +use crate::{actors::flow_coordinator::{RegisterUserAgent, client_connect_approval::ClientApprovalController}, peers::client::ClientProfile}; mod state; use state::{DummyContext, UserAgentEvents, UserAgentStateMachine}; +use super::{OutOfBand, UserAgentConnection}; + #[derive(Debug, Error)] pub enum Error { #[error("State transition failed")] diff --git a/server/crates/arbiter-server/src/actors/user_agent/session/connection.rs b/server/crates/arbiter-server/src/peers/user_agent/session/connection.rs similarity index 93% rename from server/crates/arbiter-server/src/actors/user_agent/session/connection.rs rename to server/crates/arbiter-server/src/peers/user_agent/session/connection.rs index 71f4067..119800f 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/session/connection.rs +++ b/server/crates/arbiter-server/src/peers/user_agent/session/connection.rs @@ -14,28 +14,27 @@ use kameo::prelude::Context; use tracing::{error, info}; use x25519_dalek::{EphemeralSecret, PublicKey}; -use crate::actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer; -use crate::actors::keyholder::KeyHolderState; -use crate::actors::user_agent::session::Error; +use crate::{actors::vault::VaultState, peers::user_agent::session::state::{UnsealContext, UserAgentEvents}}; use crate::actors::{ evm::{ ClientSignTransaction, Generate, ListWallets, SignTransactionError as EvmSignError, UseragentCreateGrant, UseragentListGrants, }, - keyholder::{self, Bootstrap, TryUnseal}, - user_agent::session::{ - UserAgentSession, - state::{UnsealContext, UserAgentEvents, UserAgentStates}, - }, + vault::{self, Bootstrap, TryUnseal}, }; use crate::db::models::{ EvmWalletAccess, NewEvmWalletAccess, ProgramClient, ProgramClientMetadata, }; use crate::evm::policies::{Grant, SpecificGrant}; +use crate::{ + actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer, +}; + +use super::{UserAgentSession, state, Error}; impl UserAgentSession { fn take_unseal_secret(&mut self) -> Result<(EphemeralSecret, PublicKey), Error> { - let UserAgentStates::WaitingForUnsealKey(unseal_context) = self.state.state() else { + let state::UserAgentStates::WaitingForUnsealKey(unseal_context) = self.state.state() else { error!("Received encrypted key in invalid state"); return Err(Error::internal("Invalid state for unseal encrypted key")); }; @@ -184,7 +183,7 @@ impl UserAgentSession { match self .props .actors - .key_holder + .vault .ask(TryUnseal { seal_key_raw: seal_key_buffer, }) @@ -195,17 +194,17 @@ impl UserAgentSession { self.transition(UserAgentEvents::ReceivedValidKey)?; Ok(()) } - Err(SendError::HandlerError(keyholder::Error::InvalidKey)) => { + Err(SendError::HandlerError(vault::Error::InvalidKey)) => { self.transition(UserAgentEvents::ReceivedInvalidKey)?; Err(UnsealError::InvalidKey) } Err(SendError::HandlerError(err)) => { - error!(?err, "Keyholder failed to unseal key"); + error!(?err, "Vault failed to unseal key"); self.transition(UserAgentEvents::ReceivedInvalidKey)?; Err(UnsealError::InvalidKey) } Err(err) => { - error!(?err, "Failed to send unseal request to keyholder"); + error!(?err, "Failed to send unseal request to vault"); self.transition(UserAgentEvents::ReceivedInvalidKey)?; Err(Error::internal("Vault actor error").into()) } @@ -245,7 +244,7 @@ impl UserAgentSession { match self .props .actors - .key_holder + .vault .ask(Bootstrap { seal_key_raw: seal_key_buffer, }) @@ -256,17 +255,17 @@ impl UserAgentSession { self.transition(UserAgentEvents::ReceivedValidKey)?; Ok(()) } - Err(SendError::HandlerError(keyholder::Error::AlreadyBootstrapped)) => { + Err(SendError::HandlerError(vault::Error::AlreadyBootstrapped)) => { self.transition(UserAgentEvents::ReceivedInvalidKey)?; Err(BootstrapError::AlreadyBootstrapped) } Err(SendError::HandlerError(err)) => { - error!(?err, "Keyholder failed to bootstrap vault"); + error!(?err, "Vault failed to bootstrap vault"); self.transition(UserAgentEvents::ReceivedInvalidKey)?; Err(BootstrapError::InvalidKey) } Err(err) => { - error!(?err, "Failed to send bootstrap request to keyholder"); + error!(?err, "Failed to send bootstrap request to vault"); self.transition(UserAgentEvents::ReceivedInvalidKey)?; Err(BootstrapError::General(Error::internal( "Vault actor error", @@ -279,13 +278,13 @@ impl UserAgentSession { #[messages] impl UserAgentSession { #[message] - pub(crate) async fn handle_query_vault_state(&mut self) -> Result { - use crate::actors::keyholder::GetState; + pub(crate) async fn handle_query_vault_state(&mut self) -> Result { + use crate::actors::vault::GetState; - let vault_state = match self.props.actors.key_holder.ask(GetState {}).await { + let vault_state = match self.props.actors.vault.ask(GetState {}).await { Ok(state) => state, Err(err) => { - error!(?err, actor = "useragent", "keyholder.query.failed"); + error!(?err, actor = "useragent", "vault.query.failed"); return Err(Error::internal("Vault is in broken state")); } }; @@ -374,7 +373,7 @@ impl UserAgentSession { // Err(GrantMutationError::Internal) // } // } - let _ = grant_id; + let _ = grant_id; todo!() } diff --git a/server/crates/arbiter-server/src/actors/user_agent/session/state.rs b/server/crates/arbiter-server/src/peers/user_agent/session/state.rs similarity index 100% rename from server/crates/arbiter-server/src/actors/user_agent/session/state.rs rename to server/crates/arbiter-server/src/peers/user_agent/session/state.rs diff --git a/server/crates/arbiter-server/tests/client/auth.rs b/server/crates/arbiter-server/tests/client/auth.rs index a7137e3..80e2652 100644 --- a/server/crates/arbiter-server/tests/client/auth.rs +++ b/server/crates/arbiter-server/tests/client/auth.rs @@ -7,9 +7,10 @@ use arbiter_proto::transport::{Receiver, Sender}; use arbiter_server::{ actors::{ GlobalActors, - client::{ClientConnection, ClientCredentials, auth, connect_client}, - keyholder::Bootstrap, + + vault::Bootstrap, }, + peers::client::{ClientConnection, ClientCredentials, auth, connect_client}, crypto::integrity, db::{self, schema}, }; @@ -58,7 +59,7 @@ async fn insert_registered_client( integrity::sign_entity( &mut conn, - &actors.key_holder, + &actors.vault, &ClientCredentials { pubkey: pubkey.into(), nonce: 1, @@ -103,7 +104,7 @@ async fn spawn_test_actors(db: &db::DatabasePool) -> GlobalActors { let actors = GlobalActors::spawn(db.clone()).await.unwrap(); actors - .key_holder + .vault .ask(Bootstrap { seal_key_raw: SafeCell::new(b"test-seal-key".to_vec()), }) diff --git a/server/crates/arbiter-server/tests/common/mod.rs b/server/crates/arbiter-server/tests/common/mod.rs index c4e6878..17676d4 100644 --- a/server/crates/arbiter-server/tests/common/mod.rs +++ b/server/crates/arbiter-server/tests/common/mod.rs @@ -1,7 +1,7 @@ use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_proto::transport::{Bi, Error, Receiver, Sender}; use arbiter_server::{ - actors::keyholder::KeyHolder, + actors::{GlobalActors, vault::Vault}, db::{self, schema}, }; @@ -11,8 +11,8 @@ use diesel_async::RunQueryDsl; use tokio::sync::mpsc; #[allow(dead_code)] -pub async fn bootstrapped_keyholder(db: &db::DatabasePool) -> KeyHolder { - let mut actor = KeyHolder::new(db.clone()).await.unwrap(); +pub async fn bootstrapped_vault(db: &db::DatabasePool) -> Vault { + let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()).await.unwrap(); actor .bootstrap(SafeCell::new(b"test-seal-key".to_vec())) .await diff --git a/server/crates/arbiter-server/tests/keyholder.rs b/server/crates/arbiter-server/tests/keyholder.rs deleted file mode 100644 index 0fa5692..0000000 --- a/server/crates/arbiter-server/tests/keyholder.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod common; - -#[path = "keyholder/concurrency.rs"] -mod concurrency; -#[path = "keyholder/lifecycle.rs"] -mod lifecycle; -#[path = "keyholder/storage.rs"] -mod storage; diff --git a/server/crates/arbiter-server/tests/user_agent/auth.rs b/server/crates/arbiter-server/tests/user_agent/auth.rs index aeccc8a..31d6786 100644 --- a/server/crates/arbiter-server/tests/user_agent/auth.rs +++ b/server/crates/arbiter-server/tests/user_agent/auth.rs @@ -8,9 +8,10 @@ use arbiter_server::{ actors::{ GlobalActors, bootstrap::GetToken, - keyholder::Bootstrap, - user_agent::{UserAgentConnection, UserAgentCredentials, auth}, + vault::Bootstrap, + }, + peers::user_agent::{UserAgentConnection, UserAgentCredentials, auth}, crypto::integrity, db::{self, schema}, }; @@ -38,7 +39,7 @@ pub async fn test_bootstrap_token_auth() { let db = db::create_test_pool().await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); actors - .key_holder + .vault .ask(Bootstrap { seal_key_raw: SafeCell::new(b"test-seal-key".to_vec()), }) @@ -124,7 +125,7 @@ pub async fn test_challenge_auth() { let db = db::create_test_pool().await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); actors - .key_holder + .vault .ask(Bootstrap { seal_key_raw: SafeCell::new(b"test-seal-key".to_vec()), }) @@ -147,7 +148,7 @@ pub async fn test_challenge_auth() { .unwrap(); integrity::sign_entity( &mut conn, - &actors.key_holder, + &actors.vault, &UserAgentCredentials { pubkey: new_key.verifying_key().into(), nonce: 1, @@ -213,7 +214,7 @@ pub async fn test_challenge_auth_rejects_integrity_tag_mismatch_when_unsealed() let actors = GlobalActors::spawn(db.clone()).await.unwrap(); actors - .key_holder + .vault .ask(Bootstrap { seal_key_raw: SafeCell::new(b"test-seal-key".to_vec()), }) @@ -262,7 +263,7 @@ pub async fn test_challenge_auth_rejects_invalid_signature() { let db = db::create_test_pool().await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); actors - .key_holder + .vault .ask(Bootstrap { seal_key_raw: SafeCell::new(b"test-seal-key".to_vec()), }) @@ -285,7 +286,7 @@ pub async fn test_challenge_auth_rejects_invalid_signature() { .unwrap(); integrity::sign_entity( &mut conn, - &actors.key_holder, + &actors.vault, &UserAgentCredentials { pubkey: new_key.verifying_key().into(), nonce: 1, diff --git a/server/crates/arbiter-server/tests/user_agent/unseal.rs b/server/crates/arbiter-server/tests/user_agent/unseal.rs index 15cf475..59ef627 100644 --- a/server/crates/arbiter-server/tests/user_agent/unseal.rs +++ b/server/crates/arbiter-server/tests/user_agent/unseal.rs @@ -2,12 +2,13 @@ use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_server::{ actors::{ GlobalActors, - keyholder::{Bootstrap, Seal}, - user_agent::{ + vault::{Bootstrap, Seal}, + + }, + peers::user_agent::{ UserAgentSession, session::connection::{HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError}, }, - }, db, }; @@ -22,13 +23,13 @@ async fn setup_sealed_user_agent( let actors = GlobalActors::spawn(db.clone()).await.unwrap(); actors - .key_holder + .vault .ask(Bootstrap { seal_key_raw: SafeCell::new(seal_key.to_vec()), }) .await .unwrap(); - actors.key_holder.ask(Seal).await.unwrap(); + actors.vault.ask(Seal).await.unwrap(); let session = UserAgentSession::spawn(UserAgentSession::new_test(db.clone(), actors)); diff --git a/server/crates/arbiter-server/tests/vault.rs b/server/crates/arbiter-server/tests/vault.rs new file mode 100644 index 0000000..c7640a8 --- /dev/null +++ b/server/crates/arbiter-server/tests/vault.rs @@ -0,0 +1,8 @@ +mod common; + +#[path = "vault/concurrency.rs"] +mod concurrency; +#[path = "vault/lifecycle.rs"] +mod lifecycle; +#[path = "vault/storage.rs"] +mod storage; diff --git a/server/crates/arbiter-server/tests/keyholder/concurrency.rs b/server/crates/arbiter-server/tests/vault/concurrency.rs similarity index 91% rename from server/crates/arbiter-server/tests/keyholder/concurrency.rs rename to server/crates/arbiter-server/tests/vault/concurrency.rs index f128beb..777b203 100644 --- a/server/crates/arbiter-server/tests/keyholder/concurrency.rs +++ b/server/crates/arbiter-server/tests/vault/concurrency.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_server::{ - actors::keyholder::{CreateNew, Error, KeyHolder}, + actors::{GlobalActors, vault::{CreateNew, Error, Vault}}, db::{self, models, schema}, }; @@ -14,7 +14,7 @@ use tokio::task::JoinSet; use crate::common; async fn write_concurrently( - actor: ActorRef, + actor: ActorRef, prefix: &'static str, count: usize, ) -> Vec<(i32, Vec)> { @@ -44,7 +44,7 @@ async fn write_concurrently( #[test_log::test] async fn concurrent_create_new_no_duplicate_nonces_() { let db = db::create_test_pool().await; - let actor = KeyHolder::spawn(common::bootstrapped_keyholder(&db).await); + let actor = Vault::spawn(common::bootstrapped_vault(&db).await); let writes = write_concurrently(actor, "nonce-unique", 32).await; assert_eq!(writes.len(), 32); @@ -66,7 +66,7 @@ async fn concurrent_create_new_no_duplicate_nonces_() { #[test_log::test] async fn concurrent_create_new_root_nonce_never_moves_backward() { let db = db::create_test_pool().await; - let actor = KeyHolder::spawn(common::bootstrapped_keyholder(&db).await); + let actor = Vault::spawn(common::bootstrapped_vault(&db).await); write_concurrently(actor, "root-max", 24).await; @@ -94,7 +94,7 @@ async fn concurrent_create_new_root_nonce_never_moves_backward() { #[test_log::test] async fn insert_failure_does_not_create_partial_row() { let db = db::create_test_pool().await; - let mut actor = common::bootstrapped_keyholder(&db).await; + let mut actor = common::bootstrapped_vault(&db).await; let root_key_history_id = common::root_key_history_id(&db).await; let mut conn = db.get().await.unwrap(); @@ -156,12 +156,12 @@ async fn insert_failure_does_not_create_partial_row() { #[test_log::test] async fn decrypt_roundtrip_after_high_concurrency() { let db = db::create_test_pool().await; - let actor = KeyHolder::spawn(common::bootstrapped_keyholder(&db).await); + let actor = Vault::spawn(common::bootstrapped_vault(&db).await); let writes = write_concurrently(actor, "roundtrip", 40).await; let expected: HashMap> = writes.into_iter().collect(); - let mut decryptor = KeyHolder::new(db.clone()).await.unwrap(); + let mut decryptor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()).await.unwrap(); decryptor .try_unseal(SafeCell::new(b"test-seal-key".to_vec())) .await diff --git a/server/crates/arbiter-server/tests/keyholder/lifecycle.rs b/server/crates/arbiter-server/tests/vault/lifecycle.rs similarity index 76% rename from server/crates/arbiter-server/tests/keyholder/lifecycle.rs rename to server/crates/arbiter-server/tests/vault/lifecycle.rs index bd50b6f..95cc52f 100644 --- a/server/crates/arbiter-server/tests/keyholder/lifecycle.rs +++ b/server/crates/arbiter-server/tests/vault/lifecycle.rs @@ -1,8 +1,12 @@ use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_server::{ - actors::keyholder::{Error, KeyHolder}, + actors::{GlobalActors, vault::{Error, Vault}}, crypto::encryption::v1::{Nonce, ROOT_KEY_TAG}, db::{self, models, schema}, + peers::user_agent::{ + UserAgentSession, + session::connection::{HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError}, + }, }; use diesel::{QueryDsl, SelectableHelper}; @@ -14,7 +18,7 @@ use crate::common; #[test_log::test] async fn test_bootstrap() { let db = db::create_test_pool().await; - let mut actor = KeyHolder::new(db.clone()).await.unwrap(); + let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()).await.unwrap(); let seal_key = SafeCell::new(b"test-seal-key".to_vec()); actor.bootstrap(seal_key).await.unwrap(); @@ -37,7 +41,7 @@ async fn test_bootstrap() { #[test_log::test] async fn test_bootstrap_rejects_double() { let db = db::create_test_pool().await; - let mut actor = common::bootstrapped_keyholder(&db).await; + let mut actor = common::bootstrapped_vault(&db).await; let seal_key2 = SafeCell::new(b"test-seal-key".to_vec()); let err = actor.bootstrap(seal_key2).await.unwrap_err(); @@ -48,7 +52,7 @@ async fn test_bootstrap_rejects_double() { #[test_log::test] async fn test_create_new_before_bootstrap_fails() { let db = db::create_test_pool().await; - let mut actor = KeyHolder::new(db).await.unwrap(); + let mut actor = Vault::new(db, GlobalActors::spawn_message_bus()).await.unwrap(); let err = actor .create_new(SafeCell::new(b"data".to_vec())) @@ -61,7 +65,7 @@ async fn test_create_new_before_bootstrap_fails() { #[test_log::test] async fn test_decrypt_before_bootstrap_fails() { let db = db::create_test_pool().await; - let mut actor = KeyHolder::new(db).await.unwrap(); + let mut actor = Vault::new(db, GlobalActors::spawn_message_bus()).await.unwrap(); let err = actor.decrypt(1).await.unwrap_err(); assert!(matches!(err, Error::NotBootstrapped)); @@ -71,10 +75,10 @@ async fn test_decrypt_before_bootstrap_fails() { #[test_log::test] async fn test_new_restores_sealed_state() { let db = db::create_test_pool().await; - let actor = common::bootstrapped_keyholder(&db).await; + let actor = common::bootstrapped_vault(&db).await; drop(actor); - let mut actor2 = KeyHolder::new(db).await.unwrap(); + let mut actor2 = Vault::new(db, GlobalActors::spawn_message_bus()).await.unwrap(); let err = actor2.decrypt(1).await.unwrap_err(); assert!(matches!(err, Error::NotBootstrapped)); } @@ -83,7 +87,7 @@ async fn test_new_restores_sealed_state() { #[test_log::test] async fn test_unseal_correct_password() { let db = db::create_test_pool().await; - let mut actor = common::bootstrapped_keyholder(&db).await; + let mut actor = common::bootstrapped_vault(&db).await; let plaintext = b"survive a restart"; let aead_id = actor @@ -92,7 +96,7 @@ async fn test_unseal_correct_password() { .unwrap(); drop(actor); - let mut actor = KeyHolder::new(db.clone()).await.unwrap(); + let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()).await.unwrap(); let seal_key = SafeCell::new(b"test-seal-key".to_vec()); actor.try_unseal(seal_key).await.unwrap(); @@ -104,7 +108,7 @@ async fn test_unseal_correct_password() { #[test_log::test] async fn test_unseal_wrong_then_correct_password() { let db = db::create_test_pool().await; - let mut actor = common::bootstrapped_keyholder(&db).await; + let mut actor = common::bootstrapped_vault(&db).await; let plaintext = b"important data"; let aead_id = actor @@ -113,7 +117,7 @@ async fn test_unseal_wrong_then_correct_password() { .unwrap(); drop(actor); - let mut actor = KeyHolder::new(db.clone()).await.unwrap(); + let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus()).await.unwrap(); let bad_key = SafeCell::new(b"wrong-password".to_vec()); let err = actor.try_unseal(bad_key).await.unwrap_err(); diff --git a/server/crates/arbiter-server/tests/keyholder/storage.rs b/server/crates/arbiter-server/tests/vault/storage.rs similarity index 91% rename from server/crates/arbiter-server/tests/keyholder/storage.rs rename to server/crates/arbiter-server/tests/vault/storage.rs index 71ebccf..ae6d3d3 100644 --- a/server/crates/arbiter-server/tests/keyholder/storage.rs +++ b/server/crates/arbiter-server/tests/vault/storage.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use arbiter_server::{ - actors::keyholder::Error, + actors::vault::Error, crypto::encryption::v1::Nonce, db::{self, models, schema}, }; @@ -16,7 +16,7 @@ use crate::common; #[test_log::test] async fn test_create_decrypt_roundtrip() { let db = db::create_test_pool().await; - let mut actor = common::bootstrapped_keyholder(&db).await; + let mut actor = common::bootstrapped_vault(&db).await; let plaintext = b"hello arbiter"; let aead_id = actor @@ -32,7 +32,7 @@ async fn test_create_decrypt_roundtrip() { #[test_log::test] async fn test_decrypt_nonexistent_returns_not_found() { let db = db::create_test_pool().await; - let mut actor = common::bootstrapped_keyholder(&db).await; + let mut actor = common::bootstrapped_vault(&db).await; let err = actor.decrypt(9999).await.unwrap_err(); assert!(matches!(err, Error::NotFound)); @@ -42,7 +42,7 @@ async fn test_decrypt_nonexistent_returns_not_found() { #[test_log::test] async fn test_ciphertext_differs_across_entries() { let db = db::create_test_pool().await; - let mut actor = common::bootstrapped_keyholder(&db).await; + let mut actor = common::bootstrapped_vault(&db).await; let plaintext = b"same content"; let id1 = actor @@ -80,7 +80,7 @@ async fn test_ciphertext_differs_across_entries() { #[test_log::test] async fn test_nonce_never_reused() { let db = db::create_test_pool().await; - let mut actor = common::bootstrapped_keyholder(&db).await; + let mut actor = common::bootstrapped_vault(&db).await; let n = 5; for i in 0..n { @@ -124,7 +124,7 @@ async fn test_nonce_never_reused() { #[test_log::test] async fn broken_db_nonce_format_fails_closed() { let db = db::create_test_pool().await; - let mut actor = common::bootstrapped_keyholder(&db).await; + let mut actor = common::bootstrapped_vault(&db).await; let root_key_history_id = common::root_key_history_id(&db).await; let mut conn = db.get().await.unwrap(); @@ -145,7 +145,7 @@ async fn broken_db_nonce_format_fails_closed() { assert!(matches!(err, Error::BrokenDatabase)); let db = db::create_test_pool().await; - let mut actor = common::bootstrapped_keyholder(&db).await; + let mut actor = common::bootstrapped_vault(&db).await; let id = actor .create_new(SafeCell::new(b"decrypt target".to_vec())) .await