fix(vault): apply Shamir custody review feedback
This commit is contained in:
@@ -76,6 +76,9 @@ pub enum Error {
|
||||
/// declared, so the token stays valid across several registrations and is
|
||||
/// retired by the `Bootstrapped` event rather than by first use, whichever
|
||||
/// bootstrap path fired it.
|
||||
///
|
||||
/// Every daemon start mints a fresh token and overwrites the file: a token
|
||||
/// handed out by an earlier run is dead.
|
||||
pub struct Bootstrapper {
|
||||
token: Option<SafeCell<[u8; TOKEN_LENGTH]>>,
|
||||
token_path: Option<PathBuf>,
|
||||
@@ -116,41 +119,13 @@ impl Bootstrapper {
|
||||
});
|
||||
}
|
||||
|
||||
let registered = diesel::select(diesel::dsl::exists(
|
||||
schema::operator_identity::table.select(schema::operator_identity::id),
|
||||
))
|
||||
.get_result::<bool>(&mut conn)
|
||||
.await?;
|
||||
|
||||
let token = if registered {
|
||||
match tokio::fs::read_to_string(&path).await {
|
||||
Ok(existing)
|
||||
if existing.len() == TOKEN_LENGTH
|
||||
&& existing.chars().all(|c| c.is_ascii_alphanumeric()) =>
|
||||
{
|
||||
let mut cell = SafeCell::new([0u8; TOKEN_LENGTH]);
|
||||
cell.write().copy_from_slice(existing.as_bytes());
|
||||
cell
|
||||
}
|
||||
Ok(_) | Err(_) => generate_token(&path).await?,
|
||||
}
|
||||
} else {
|
||||
generate_token(&path).await?
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
token: Some(token),
|
||||
token: Some(generate_token(&path).await?),
|
||||
token_path: Some(path),
|
||||
events,
|
||||
})
|
||||
}
|
||||
|
||||
fn is_correct_token(&mut self, token: &[u8]) -> bool {
|
||||
self.token.as_mut().is_some_and(|expected| {
|
||||
expected.read_inline(|bytes| bool::from(bytes.as_ref().ct_eq(token)))
|
||||
})
|
||||
}
|
||||
|
||||
async fn forget(&mut self) {
|
||||
self.token = None;
|
||||
if let Some(path) = self.token_path.take() {
|
||||
@@ -170,12 +145,14 @@ impl Message<events::Bootstrapped> for Bootstrapper {
|
||||
self.forget().await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[messages]
|
||||
impl Bootstrapper {
|
||||
#[message]
|
||||
pub fn verify_token(&mut self, token: Vec<u8>) -> bool {
|
||||
self.is_correct_token(&token)
|
||||
self.token.as_mut().is_some_and(|expected| {
|
||||
expected.read_inline(|bytes| bool::from(bytes.as_ref().ct_eq(token.as_slice())))
|
||||
})
|
||||
}
|
||||
|
||||
#[message]
|
||||
|
||||
@@ -192,12 +192,14 @@ async fn finalize_bootstrap(
|
||||
let mut shares = shamir::split_key(threshold, total, &mut seal_key, UnwrapErr(SysRng))
|
||||
.map_err(|error| Error::Shamir(error.to_string()))?;
|
||||
|
||||
if shares.len() < total {
|
||||
return Err(Error::Shamir("missing share for operator".to_owned()));
|
||||
}
|
||||
|
||||
let mut encrypted = Vec::with_capacity(total);
|
||||
for (index, (operator_id, passphrase)) in contributions.0.iter_mut().enumerate() {
|
||||
let share = shares
|
||||
.read_inline(|shares| shares.get(index).cloned())
|
||||
.ok_or_else(|| Error::Shamir("missing share for operator".to_owned()))?;
|
||||
encrypted.push((*operator_id, encrypt_share(passphrase, &share)?));
|
||||
for ((operator_id, passphrase), share) in contributions.0.iter_mut().zip(shares.iter_mut()) {
|
||||
let share = share.read_inline(|share| encrypt_share(passphrase, share))?;
|
||||
encrypted.push((*operator_id, share));
|
||||
}
|
||||
|
||||
vault
|
||||
@@ -227,12 +229,9 @@ async fn finalize_unseal(
|
||||
.await?
|
||||
};
|
||||
|
||||
let mut plaintext = SafeCell::new(Vec::with_capacity(stored.len()));
|
||||
let mut plaintext = Vec::with_capacity(stored.len());
|
||||
for ((_, passphrase), share) in contributions.0.iter_mut().zip(stored) {
|
||||
let mut decrypted = decrypt_share(passphrase, share)?;
|
||||
decrypted.read_inline(|share| {
|
||||
plaintext.write_inline(|shares| shares.push(share.clone()));
|
||||
});
|
||||
plaintext.push(decrypt_share(passphrase, share)?);
|
||||
}
|
||||
|
||||
let seal_key = shamir::combine_shares(threshold, &mut plaintext)
|
||||
|
||||
Reference in New Issue
Block a user