refactor(server): rewrote cell access using new helpers and added ast-grep rules for it
This commit is contained in:
@@ -1,3 +1,12 @@
|
|||||||
|
[[tools.ast-grep]]
|
||||||
|
version = "0.42.0"
|
||||||
|
backend = "aqua:ast-grep/ast-grep"
|
||||||
|
"platforms.linux-arm64" = { checksum = "sha256:5c830eae8456569e2f7212434ed9c238f58dca412d76045418ed6d394a755836", url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-aarch64-unknown-linux-gnu.zip"}
|
||||||
|
"platforms.linux-x64" = { checksum = "sha256:e825a05603f0bcc4cd9076c4cc8c9abd6d008b7cd07d9aa3cc323ba4b8606651", url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-x86_64-unknown-linux-gnu.zip"}
|
||||||
|
"platforms.macos-arm64" = { checksum = "sha256:fc300d5293b1c770a5aece03a8a193b92e71e87cec726c28096990691a582620", url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-aarch64-apple-darwin.zip"}
|
||||||
|
"platforms.macos-x64" = { checksum = "sha256:979ffe611327056f4730a1ae71b0209b3b830f58b22c6ed194cda34f55400db2", url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-x86_64-apple-darwin.zip"}
|
||||||
|
"platforms.windows-x64" = { checksum = "sha256:55836fa1b2c65dc7d61615a4d9368622a0d2371a76d28b9a165e5a3ab6ae32a4", url = "https://github.com/ast-grep/ast-grep/releases/download/0.42.0/app-x86_64-pc-windows-msvc.zip"}
|
||||||
|
|
||||||
[[tools."cargo:cargo-audit"]]
|
[[tools."cargo:cargo-audit"]]
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
backend = "cargo:cargo-audit"
|
backend = "cargo:cargo-audit"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ protoc = "29.6"
|
|||||||
"cargo:cargo-shear" = "latest"
|
"cargo:cargo-shear" = "latest"
|
||||||
"cargo:cargo-insta" = "1.46.3"
|
"cargo:cargo-insta" = "1.46.3"
|
||||||
python = "3.14.3"
|
python = "3.14.3"
|
||||||
|
ast-grep = "0.42.0"
|
||||||
|
|
||||||
[tasks.codegen]
|
[tasks.codegen]
|
||||||
sources = ['protobufs/*.proto']
|
sources = ['protobufs/*.proto']
|
||||||
|
|||||||
@@ -108,11 +108,7 @@ impl EvmActor {
|
|||||||
pub async fn generate(&mut self) -> Result<Address, Error> {
|
pub async fn generate(&mut self) -> Result<Address, Error> {
|
||||||
let (mut key_cell, address) = safe_signer::generate(&mut self.rng);
|
let (mut key_cell, address) = safe_signer::generate(&mut self.rng);
|
||||||
|
|
||||||
// Move raw key bytes into a Vec<u8> MemSafe for KeyHolder
|
let plaintext = key_cell.read_inline(|reader| SafeCell::new(reader.to_vec()));
|
||||||
let plaintext = {
|
|
||||||
let reader = key_cell.read();
|
|
||||||
SafeCell::new(reader.to_vec())
|
|
||||||
};
|
|
||||||
|
|
||||||
let aead_id: i32 = self
|
let aead_id: i32 = self
|
||||||
.keyholder
|
.keyholder
|
||||||
|
|||||||
@@ -62,26 +62,19 @@ impl TryFrom<SafeCell<Vec<u8>>> for KeyCell {
|
|||||||
if value.len() != size_of::<Key>() {
|
if value.len() != size_of::<Key>() {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
let mut cell = SafeCell::new(Key::default());
|
let cell = SafeCell::new_inline(|cell_write: &mut Key| {
|
||||||
{
|
cell_write.copy_from_slice(&value);
|
||||||
let mut cell_write = cell.write();
|
});
|
||||||
let cell_slice: &mut [u8] = cell_write.as_mut();
|
|
||||||
cell_slice.copy_from_slice(&value);
|
|
||||||
}
|
|
||||||
Ok(Self(cell))
|
Ok(Self(cell))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyCell {
|
impl KeyCell {
|
||||||
pub fn new_secure_random() -> Self {
|
pub fn new_secure_random() -> Self {
|
||||||
let mut key = SafeCell::new(Key::default());
|
let key = SafeCell::new_inline(|key_buffer: &mut Key| {
|
||||||
{
|
|
||||||
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();
|
let mut rng = StdRng::try_from_rng(&mut SysRng).unwrap();
|
||||||
rng.fill_bytes(key_buffer);
|
rng.fill_bytes(key_buffer);
|
||||||
}
|
});
|
||||||
|
|
||||||
key.into()
|
key.into()
|
||||||
}
|
}
|
||||||
@@ -151,15 +144,14 @@ pub fn derive_seal_key(mut password: SafeCell<Vec<u8>>, salt: &Salt) -> KeyCell
|
|||||||
let params = argon2::Params::new(262_144, 3, 4, None).unwrap();
|
let params = argon2::Params::new(262_144, 3, 4, None).unwrap();
|
||||||
let hasher = Argon2::new(Algorithm::Argon2id, argon2::Version::V0x13, params);
|
let hasher = Argon2::new(Algorithm::Argon2id, argon2::Version::V0x13, params);
|
||||||
let mut key = SafeCell::new(Key::default());
|
let mut key = SafeCell::new(Key::default());
|
||||||
{
|
password.read_inline(|password_source| {
|
||||||
let password_source = password.read();
|
|
||||||
let mut key_buffer = key.write();
|
let mut key_buffer = key.write();
|
||||||
let key_buffer: &mut [u8] = key_buffer.as_mut();
|
let key_buffer: &mut [u8] = key_buffer.as_mut();
|
||||||
|
|
||||||
hasher
|
hasher
|
||||||
.hash_password_into(password_source.deref(), salt, key_buffer)
|
.hash_password_into(password_source.deref(), salt, key_buffer)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
});
|
||||||
|
|
||||||
key.into()
|
key.into()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,15 @@ use kameo::{Actor, Reply, messages};
|
|||||||
use strum::{EnumDiscriminants, IntoDiscriminant};
|
use strum::{EnumDiscriminants, IntoDiscriminant};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::{db::{
|
use crate::safe_cell::SafeCell;
|
||||||
|
use crate::{
|
||||||
|
db::{
|
||||||
self,
|
self,
|
||||||
models::{self, RootKeyHistory},
|
models::{self, RootKeyHistory},
|
||||||
schema::{self},
|
schema::{self},
|
||||||
}, safe_cell::SafeCellHandle as _};
|
},
|
||||||
use crate::safe_cell::SafeCell;
|
safe_cell::SafeCellHandle as _,
|
||||||
|
};
|
||||||
use encryption::v1::{self, KeyCell, Nonce};
|
use encryption::v1::{self, KeyCell, Nonce};
|
||||||
|
|
||||||
pub mod encryption;
|
pub mod encryption;
|
||||||
@@ -148,16 +151,15 @@ impl KeyHolder {
|
|||||||
let root_key_nonce = v1::Nonce::default();
|
let root_key_nonce = v1::Nonce::default();
|
||||||
let data_encryption_nonce = v1::Nonce::default();
|
let data_encryption_nonce = v1::Nonce::default();
|
||||||
|
|
||||||
let root_key_ciphertext: Vec<u8> = {
|
let root_key_ciphertext: Vec<u8> = root_key.0.read_inline(|reader| {
|
||||||
let root_key_reader = root_key.0.read();
|
let root_key_reader = reader.as_slice();
|
||||||
let root_key_reader = root_key_reader.as_slice();
|
|
||||||
seal_key
|
seal_key
|
||||||
.encrypt(&root_key_nonce, v1::ROOT_KEY_TAG, root_key_reader)
|
.encrypt(&root_key_nonce, v1::ROOT_KEY_TAG, root_key_reader)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!(?err, "Fatal bootstrap error");
|
error!(?err, "Fatal bootstrap error");
|
||||||
Error::Encryption(err)
|
Error::Encryption(err)
|
||||||
})?
|
})
|
||||||
};
|
})?;
|
||||||
|
|
||||||
let mut conn = self.db.get().await?;
|
let mut conn = self.db.get().await?;
|
||||||
|
|
||||||
@@ -349,7 +351,10 @@ mod tests {
|
|||||||
|
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
use crate::{db::{self}, safe_cell::SafeCell};
|
use crate::{
|
||||||
|
db::{self},
|
||||||
|
safe_cell::SafeCell,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,12 @@ use kameo::error::SendError;
|
|||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
use x25519_dalek::{EphemeralSecret, PublicKey};
|
use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||||
|
|
||||||
use crate::{actors::{
|
use crate::safe_cell::SafeCell;
|
||||||
evm::{Generate, ListWallets, UseragentCreateGrant, UseragentDeleteGrant, UseragentListGrants},
|
use crate::{
|
||||||
|
actors::{
|
||||||
|
evm::{
|
||||||
|
Generate, ListWallets, UseragentCreateGrant, UseragentDeleteGrant, UseragentListGrants,
|
||||||
|
},
|
||||||
keyholder::{self, Bootstrap, TryUnseal},
|
keyholder::{self, Bootstrap, TryUnseal},
|
||||||
user_agent::{
|
user_agent::{
|
||||||
BootstrapError, Request, Response, TransportResponseError, UnsealError, VaultState,
|
BootstrapError, Request, Response, TransportResponseError, UnsealError, VaultState,
|
||||||
@@ -15,8 +19,9 @@ use crate::{actors::{
|
|||||||
state::{UnsealContext, UserAgentEvents, UserAgentStates},
|
state::{UnsealContext, UserAgentEvents, UserAgentStates},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, safe_cell::SafeCellHandle as _};
|
},
|
||||||
use crate::safe_cell::SafeCell;
|
safe_cell::SafeCellHandle as _,
|
||||||
|
};
|
||||||
|
|
||||||
impl UserAgentSession {
|
impl UserAgentSession {
|
||||||
pub async fn process_transport_inbound(&mut self, req: Request) -> Output {
|
pub async fn process_transport_inbound(&mut self, req: Request) -> Output {
|
||||||
@@ -100,11 +105,9 @@ impl UserAgentSession {
|
|||||||
|
|
||||||
let mut key_buffer = SafeCell::new(ciphertext.to_vec());
|
let mut key_buffer = SafeCell::new(ciphertext.to_vec());
|
||||||
|
|
||||||
let decryption_result = {
|
let decryption_result = key_buffer.write_inline(|write_handle| {
|
||||||
let mut write_handle = key_buffer.write();
|
|
||||||
let write_handle = write_handle.deref_mut();
|
|
||||||
cipher.decrypt_in_place(nonce, associated_data, write_handle)
|
cipher.decrypt_in_place(nonce, associated_data, write_handle)
|
||||||
};
|
});
|
||||||
|
|
||||||
match decryption_result {
|
match decryption_result {
|
||||||
Ok(_) => Ok(key_buffer),
|
Ok(_) => Ok(key_buffer),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::safe_cell::{SafeCell, SafeCellHandle as _};
|
||||||
use alloy::{
|
use alloy::{
|
||||||
consensus::SignableTransaction,
|
consensus::SignableTransaction,
|
||||||
network::{TxSigner, TxSignerSync},
|
network::{TxSigner, TxSignerSync},
|
||||||
@@ -8,7 +9,6 @@ use alloy::{
|
|||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use k256::ecdsa::{self, RecoveryId, SigningKey, signature::hazmat::PrehashSigner};
|
use k256::ecdsa::{self, RecoveryId, SigningKey, signature::hazmat::PrehashSigner};
|
||||||
use crate::safe_cell::{SafeCell, SafeCellHandle as _};
|
|
||||||
|
|
||||||
/// An Ethereum signer that stores its secp256k1 secret key inside a
|
/// An Ethereum signer that stores its secp256k1 secret key inside a
|
||||||
/// hardware-protected [`MemSafe`] cell.
|
/// hardware-protected [`MemSafe`] cell.
|
||||||
@@ -44,11 +44,10 @@ impl std::fmt::Debug for SafeSigner {
|
|||||||
/// Returns the protected key bytes and the derived Ethereum address.
|
/// Returns the protected key bytes and the derived Ethereum address.
|
||||||
pub fn generate(rng: &mut impl rand::Rng) -> (SafeCell<[u8; 32]>, Address) {
|
pub fn generate(rng: &mut impl rand::Rng) -> (SafeCell<[u8; 32]>, Address) {
|
||||||
loop {
|
loop {
|
||||||
let mut cell = SafeCell::new([0u8; 32]);
|
let mut cell = SafeCell::new_inline(|w: &mut [u8; 32]| {
|
||||||
{
|
rng.fill_bytes(w);
|
||||||
let mut w = cell.write();
|
});
|
||||||
rng.fill_bytes(w.as_mut());
|
|
||||||
}
|
|
||||||
let reader = cell.read();
|
let reader = cell.read();
|
||||||
if let Ok(sk) = SigningKey::from_slice(reader.as_ref()) {
|
if let Ok(sk) = SigningKey::from_slice(reader.as_ref()) {
|
||||||
let address = secret_key_to_address(&sk);
|
let address = secret_key_to_address(&sk);
|
||||||
|
|||||||
@@ -19,6 +19,36 @@ pub trait SafeCellHandle<T> {
|
|||||||
|
|
||||||
fn read(&mut self) -> Self::CellRead<'_>;
|
fn read(&mut self) -> Self::CellRead<'_>;
|
||||||
fn write(&mut self) -> Self::CellWrite<'_>;
|
fn write(&mut self) -> Self::CellWrite<'_>;
|
||||||
|
|
||||||
|
fn new_inline<F>(f: F) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
T: Default,
|
||||||
|
F: for<'a> FnOnce(&'a mut T),
|
||||||
|
{
|
||||||
|
let mut cell = Self::new(T::default());
|
||||||
|
{
|
||||||
|
let mut handle = cell.write();
|
||||||
|
f(handle.deref_mut());
|
||||||
|
}
|
||||||
|
cell
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_inline<F, R>(&mut self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&T) -> R,
|
||||||
|
{
|
||||||
|
f(&*self.read())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_inline<F, R>(&mut self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut T) -> R,
|
||||||
|
{
|
||||||
|
f(&mut *self.write())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MemSafeCell<T>(MemSafe<T>);
|
pub struct MemSafeCell<T>(MemSafe<T>);
|
||||||
@@ -53,6 +83,7 @@ impl<T> SafeCellHandle<T> for MemSafeCell<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn read(&mut self) -> Self::CellRead<'_> {
|
fn read(&mut self) -> Self::CellRead<'_> {
|
||||||
match self.0.read() {
|
match self.0.read() {
|
||||||
Ok(inner) => inner,
|
Ok(inner) => inner,
|
||||||
@@ -60,6 +91,7 @@ impl<T> SafeCellHandle<T> for MemSafeCell<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn write(&mut self) -> Self::CellWrite<'_> {
|
fn write(&mut self) -> Self::CellWrite<'_> {
|
||||||
match self.0.write() {
|
match self.0.write() {
|
||||||
Ok(inner) => inner,
|
Ok(inner) => inner,
|
||||||
|
|||||||
0
server/rules/.gitkeep
Normal file
0
server/rules/.gitkeep
Normal file
10
server/rules/safecell/new-inline.yaml
Normal file
10
server/rules/safecell/new-inline.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
id: safecell-new-inline
|
||||||
|
language: Rust
|
||||||
|
rule:
|
||||||
|
pattern: $CELL.write_inline(|$W| $BODY);
|
||||||
|
follows:
|
||||||
|
pattern: let mut $CELL = SafeCell::new($INIT);
|
||||||
|
fix:
|
||||||
|
template: let mut $CELL = SafeCell::new_inline(|$W| $BODY);
|
||||||
|
expandStart:
|
||||||
|
pattern: let mut $CELL = SafeCell::new($INIT)
|
||||||
17
server/rules/safecell/read-inline.yaml
Normal file
17
server/rules/safecell/read-inline.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
id: safecell-read-inline
|
||||||
|
language: Rust
|
||||||
|
rule:
|
||||||
|
pattern:
|
||||||
|
context: |
|
||||||
|
{
|
||||||
|
let $READ = $CELL.read();
|
||||||
|
$$$BODY
|
||||||
|
}
|
||||||
|
selector: block
|
||||||
|
inside:
|
||||||
|
kind: block
|
||||||
|
fix:
|
||||||
|
template: |
|
||||||
|
$CELL.read_inline(|$READ| {
|
||||||
|
$$$BODY
|
||||||
|
});
|
||||||
13
server/rules/safecell/write-inline.yaml
Normal file
13
server/rules/safecell/write-inline.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
id: safecell-write-inline
|
||||||
|
language: Rust
|
||||||
|
rule:
|
||||||
|
pattern: |
|
||||||
|
{
|
||||||
|
let mut $WRITE = $CELL.write();
|
||||||
|
$$$BODY
|
||||||
|
}
|
||||||
|
fix:
|
||||||
|
template: |
|
||||||
|
$CELL.write_inline(|$WRITE| {
|
||||||
|
$$$BODY
|
||||||
|
});
|
||||||
2
server/sgconfig.yml
Normal file
2
server/sgconfig.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ruleDirs:
|
||||||
|
- ./rules
|
||||||
Reference in New Issue
Block a user