diff --git a/server/crates/arbiter-server/src/actors/evm/mod.rs b/server/crates/arbiter-server/src/actors/evm/mod.rs index ce3f177..05a9d94 100644 --- a/server/crates/arbiter-server/src/actors/evm/mod.rs +++ b/server/crates/arbiter-server/src/actors/evm/mod.rs @@ -4,14 +4,13 @@ use diesel::{ }; use diesel_async::RunQueryDsl; use kameo::{Actor, actor::ActorRef, messages}; -use memsafe::MemSafe; use rand::{SeedableRng, rng, rngs::StdRng}; use crate::{ actors::keyholder::{CreateNew, Decrypt, KeyHolder}, db::{ self, DatabasePool, - models::{self, EvmBasicGrant, SqliteTimestamp}, + models::{self, SqliteTimestamp}, schema, }, evm::{ @@ -21,6 +20,7 @@ use crate::{ ether_transfer::EtherTransfer, token_transfers::TokenTransfer, }, }, + safe_cell::{SafeCell, SafeCellHandle as _}, }; pub use crate::evm::safe_signer; @@ -110,8 +110,8 @@ impl EvmActor { // Move raw key bytes into a Vec MemSafe for KeyHolder let plaintext = { - let reader = key_cell.read().expect("MemSafe read"); - MemSafe::new(reader.to_vec()).expect("MemSafe allocation") + let reader = key_cell.read(); + SafeCell::new(reader.to_vec()) }; let aead_id: i32 = self @@ -249,7 +249,7 @@ impl EvmActor { .ok_or(SignTransactionError::WalletNotFound)?; drop(conn); - let raw_key: MemSafe> = self + let raw_key: SafeCell> = self .keyholder .ask(Decrypt { aead_id: wallet.aead_encrypted_id, @@ -257,7 +257,7 @@ impl EvmActor { .await .map_err(|_| SignTransactionError::KeyholderSend)?; - let signer = safe_signer::SafeSigner::from_memsafe(raw_key)?; + let signer = safe_signer::SafeSigner::from_cell(raw_key)?; self.engine .evaluate_transaction( diff --git a/server/crates/arbiter-server/src/actors/keyholder/encryption/v1.rs b/server/crates/arbiter-server/src/actors/keyholder/encryption/v1.rs index fdc9727..9ca5a45 100644 --- a/server/crates/arbiter-server/src/actors/keyholder/encryption/v1.rs +++ b/server/crates/arbiter-server/src/actors/keyholder/encryption/v1.rs @@ -5,12 +5,13 @@ use chacha20poly1305::{ AeadInPlace, Key, KeyInit as _, XChaCha20Poly1305, XNonce, aead::{AeadMut, Error, Payload}, }; -use memsafe::MemSafe; use rand::{ Rng as _, SeedableRng, rngs::{StdRng, SysRng}, }; +use crate::safe_cell::{SafeCell, SafeCellHandle as _}; + pub const ROOT_KEY_TAG: &[u8] = "arbiter/seal/v1".as_bytes(); pub const TAG: &[u8] = "arbiter/private-key/v1".as_bytes(); @@ -47,23 +48,23 @@ impl<'a> TryFrom<&'a [u8]> for Nonce { } } -pub struct KeyCell(pub MemSafe); -impl From> for KeyCell { - fn from(value: MemSafe) -> Self { +pub struct KeyCell(pub SafeCell); +impl From> for KeyCell { + fn from(value: SafeCell) -> Self { Self(value) } } -impl TryFrom>> for KeyCell { +impl TryFrom>> for KeyCell { type Error = (); - fn try_from(mut value: MemSafe>) -> Result { - let value = value.read().unwrap(); + fn try_from(mut value: SafeCell>) -> Result { + let value = value.read(); if value.len() != size_of::() { return Err(()); } - let mut cell = MemSafe::new(Key::default()).unwrap(); + let mut cell = SafeCell::new(Key::default()); { - let mut cell_write = cell.write().unwrap(); + let mut cell_write = cell.write(); let cell_slice: &mut [u8] = cell_write.as_mut(); cell_slice.copy_from_slice(&value); } @@ -73,9 +74,9 @@ impl TryFrom>> for KeyCell { impl KeyCell { pub fn new_secure_random() -> Self { - let mut key = MemSafe::new(Key::default()).unwrap(); + let mut key = SafeCell::new(Key::default()); { - let mut key_buffer = key.write().unwrap(); + let mut key_buffer = key.write(); let key_buffer: &mut [u8] = key_buffer.as_mut(); let mut rng = StdRng::try_from_rng(&mut SysRng).unwrap(); @@ -91,7 +92,7 @@ impl KeyCell { associated_data: &[u8], mut buffer: impl AsMut>, ) -> Result<(), Error> { - let key_reader = self.0.read().unwrap(); + let key_reader = self.0.read(); let key_ref = key_reader.deref(); let cipher = XChaCha20Poly1305::new(key_ref); let nonce = XNonce::from_slice(nonce.0.as_ref()); @@ -102,13 +103,13 @@ impl KeyCell { &mut self, nonce: &Nonce, associated_data: &[u8], - buffer: &mut MemSafe>, + buffer: &mut SafeCell>, ) -> Result<(), Error> { - let key_reader = self.0.read().unwrap(); + let key_reader = self.0.read(); let key_ref = key_reader.deref(); let cipher = XChaCha20Poly1305::new(key_ref); let nonce = XNonce::from_slice(nonce.0.as_ref()); - let mut buffer = buffer.write().unwrap(); + let mut buffer = buffer.write(); let buffer: &mut Vec = buffer.as_mut(); cipher.decrypt_in_place(nonce, associated_data, buffer) } @@ -119,7 +120,7 @@ impl KeyCell { associated_data: &[u8], plaintext: impl AsRef<[u8]>, ) -> Result, Error> { - let key_reader = self.0.read().unwrap(); + let key_reader = self.0.read(); let key_ref = key_reader.deref(); let mut cipher = XChaCha20Poly1305::new(key_ref); let nonce = XNonce::from_slice(nonce.0.as_ref()); @@ -146,13 +147,13 @@ pub 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 fn derive_seal_key(mut password: MemSafe>, salt: &Salt) -> KeyCell { +pub fn derive_seal_key(mut password: SafeCell>, 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(); + let mut key = SafeCell::new(Key::default()); { - let password_source = password.read().unwrap(); - let mut key_buffer = key.write().unwrap(); + let password_source = password.read(); + let mut key_buffer = key.write(); let key_buffer: &mut [u8] = key_buffer.as_mut(); hasher @@ -166,20 +167,20 @@ pub fn derive_seal_key(mut password: MemSafe>, salt: &Salt) -> KeyCell { #[cfg(test)] mod tests { use super::*; - use memsafe::MemSafe; + use crate::safe_cell::SafeCell; #[test] pub fn derive_seal_key_deterministic() { static PASSWORD: &[u8] = b"password"; - let password = MemSafe::new(PASSWORD.to_vec()).unwrap(); - let password2 = MemSafe::new(PASSWORD.to_vec()).unwrap(); + let password = SafeCell::new(PASSWORD.to_vec()); + let password2 = SafeCell::new(PASSWORD.to_vec()); let salt = generate_salt(); let mut key1 = derive_seal_key(password, &salt); let mut key2 = derive_seal_key(password2, &salt); - let key1_reader = key1.0.read().unwrap(); - let key2_reader = key2.0.read().unwrap(); + let key1_reader = key1.0.read(); + let key2_reader = key2.0.read(); assert_eq!(key1_reader.deref(), key2_reader.deref()); } @@ -187,11 +188,11 @@ mod tests { #[test] pub fn successful_derive() { static PASSWORD: &[u8] = b"password"; - let password = MemSafe::new(PASSWORD.to_vec()).unwrap(); + let password = SafeCell::new(PASSWORD.to_vec()); let salt = generate_salt(); let mut key = derive_seal_key(password, &salt); - let key_reader = key.0.read().unwrap(); + let key_reader = key.0.read(); let key_ref = key_reader.deref(); assert_ne!(key_ref.as_slice(), &[0u8; 32][..]); @@ -200,7 +201,7 @@ mod tests { #[test] pub fn encrypt_decrypt() { static PASSWORD: &[u8] = b"password"; - let password = MemSafe::new(PASSWORD.to_vec()).unwrap(); + let password = SafeCell::new(PASSWORD.to_vec()); let salt = generate_salt(); let mut key = derive_seal_key(password, &salt); @@ -212,12 +213,12 @@ mod tests { .unwrap(); assert_ne!(buffer, b"secret data"); - let mut buffer = MemSafe::new(buffer).unwrap(); + let mut buffer = SafeCell::new(buffer); key.decrypt_in_place(&nonce, associated_data, &mut buffer) .unwrap(); - let buffer = buffer.read().unwrap(); + let buffer = buffer.read(); assert_eq!(*buffer, b"secret data"); } diff --git a/server/crates/arbiter-server/src/actors/keyholder/mod.rs b/server/crates/arbiter-server/src/actors/keyholder/mod.rs index f7cc8cc..e53f6a3 100644 --- a/server/crates/arbiter-server/src/actors/keyholder/mod.rs +++ b/server/crates/arbiter-server/src/actors/keyholder/mod.rs @@ -5,15 +5,15 @@ use diesel::{ }; use diesel_async::{AsyncConnection, RunQueryDsl}; use kameo::{Actor, Reply, messages}; -use memsafe::MemSafe; use strum::{EnumDiscriminants, IntoDiscriminant}; use tracing::{error, info}; -use crate::db::{ +use crate::{db::{ self, models::{self, RootKeyHistory}, schema::{self}, -}; +}, safe_cell::SafeCellHandle as _}; +use crate::safe_cell::SafeCell; use encryption::v1::{self, KeyCell, Nonce}; pub mod encryption; @@ -136,7 +136,7 @@ impl KeyHolder { } #[message] - pub async fn bootstrap(&mut self, seal_key_raw: MemSafe>) -> Result<(), Error> { + pub async fn bootstrap(&mut self, seal_key_raw: SafeCell>) -> Result<(), Error> { if !matches!(self.state, State::Unbootstrapped) { return Err(Error::AlreadyBootstrapped); } @@ -149,7 +149,7 @@ impl KeyHolder { let data_encryption_nonce = v1::Nonce::default(); let root_key_ciphertext: Vec = { - let root_key_reader = root_key.0.read().unwrap(); + let root_key_reader = root_key.0.read(); let root_key_reader = root_key_reader.as_slice(); seal_key .encrypt(&root_key_nonce, v1::ROOT_KEY_TAG, root_key_reader) @@ -199,7 +199,7 @@ impl KeyHolder { } #[message] - pub async fn try_unseal(&mut self, seal_key_raw: MemSafe>) -> Result<(), Error> { + pub async fn try_unseal(&mut self, seal_key_raw: SafeCell>) -> Result<(), Error> { let State::Sealed { root_key_history_id, } = &self.state @@ -225,7 +225,7 @@ impl KeyHolder { })?; let mut seal_key = v1::derive_seal_key(seal_key_raw, &salt); - let mut root_key = MemSafe::new(current_key.ciphertext.clone()).unwrap(); + let mut root_key = SafeCell::new(current_key.ciphertext.clone()); let nonce = v1::Nonce::try_from(current_key.root_key_encryption_nonce.as_slice()).map_err( |_| { @@ -256,7 +256,7 @@ impl KeyHolder { // Decrypts the `aead_encrypted` entry with the given ID and returns the plaintext #[message] - pub async fn decrypt(&mut self, aead_id: i32) -> Result>, Error> { + pub async fn decrypt(&mut self, aead_id: i32) -> Result>, Error> { let State::Unsealed { root_key, .. } = &mut self.state else { return Err(Error::NotBootstrapped); }; @@ -279,14 +279,14 @@ impl KeyHolder { ); Error::BrokenDatabase })?; - let mut output = MemSafe::new(row.ciphertext).unwrap(); + let mut output = SafeCell::new(row.ciphertext); root_key.decrypt_in_place(&nonce, v1::TAG, &mut output)?; Ok(output) } // Creates new `aead_encrypted` entry in the database and returns it's ID #[message] - pub async fn create_new(&mut self, mut plaintext: MemSafe>) -> Result { + pub async fn create_new(&mut self, mut plaintext: SafeCell>) -> Result { let State::Unsealed { root_key, root_key_history_id, @@ -299,7 +299,7 @@ impl KeyHolder { // Borrow checker note: &mut borrow a few lines above is disjoint from this field let nonce = Self::get_new_nonce(&self.db, *root_key_history_id).await?; - let mut ciphertext_buffer = plaintext.write().unwrap(); + let mut ciphertext_buffer = plaintext.write(); let ciphertext_buffer: &mut Vec = ciphertext_buffer.as_mut(); root_key.encrypt_in_place(&nonce, v1::TAG, &mut *ciphertext_buffer)?; @@ -348,15 +348,14 @@ mod tests { use diesel::SelectableHelper; use diesel_async::RunQueryDsl; - use memsafe::MemSafe; - use crate::db::{self}; + use crate::{db::{self}, safe_cell::SafeCell}; use super::*; async fn bootstrapped_actor(db: &db::DatabasePool) -> KeyHolder { let mut actor = KeyHolder::new(db.clone()).await.unwrap(); - let seal_key = MemSafe::new(b"test-seal-key".to_vec()).unwrap(); + let seal_key = SafeCell::new(b"test-seal-key".to_vec()); actor.bootstrap(seal_key).await.unwrap(); actor } @@ -391,7 +390,7 @@ mod tests { assert_eq!(root_row.data_encryption_nonce, n2.to_vec()); let id = actor - .create_new(MemSafe::new(b"post-interleave".to_vec()).unwrap()) + .create_new(SafeCell::new(b"post-interleave".to_vec())) .await .unwrap(); let row: models::AeadEncrypted = schema::aead_encrypted::table diff --git a/server/crates/arbiter-server/src/actors/user_agent/session.rs b/server/crates/arbiter-server/src/actors/user_agent/session.rs index 9fe8d91..d568cc5 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/session.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/session.rs @@ -1,5 +1,3 @@ - -use chacha20poly1305::aead::KeyInit; use ed25519_dalek::VerifyingKey; use kameo::{Actor, messages, prelude::Context}; use tokio::{select, sync::watch}; diff --git a/server/crates/arbiter-server/src/actors/user_agent/session/connection.rs b/server/crates/arbiter-server/src/actors/user_agent/session/connection.rs index e303ef6..984f464 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/session/connection.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/session/connection.rs @@ -2,13 +2,11 @@ use std::{ops::DerefMut, sync::Mutex}; use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit}; use kameo::error::SendError; -use memsafe::MemSafe; use tracing::{error, info}; use x25519_dalek::{EphemeralSecret, PublicKey}; -use crate::actors::{ - evm::{Generate, ListWallets, UseragentListGrants}, - evm::{UseragentCreateGrant, UseragentDeleteGrant}, +use crate::{actors::{ + evm::{Generate, ListWallets, UseragentCreateGrant, UseragentDeleteGrant, UseragentListGrants}, keyholder::{self, Bootstrap, TryUnseal}, user_agent::{ BootstrapError, Request, Response, TransportResponseError, UnsealError, VaultState, @@ -17,7 +15,8 @@ use crate::actors::{ state::{UnsealContext, UserAgentEvents, UserAgentStates}, }, }, -}; +}, safe_cell::SafeCellHandle as _}; +use crate::safe_cell::SafeCell; impl UserAgentSession { pub async fn process_transport_inbound(&mut self, req: Request) -> Output { @@ -93,16 +92,16 @@ impl UserAgentSession { nonce: &[u8], ciphertext: &[u8], associated_data: &[u8], - ) -> Result>, ()> { + ) -> Result>, ()> { let nonce = XNonce::from_slice(nonce); let shared_secret = ephemeral_secret.diffie_hellman(&client_public_key); let cipher = XChaCha20Poly1305::new(shared_secret.as_bytes().into()); - let mut key_buffer = MemSafe::new(ciphertext.to_vec()).unwrap(); + let mut key_buffer = SafeCell::new(ciphertext.to_vec()); let decryption_result = { - let mut write_handle = key_buffer.write().unwrap(); + let mut write_handle = key_buffer.write(); let write_handle = write_handle.deref_mut(); cipher.decrypt_in_place(nonce, associated_data, write_handle) }; diff --git a/server/crates/arbiter-server/src/evm/safe_signer.rs b/server/crates/arbiter-server/src/evm/safe_signer.rs index fd39189..124a248 100644 --- a/server/crates/arbiter-server/src/evm/safe_signer.rs +++ b/server/crates/arbiter-server/src/evm/safe_signer.rs @@ -8,7 +8,7 @@ use alloy::{ }; use async_trait::async_trait; use k256::ecdsa::{self, RecoveryId, SigningKey, signature::hazmat::PrehashSigner}; -use memsafe::MemSafe; +use crate::safe_cell::{SafeCell, SafeCellHandle as _}; /// An Ethereum signer that stores its secp256k1 secret key inside a /// hardware-protected [`MemSafe`] cell. @@ -20,7 +20,7 @@ use memsafe::MemSafe; /// Because [`MemSafe::read`] requires `&mut self` while the [`Signer`] trait /// requires `&self`, the cell is wrapped in a [`Mutex`]. pub struct SafeSigner { - key: Mutex>, + key: Mutex>, address: Address, chain_id: Option, } @@ -42,14 +42,14 @@ impl std::fmt::Debug for SafeSigner { /// rejection, but we retry to be correct). /// /// Returns the protected key bytes and the derived Ethereum address. -pub fn generate(rng: &mut impl rand::Rng) -> (MemSafe<[u8; 32]>, Address) { +pub fn generate(rng: &mut impl rand::Rng) -> (SafeCell<[u8; 32]>, Address) { loop { - let mut cell = MemSafe::new([0u8; 32]).expect("MemSafe allocation"); + let mut cell = SafeCell::new([0u8; 32]); { - let mut w = cell.write().expect("MemSafe write"); + let mut w = cell.write(); rng.fill_bytes(w.as_mut()); } - let reader = cell.read().expect("MemSafe read"); + let reader = cell.read(); if let Ok(sk) = SigningKey::from_slice(reader.as_ref()) { let address = secret_key_to_address(&sk); drop(reader); @@ -64,8 +64,8 @@ impl SafeSigner { /// The key bytes are read from protected memory, parsed as a secp256k1 /// scalar, and immediately moved into a new [`MemSafe`] cell. The raw /// bytes are never exposed outside this function. - pub fn from_memsafe(mut cell: MemSafe>) -> Result { - let reader = cell.read().map_err(Error::other)?; + pub fn from_cell(mut cell: SafeCell>) -> Result { + let reader = cell.read(); let sk = SigningKey::from_slice(reader.as_slice()).map_err(Error::other)?; drop(reader); Self::new(sk) @@ -75,7 +75,7 @@ impl SafeSigner { /// memory region. pub fn new(key: SigningKey) -> Result { let address = secret_key_to_address(&key); - let cell = MemSafe::new(key).map_err(Error::other)?; + let cell = SafeCell::new(key); Ok(Self { key: Mutex::new(cell), address, @@ -85,7 +85,7 @@ impl SafeSigner { fn sign_hash_inner(&self, hash: &B256) -> Result { let mut cell = self.key.lock().expect("SafeSigner mutex poisoned"); - let reader = cell.read().map_err(Error::other)?; + let reader = cell.read(); let sig: (ecdsa::Signature, RecoveryId) = reader.sign_prehash(hash.as_ref())?; Ok(sig.into()) } diff --git a/server/crates/arbiter-server/src/grpc/user_agent.rs b/server/crates/arbiter-server/src/grpc/user_agent.rs index a309b52..39c6afb 100644 --- a/server/crates/arbiter-server/src/grpc/user_agent.rs +++ b/server/crates/arbiter-server/src/grpc/user_agent.rs @@ -6,7 +6,6 @@ use arbiter_proto::{ EvmGrantCreateRequest, EvmGrantCreateResponse, EvmGrantDeleteRequest, EvmGrantDeleteResponse, EvmGrantList, EvmGrantListResponse, GrantEntry, SharedSettings as ProtoSharedSettings, SpecificGrant as ProtoSpecificGrant, - SpecificGrant as ProtoGrantSpecificGrant, TokenTransferSettings as ProtoTokenTransferSettings, VolumeRateLimit as ProtoVolumeRateLimit, WalletCreateResponse, WalletEntry, WalletList, WalletListResponse, evm_grant_create_response::Result as EvmGrantCreateResult, @@ -42,7 +41,6 @@ use crate::{ TransportResponseError, UnsealError, VaultState, }, evm::{ - self, policies::{Grant, SpecificGrant}, policies::{ SharedGrantSettings, TransactionRateLimit, VolumeRateLimit, ether_transfer, diff --git a/server/crates/arbiter-server/src/lib.rs b/server/crates/arbiter-server/src/lib.rs index 70f1f80..410e499 100644 --- a/server/crates/arbiter-server/src/lib.rs +++ b/server/crates/arbiter-server/src/lib.rs @@ -12,6 +12,7 @@ pub mod context; pub mod db; pub mod evm; pub mod grpc; +pub mod safe_cell; const DEFAULT_CHANNEL_SIZE: usize = 1000; @@ -25,4 +26,3 @@ impl Server { } } - diff --git a/server/crates/arbiter-server/src/safe_cell.rs b/server/crates/arbiter-server/src/safe_cell.rs new file mode 100644 index 0000000..056e131 --- /dev/null +++ b/server/crates/arbiter-server/src/safe_cell.rs @@ -0,0 +1,79 @@ +use std::ops::{Deref, DerefMut}; +use std::{any::type_name, fmt}; + +use memsafe::MemSafe; + +pub trait SafeCellHandle { + type CellRead<'a>: Deref + where + Self: 'a, + T: 'a; + type CellWrite<'a>: Deref + DerefMut + where + Self: 'a, + T: 'a; + + fn new(value: T) -> Self + where + Self: Sized; + + fn read(&mut self) -> Self::CellRead<'_>; + fn write(&mut self) -> Self::CellWrite<'_>; +} + +pub struct MemSafeCell(MemSafe); + +impl fmt::Debug for MemSafeCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MemSafeCell") + .field("inner", &format_args!("", type_name::())) + .finish() + } +} + +impl SafeCellHandle for MemSafeCell { + type CellRead<'a> + = memsafe::MemSafeRead<'a, T> + where + Self: 'a, + T: 'a; + type CellWrite<'a> + = memsafe::MemSafeWrite<'a, T> + where + Self: 'a, + T: 'a; + + fn new(value: T) -> Self { + match MemSafe::new(value) { + Ok(inner) => Self(inner), + Err(err) => { + // If protected memory cannot be allocated, process integrity is compromised. + abort_memory_breach("safe cell allocation", &err) + } + } + } + + fn read(&mut self) -> Self::CellRead<'_> { + match self.0.read() { + Ok(inner) => inner, + Err(err) => abort_memory_breach("safe cell read", &err), + } + } + + fn write(&mut self) -> Self::CellWrite<'_> { + match self.0.write() { + Ok(inner) => inner, + Err(err) => { + // If protected memory becomes unwritable here, treat it as a fatal memory breach. + abort_memory_breach("safe cell write", &err) + } + } + } +} + +fn abort_memory_breach(action: &str, err: &memsafe::error::MemoryError) -> ! { + eprintln!("fatal {action}: {err}"); + std::process::abort(); +} + +pub type SafeCell = MemSafeCell; diff --git a/server/crates/arbiter-server/tests/common/mod.rs b/server/crates/arbiter-server/tests/common/mod.rs index 7fb5bac..3bc3430 100644 --- a/server/crates/arbiter-server/tests/common/mod.rs +++ b/server/crates/arbiter-server/tests/common/mod.rs @@ -1,19 +1,18 @@ use arbiter_proto::transport::{Bi, Error}; use arbiter_server::{ actors::keyholder::KeyHolder, - db::{self, schema}, + db::{self, schema}, safe_cell::{SafeCell, SafeCellHandle as _}, }; use async_trait::async_trait; use diesel::QueryDsl; use diesel_async::RunQueryDsl; -use memsafe::MemSafe; 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(); actor - .bootstrap(MemSafe::new(b"test-seal-key".to_vec()).unwrap()) + .bootstrap(SafeCell::new(b"test-seal-key".to_vec())) .await .unwrap(); actor diff --git a/server/crates/arbiter-server/tests/keyholder/concurrency.rs b/server/crates/arbiter-server/tests/keyholder/concurrency.rs index d34e315..7dbe669 100644 --- a/server/crates/arbiter-server/tests/keyholder/concurrency.rs +++ b/server/crates/arbiter-server/tests/keyholder/concurrency.rs @@ -3,11 +3,11 @@ use std::collections::{HashMap, HashSet}; use arbiter_server::{ actors::keyholder::{CreateNew, Error, KeyHolder}, db::{self, models, schema}, + safe_cell::{SafeCell, SafeCellHandle as _}, }; use diesel::{ExpressionMethods as _, QueryDsl, SelectableHelper, dsl::sql_query}; use diesel_async::RunQueryDsl; use kameo::actor::{ActorRef, Spawn as _}; -use memsafe::MemSafe; use tokio::task::JoinSet; use crate::common; @@ -24,7 +24,7 @@ async fn write_concurrently( let plaintext = format!("{prefix}-{i}").into_bytes(); let id = actor .ask(CreateNew { - plaintext: MemSafe::new(plaintext.clone()).unwrap(), + plaintext: SafeCell::new(plaintext.clone()), }) .await .unwrap(); @@ -118,7 +118,7 @@ async fn insert_failure_does_not_create_partial_row() { drop(conn); let err = actor - .create_new(MemSafe::new(b"should fail".to_vec()).unwrap()) + .create_new(SafeCell::new(b"should fail".to_vec())) .await .unwrap_err(); assert!(matches!(err, Error::DatabaseTransaction(_))); @@ -162,12 +162,12 @@ async fn decrypt_roundtrip_after_high_concurrency() { let mut decryptor = KeyHolder::new(db.clone()).await.unwrap(); decryptor - .try_unseal(MemSafe::new(b"test-seal-key".to_vec()).unwrap()) + .try_unseal(SafeCell::new(b"test-seal-key".to_vec())) .await .unwrap(); for (id, plaintext) in expected { let mut decrypted = decryptor.decrypt(id).await.unwrap(); - assert_eq!(*decrypted.read().unwrap(), plaintext); + assert_eq!(*decrypted.read(), plaintext); } } diff --git a/server/crates/arbiter-server/tests/keyholder/lifecycle.rs b/server/crates/arbiter-server/tests/keyholder/lifecycle.rs index 2cf1f55..e0023d3 100644 --- a/server/crates/arbiter-server/tests/keyholder/lifecycle.rs +++ b/server/crates/arbiter-server/tests/keyholder/lifecycle.rs @@ -1,10 +1,10 @@ use arbiter_server::{ actors::keyholder::{Error, KeyHolder}, db::{self, models, schema}, + safe_cell::{SafeCell, SafeCellHandle as _}, }; use diesel::{QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; -use memsafe::MemSafe; use crate::common; @@ -14,7 +14,7 @@ async fn test_bootstrap() { let db = db::create_test_pool().await; let mut actor = KeyHolder::new(db.clone()).await.unwrap(); - let seal_key = MemSafe::new(b"test-seal-key".to_vec()).unwrap(); + let seal_key = SafeCell::new(b"test-seal-key".to_vec()); actor.bootstrap(seal_key).await.unwrap(); let mut conn = db.get().await.unwrap(); @@ -43,7 +43,7 @@ async fn test_bootstrap_rejects_double() { let db = db::create_test_pool().await; let mut actor = common::bootstrapped_keyholder(&db).await; - let seal_key2 = MemSafe::new(b"test-seal-key".to_vec()).unwrap(); + let seal_key2 = SafeCell::new(b"test-seal-key".to_vec()); let err = actor.bootstrap(seal_key2).await.unwrap_err(); assert!(matches!(err, Error::AlreadyBootstrapped)); } @@ -55,7 +55,7 @@ async fn test_create_new_before_bootstrap_fails() { let mut actor = KeyHolder::new(db).await.unwrap(); let err = actor - .create_new(MemSafe::new(b"data".to_vec()).unwrap()) + .create_new(SafeCell::new(b"data".to_vec())) .await .unwrap_err(); assert!(matches!(err, Error::NotBootstrapped)); @@ -91,17 +91,17 @@ async fn test_unseal_correct_password() { let plaintext = b"survive a restart"; let aead_id = actor - .create_new(MemSafe::new(plaintext.to_vec()).unwrap()) + .create_new(SafeCell::new(plaintext.to_vec())) .await .unwrap(); drop(actor); let mut actor = KeyHolder::new(db.clone()).await.unwrap(); - let seal_key = MemSafe::new(b"test-seal-key".to_vec()).unwrap(); + let seal_key = SafeCell::new(b"test-seal-key".to_vec()); actor.try_unseal(seal_key).await.unwrap(); let mut decrypted = actor.decrypt(aead_id).await.unwrap(); - assert_eq!(*decrypted.read().unwrap(), plaintext); + assert_eq!(*decrypted.read(), plaintext); } #[tokio::test] @@ -112,20 +112,20 @@ async fn test_unseal_wrong_then_correct_password() { let plaintext = b"important data"; let aead_id = actor - .create_new(MemSafe::new(plaintext.to_vec()).unwrap()) + .create_new(SafeCell::new(plaintext.to_vec())) .await .unwrap(); drop(actor); let mut actor = KeyHolder::new(db.clone()).await.unwrap(); - let bad_key = MemSafe::new(b"wrong-password".to_vec()).unwrap(); + let bad_key = SafeCell::new(b"wrong-password".to_vec()); let err = actor.try_unseal(bad_key).await.unwrap_err(); assert!(matches!(err, Error::InvalidKey)); - let good_key = MemSafe::new(b"test-seal-key".to_vec()).unwrap(); + let good_key = SafeCell::new(b"test-seal-key".to_vec()); actor.try_unseal(good_key).await.unwrap(); let mut decrypted = actor.decrypt(aead_id).await.unwrap(); - assert_eq!(*decrypted.read().unwrap(), plaintext); + assert_eq!(*decrypted.read(), plaintext); } diff --git a/server/crates/arbiter-server/tests/keyholder/storage.rs b/server/crates/arbiter-server/tests/keyholder/storage.rs index e595339..74a67cc 100644 --- a/server/crates/arbiter-server/tests/keyholder/storage.rs +++ b/server/crates/arbiter-server/tests/keyholder/storage.rs @@ -3,10 +3,10 @@ use std::collections::HashSet; use arbiter_server::{ actors::keyholder::{Error, encryption::v1}, db::{self, models, schema}, + safe_cell::{SafeCell, SafeCellHandle as _}, }; use diesel::{ExpressionMethods as _, QueryDsl, SelectableHelper, dsl::update}; use diesel_async::RunQueryDsl; -use memsafe::MemSafe; use crate::common; @@ -18,12 +18,12 @@ async fn test_create_decrypt_roundtrip() { let plaintext = b"hello arbiter"; let aead_id = actor - .create_new(MemSafe::new(plaintext.to_vec()).unwrap()) + .create_new(SafeCell::new(plaintext.to_vec())) .await .unwrap(); let mut decrypted = actor.decrypt(aead_id).await.unwrap(); - assert_eq!(*decrypted.read().unwrap(), plaintext); + assert_eq!(*decrypted.read(), plaintext); } #[tokio::test] @@ -44,11 +44,11 @@ async fn test_ciphertext_differs_across_entries() { let plaintext = b"same content"; let id1 = actor - .create_new(MemSafe::new(plaintext.to_vec()).unwrap()) + .create_new(SafeCell::new(plaintext.to_vec())) .await .unwrap(); let id2 = actor - .create_new(MemSafe::new(plaintext.to_vec()).unwrap()) + .create_new(SafeCell::new(plaintext.to_vec())) .await .unwrap(); @@ -70,8 +70,8 @@ async fn test_ciphertext_differs_across_entries() { let mut d1 = actor.decrypt(id1).await.unwrap(); let mut d2 = actor.decrypt(id2).await.unwrap(); - assert_eq!(*d1.read().unwrap(), plaintext); - assert_eq!(*d2.read().unwrap(), plaintext); + assert_eq!(*d1.read(), plaintext); + assert_eq!(*d2.read(), plaintext); } #[tokio::test] @@ -83,7 +83,7 @@ async fn test_nonce_never_reused() { let n = 5; for i in 0..n { actor - .create_new(MemSafe::new(format!("secret {i}").into_bytes()).unwrap()) + .create_new(SafeCell::new(format!("secret {i}").into_bytes())) .await .unwrap(); } @@ -137,7 +137,7 @@ async fn broken_db_nonce_format_fails_closed() { drop(conn); let err = actor - .create_new(MemSafe::new(b"must fail".to_vec()).unwrap()) + .create_new(SafeCell::new(b"must fail".to_vec())) .await .unwrap_err(); assert!(matches!(err, Error::BrokenDatabase)); @@ -145,7 +145,7 @@ async fn broken_db_nonce_format_fails_closed() { let db = db::create_test_pool().await; let mut actor = common::bootstrapped_keyholder(&db).await; let id = actor - .create_new(MemSafe::new(b"decrypt target".to_vec()).unwrap()) + .create_new(SafeCell::new(b"decrypt target".to_vec())) .await .unwrap(); let mut conn = db.get().await.unwrap(); diff --git a/server/crates/arbiter-server/tests/user_agent/unseal.rs b/server/crates/arbiter-server/tests/user_agent/unseal.rs index 486cb5d..ec5de37 100644 --- a/server/crates/arbiter-server/tests/user_agent/unseal.rs +++ b/server/crates/arbiter-server/tests/user_agent/unseal.rs @@ -5,9 +5,9 @@ use arbiter_server::{ user_agent::{Request, Response, UnsealError, session::UserAgentSession}, }, db, + safe_cell::{SafeCell, SafeCellHandle as _}, }; use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit}; -use memsafe::MemSafe; use x25519_dalek::{EphemeralSecret, PublicKey}; async fn setup_sealed_user_agent(seal_key: &[u8]) -> (db::DatabasePool, UserAgentSession) { @@ -17,7 +17,7 @@ async fn setup_sealed_user_agent(seal_key: &[u8]) -> (db::DatabasePool, UserAgen actors .key_holder .ask(Bootstrap { - seal_key_raw: MemSafe::new(seal_key.to_vec()).unwrap(), + seal_key_raw: SafeCell::new(seal_key.to_vec()), }) .await .unwrap();