refactor(server::crypto): use fixed-size [u8; 32] and KeyCell throughout seal key API
This commit is contained in:
@@ -162,13 +162,12 @@ impl Vault {
|
||||
#[messages]
|
||||
impl Vault {
|
||||
#[message]
|
||||
pub async fn bootstrap(&mut self, seal_key_raw: SafeCell<Vec<u8>>) -> Result<(), Error> {
|
||||
pub async fn bootstrap(&mut self, mut seal_key: KeyCell) -> Result<(), Error> {
|
||||
if !matches!(&self.state, State::Unbootstrapped) {
|
||||
return Err(Error::AlreadyBootstrapped);
|
||||
}
|
||||
|
||||
let mut root_key = KeyCell::new_secure_random();
|
||||
let mut seal_key = KeyCell::try_from(seal_key_raw).map_err(|()| Error::InvalidKey)?;
|
||||
|
||||
// Zero nonces are fine because they are one-time
|
||||
let root_key_nonce = Nonce::default();
|
||||
@@ -227,7 +226,7 @@ impl Vault {
|
||||
}
|
||||
|
||||
#[message]
|
||||
pub async fn try_unseal(&mut self, seal_key_raw: SafeCell<Vec<u8>>) -> Result<(), Error> {
|
||||
pub async fn try_unseal(&mut self, mut seal_key: KeyCell) -> Result<(), Error> {
|
||||
let State::Sealed {
|
||||
root_key_history_id,
|
||||
} = &self.state
|
||||
@@ -246,8 +245,6 @@ impl Vault {
|
||||
.await?
|
||||
};
|
||||
|
||||
let mut seal_key = KeyCell::try_from(seal_key_raw).map_err(|()| Error::InvalidKey)?;
|
||||
|
||||
let nonce =
|
||||
Nonce::try_from(current_key.root_key_encryption_nonce.as_slice()).map_err(|()| {
|
||||
error!("Broken database: invalid nonce for root key");
|
||||
@@ -422,8 +419,7 @@ mod tests {
|
||||
let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus())
|
||||
.await
|
||||
.unwrap();
|
||||
let seal_key = SafeCell::new([0u8; 32].to_vec());
|
||||
actor.bootstrap(seal_key).await.unwrap();
|
||||
actor.bootstrap(KeyCell::from([0u8; 32])).await.unwrap();
|
||||
actor
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use tracing::error;
|
||||
|
||||
use crate::{
|
||||
actors::vault::{Bootstrap, TryUnseal, Vault},
|
||||
crypto::{derive_key, encryption::v1::Nonce, shamir},
|
||||
crypto::{KeyCell, derive_key, encryption::v1::Nonce, shamir},
|
||||
db::{self, models, schema},
|
||||
};
|
||||
|
||||
@@ -94,14 +94,14 @@ async fn finalize_bootstrap(
|
||||
let threshold = shamir_threshold(total);
|
||||
|
||||
// Generate random 32-byte seal key
|
||||
let mut seal_key_bytes = vec![0u8; 32];
|
||||
let mut seal_key_bytes = [0u8; 32];
|
||||
OsRng.fill_bytes(&mut seal_key_bytes);
|
||||
|
||||
// Split seal key into shares using Shamir (OsRng from rand_core 0.6, compatible with vsss-rs)
|
||||
let shares = shamir::split_key(threshold, total, &seal_key_bytes, OsRng)
|
||||
.map_err(|e| Error::Shamir(e.to_string()))?;
|
||||
|
||||
let seal_key = SafeCell::new(seal_key_bytes);
|
||||
let seal_key = KeyCell::from(seal_key_bytes);
|
||||
|
||||
let mut conn = db.get().await?;
|
||||
|
||||
@@ -136,9 +136,7 @@ async fn finalize_bootstrap(
|
||||
}
|
||||
|
||||
vault
|
||||
.ask(Bootstrap {
|
||||
seal_key_raw: seal_key,
|
||||
})
|
||||
.ask(Bootstrap { seal_key })
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!(?err, "Vault bootstrap failed");
|
||||
@@ -189,12 +187,10 @@ async fn finalize_unseal(
|
||||
let seal_key_bytes =
|
||||
shamir::combine_shares(&shares).map_err(|e| Error::Shamir(e.to_string()))?;
|
||||
|
||||
let seal_key = SafeCell::new(seal_key_bytes);
|
||||
let seal_key = KeyCell::from(seal_key_bytes);
|
||||
|
||||
vault
|
||||
.ask(TryUnseal {
|
||||
seal_key_raw: seal_key,
|
||||
})
|
||||
.ask(TryUnseal { seal_key })
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!(?err, "Vault unseal failed");
|
||||
|
||||
@@ -215,8 +215,6 @@ mod tests {
|
||||
},
|
||||
db::{self, schema},
|
||||
};
|
||||
use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _};
|
||||
|
||||
use super::{Error, Integrable, sign_entity, verify_entity};
|
||||
#[derive(Clone, arbiter_macros::Hashable)]
|
||||
struct DummyEntity {
|
||||
@@ -235,7 +233,7 @@ mod tests {
|
||||
);
|
||||
actor
|
||||
.ask(Bootstrap {
|
||||
seal_key_raw: SafeCell::new([0u8; 32].to_vec()),
|
||||
seal_key: crate::crypto::KeyCell::from([0u8; 32]),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -21,6 +21,15 @@ impl From<SafeCell<Key>> for KeyCell {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
impl From<[u8; 32]> for KeyCell {
|
||||
fn from(bytes: [u8; 32]) -> Self {
|
||||
let cell = SafeCell::new_inline_default(|key: &mut Key| {
|
||||
key.copy_from_slice(&bytes);
|
||||
});
|
||||
Self(cell)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<SafeCell<Vec<u8>>> for KeyCell {
|
||||
type Error = ();
|
||||
|
||||
|
||||
@@ -13,15 +13,17 @@ pub enum ShamirError {
|
||||
pub fn split_key(
|
||||
threshold: usize,
|
||||
total: usize,
|
||||
key: &[u8],
|
||||
key: &[u8; 32],
|
||||
rng: impl rand_core::RngCore + rand_core::CryptoRng,
|
||||
) -> Result<Vec<Vec<u8>>, ShamirError> {
|
||||
Gf256::split_array(threshold, total, key, rng)
|
||||
Gf256::split_array(threshold, total, key.as_slice(), rng)
|
||||
.map_err(|e| ShamirError::Split(format!("{e:?}")))
|
||||
}
|
||||
|
||||
/// Reconstruct the secret from `threshold` or more shares.
|
||||
pub fn combine_shares(shares: &[Vec<u8>]) -> Result<Vec<u8>, ShamirError> {
|
||||
Gf256::combine_array(shares)
|
||||
.map_err(|e| ShamirError::Combine(format!("{e:?}")))
|
||||
pub fn combine_shares(shares: &[Vec<u8>]) -> Result<[u8; 32], ShamirError> {
|
||||
let bytes = Gf256::combine_array(shares)
|
||||
.map_err(|e| ShamirError::Combine(format!("{e:?}")))?;
|
||||
<[u8; 32]>::try_from(bytes.as_slice())
|
||||
.map_err(|_| ShamirError::Combine("unexpected reconstructed key length".to_owned()))
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
vault::{self, Bootstrap, GetState, TryUnseal, VaultState, events},
|
||||
vault_coordinator::{ContributeBootstrap, ContributeUnseal, StartBootstrap},
|
||||
},
|
||||
crypto::integrity::{self},
|
||||
crypto::{KeyCell, integrity::{self}},
|
||||
db::DatabasePool,
|
||||
};
|
||||
use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _};
|
||||
@@ -102,11 +102,9 @@ impl VaultGate {
|
||||
nonce: &[u8],
|
||||
ciphertext: &[u8],
|
||||
associated_data: &[u8],
|
||||
) -> Result<SafeCell<Vec<u8>>, ()> {
|
||||
) -> Result<KeyCell, ()> {
|
||||
let nonce = XNonce::from_slice(nonce);
|
||||
|
||||
let cipher = XChaCha20Poly1305::new(secret.as_bytes().into());
|
||||
|
||||
let mut key_buffer = SafeCell::new(ciphertext.to_vec());
|
||||
|
||||
let decryption_result = key_buffer.write_inline(|write_handle| {
|
||||
@@ -114,7 +112,9 @@ impl VaultGate {
|
||||
});
|
||||
|
||||
match decryption_result {
|
||||
Ok(()) => Ok(key_buffer),
|
||||
Ok(()) => KeyCell::try_from(key_buffer).map_err(|()| {
|
||||
error!("Decrypted key material has unexpected length");
|
||||
}),
|
||||
Err(err) => {
|
||||
error!(?err, "Failed to decrypt encrypted key material");
|
||||
Err(())
|
||||
@@ -122,6 +122,7 @@ impl VaultGate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[messages(enum)]
|
||||
impl VaultGate {
|
||||
#[message]
|
||||
@@ -155,17 +156,14 @@ impl VaultGate {
|
||||
return Err(Error::State);
|
||||
};
|
||||
|
||||
let Ok(seal_key_buffer) = Self::decrypt_key(secret, &nonce, &ciphertext, &associated_data)
|
||||
else {
|
||||
let Ok(seal_key) = Self::decrypt_key(secret, &nonce, &ciphertext, &associated_data) else {
|
||||
return Err(Error::InvalidKey);
|
||||
};
|
||||
|
||||
match self
|
||||
.actors
|
||||
.vault
|
||||
.ask(TryUnseal {
|
||||
seal_key_raw: seal_key_buffer,
|
||||
})
|
||||
.ask(TryUnseal { seal_key })
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
@@ -195,17 +193,14 @@ impl VaultGate {
|
||||
return Err(Error::State);
|
||||
};
|
||||
|
||||
let Ok(seal_key_buffer) = Self::decrypt_key(secret, &nonce, &ciphertext, &associated_data)
|
||||
else {
|
||||
let Ok(seal_key) = Self::decrypt_key(secret, &nonce, &ciphertext, &associated_data) else {
|
||||
return Err(Error::InvalidKey);
|
||||
};
|
||||
|
||||
match self
|
||||
.actors
|
||||
.vault
|
||||
.ask(Bootstrap {
|
||||
seal_key_raw: seal_key_buffer,
|
||||
})
|
||||
.ask(Bootstrap { seal_key })
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
@@ -255,7 +250,6 @@ impl VaultGate {
|
||||
&mut self,
|
||||
passphrase: Vec<u8>,
|
||||
) -> Result<bool, Error> {
|
||||
use arbiter_crypto::safecell::SafeCell;
|
||||
let passphrase_cell = SafeCell::new(passphrase);
|
||||
self.actors
|
||||
.vault_coordinator
|
||||
@@ -272,7 +266,6 @@ impl VaultGate {
|
||||
&mut self,
|
||||
passphrase: Vec<u8>,
|
||||
) -> Result<bool, Error> {
|
||||
use arbiter_crypto::safecell::SafeCell;
|
||||
let passphrase_cell = SafeCell::new(passphrase);
|
||||
self.actors
|
||||
.vault_coordinator
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
use super::common::ChannelTransport;
|
||||
use arbiter_crypto::{
|
||||
authn::{self, AuthChallenge, CLIENT_CONTEXT},
|
||||
safecell::{SafeCell, SafeCellHandle as _},
|
||||
};
|
||||
use arbiter_crypto::authn::{self, AuthChallenge, CLIENT_CONTEXT};
|
||||
use arbiter_proto::{
|
||||
ClientMetadata,
|
||||
transport::{Receiver, Sender},
|
||||
@@ -100,7 +97,7 @@ async fn spawn_test_actors(db: &db::DatabasePool) -> GlobalActors {
|
||||
actors
|
||||
.vault
|
||||
.ask(Bootstrap {
|
||||
seal_key_raw: SafeCell::new([0u8; 32].to_vec()),
|
||||
seal_key: arbiter_server::crypto::KeyCell::from([0u8; 32]),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
dead_code,
|
||||
reason = "Common test utilities that may not be used in every test"
|
||||
)]
|
||||
use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _};
|
||||
use arbiter_proto::transport::{Bi, Error, Receiver, Sender};
|
||||
use arbiter_server::{
|
||||
actors::{GlobalActors, vault::Vault},
|
||||
@@ -19,7 +18,7 @@ pub(crate) async fn bootstrapped_vault(db: &db::DatabasePool) -> Vault {
|
||||
.await
|
||||
.unwrap();
|
||||
actor
|
||||
.bootstrap(SafeCell::new([0u8; 32].to_vec()))
|
||||
.bootstrap(arbiter_server::crypto::KeyCell::from([0u8; 32]))
|
||||
.await
|
||||
.unwrap();
|
||||
actor
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
use super::common::ChannelTransport;
|
||||
use arbiter_crypto::{
|
||||
authn::{self, AuthChallenge, OPERATOR_CONTEXT},
|
||||
safecell::{SafeCell, SafeCellHandle as _},
|
||||
};
|
||||
use arbiter_crypto::authn::{self, AuthChallenge, OPERATOR_CONTEXT};
|
||||
use arbiter_proto::transport::{Error as TransportError, Receiver, Sender};
|
||||
use arbiter_server::{
|
||||
actors::{GlobalActors, bootstrap::GetToken, vault::Bootstrap},
|
||||
@@ -157,7 +154,7 @@ pub async fn bootstrap_token_auth() {
|
||||
actors
|
||||
.vault
|
||||
.ask(Bootstrap {
|
||||
seal_key_raw: SafeCell::new([0u8; 32].to_vec()),
|
||||
seal_key: arbiter_server::crypto::KeyCell::from([0u8; 32]),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -275,7 +272,7 @@ pub async fn challenge_auth() {
|
||||
actors
|
||||
.vault
|
||||
.ask(Bootstrap {
|
||||
seal_key_raw: SafeCell::new([0u8; 32].to_vec()),
|
||||
seal_key: arbiter_server::crypto::KeyCell::from([0u8; 32]),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -361,7 +358,7 @@ pub async fn challenge_auth_rejects_integrity_tag_mismatch_when_unsealed() {
|
||||
actors
|
||||
.vault
|
||||
.ask(Bootstrap {
|
||||
seal_key_raw: SafeCell::new([0u8; 32].to_vec()),
|
||||
seal_key: arbiter_server::crypto::KeyCell::from([0u8; 32]),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -434,7 +431,7 @@ pub async fn challenge_auth_rejects_invalid_signature() {
|
||||
actors
|
||||
.vault
|
||||
.ask(Bootstrap {
|
||||
seal_key_raw: SafeCell::new([0u8; 32].to_vec()),
|
||||
seal_key: arbiter_server::crypto::KeyCell::from([0u8; 32]),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use arbiter_crypto::{
|
||||
authn,
|
||||
safecell::{SafeCell, SafeCellHandle as _},
|
||||
};
|
||||
use arbiter_crypto::authn;
|
||||
use arbiter_server::{
|
||||
actors::{
|
||||
GlobalActors,
|
||||
@@ -34,7 +31,7 @@ async fn setup_sealed_gate(
|
||||
actors
|
||||
.vault
|
||||
.ask(Bootstrap {
|
||||
seal_key_raw: SafeCell::new(seal_key.to_vec()),
|
||||
seal_key: arbiter_server::crypto::KeyCell::from(*seal_key),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -166,7 +166,7 @@ async fn decrypt_roundtrip_after_high_concurrency() {
|
||||
.await
|
||||
.unwrap();
|
||||
decryptor
|
||||
.try_unseal(SafeCell::new([0u8; 32].to_vec()))
|
||||
.try_unseal(arbiter_server::crypto::KeyCell::from([0u8; 32]))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use arbiter_server::{
|
||||
GlobalActors,
|
||||
vault::{Error, Vault},
|
||||
},
|
||||
crypto::encryption::v1::{Nonce, ROOT_KEY_TAG},
|
||||
crypto::{KeyCell, encryption::v1::{Nonce, ROOT_KEY_TAG}},
|
||||
db::{self, models, schema},
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ async fn test_bootstrap() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let seal_key = SafeCell::new([0u8; 32].to_vec());
|
||||
let seal_key = KeyCell::from([0u8; 32]);
|
||||
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_vault(&db).await;
|
||||
|
||||
let seal_key2 = SafeCell::new([0u8; 32].to_vec());
|
||||
let seal_key2 = KeyCell::from([0u8; 32]);
|
||||
let err = actor.bootstrap(seal_key2).await.unwrap_err();
|
||||
assert!(matches!(err, Error::AlreadyBootstrapped));
|
||||
}
|
||||
@@ -105,7 +105,7 @@ async fn test_unseal_correct_password() {
|
||||
let mut actor = Vault::new(db.clone(), GlobalActors::spawn_message_bus())
|
||||
.await
|
||||
.unwrap();
|
||||
let seal_key = SafeCell::new([0u8; 32].to_vec());
|
||||
let seal_key = KeyCell::from([0u8; 32]);
|
||||
actor.try_unseal(seal_key).await.unwrap();
|
||||
|
||||
let mut decrypted = actor.decrypt(aead_id).await.unwrap();
|
||||
@@ -129,11 +129,11 @@ async fn test_unseal_wrong_then_correct_password() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let bad_key = SafeCell::new([1u8; 32].to_vec());
|
||||
let bad_key = KeyCell::from([1u8; 32]);
|
||||
let err = actor.try_unseal(bad_key).await.unwrap_err();
|
||||
assert!(matches!(err, Error::InvalidKey));
|
||||
|
||||
let good_key = SafeCell::new([0u8; 32].to_vec());
|
||||
let good_key = KeyCell::from([0u8; 32]);
|
||||
actor.try_unseal(good_key).await.unwrap();
|
||||
|
||||
let mut decrypted = actor.decrypt(aead_id).await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user