5 Commits

Author SHA1 Message Date
hdbg
47108ed8ad chore(supply-chain): update cargo-vet audits and trusted publishers
Some checks failed
ci/woodpecker/pr/server-lint Pipeline failed
ci/woodpecker/pr/server-audit Pipeline was successful
ci/woodpecker/pr/server-vet Pipeline failed
ci/woodpecker/pr/server-test Pipeline was successful
ci/woodpecker/push/server-lint Pipeline failed
ci/woodpecker/push/server-audit Pipeline was successful
ci/woodpecker/push/server-vet Pipeline failed
ci/woodpecker/push/server-test Pipeline was successful
2026-02-16 20:52:31 +01:00
hdbg
359df73c2e feat(server::key_holder): unique index on (root_key_id, nonce) to avoid nonce reuse 2026-02-16 20:45:15 +01:00
hdbg
ce03b7e15d feat(server::key_holder): ability to remotely get current state 2026-02-16 20:40:36 +01:00
hdbg
e4038d9188 refactor(keyholder): rename KeyHolderActor to KeyHolder and optimize db connection lifetime 2026-02-16 20:36:47 +01:00
hdbg
c82339d764 security(server::key_holder): replaced nonce-caching with exclusive transaction fetching nonce from the database 2026-02-16 18:23:25 +01:00
10 changed files with 951 additions and 201 deletions

44
server/Cargo.lock generated
View File

@@ -94,7 +94,6 @@ dependencies = [
"rustls", "rustls",
"secrecy", "secrecy",
"smlang", "smlang",
"statig",
"strum", "strum",
"test-log", "test-log",
"thiserror", "thiserror",
@@ -1725,28 +1724,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn 2.0.115",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.106" version = "1.0.106"
@@ -2229,27 +2206,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "statig"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03c04b4a9f2d66294d63bdd8df834caad9f8e181997c3cf766b6b4f6d12d4fbc"
dependencies = [
"statig_macro",
]
[[package]]
name = "statig_macro"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8090ca395ee30c4b38fee68cf4ddf0bcc5f01aa83364cd4c3ec737a1596dab4d"
dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.115",
]
[[package]] [[package]]
name = "string_morph" name = "string_morph"
version = "0.1.0" version = "0.1.0"

View File

@@ -41,7 +41,6 @@ zeroize = { version = "1.8.2", features = ["std", "simd"] }
kameo.workspace = true kameo.workspace = true
x25519-dalek = { version = "2.0.1", features = ["getrandom"] } x25519-dalek = { version = "2.0.1", features = ["getrandom"] }
chacha20poly1305 = { version = "0.10.1", features = ["std"] } chacha20poly1305 = { version = "0.10.1", features = ["std"] }
statig = { version = "0.4.1", features = ["async"] }
argon2 = { version = "0.5.3", features = ["zeroize"] } argon2 = { version = "0.5.3", features = ["zeroize"] }
restructed = "0.2.2" restructed = "0.2.2"
strum = { version = "0.27.2", features = ["derive"] } strum = { version = "0.27.2", features = ["derive"] }

View File

@@ -1,12 +1,3 @@
create table if not exists aead_encrypted (
id INTEGER not null PRIMARY KEY,
current_nonce blob not null default(1), -- if re-encrypted, this should be incremented
ciphertext blob not null,
tag blob not null,
schema_version integer not null default(1), -- server would need to reencrypt, because this means that we have changed algorithm
created_at integer not null default(unixepoch ('now'))
) STRICT;
create table if not exists root_key_history ( create table if not exists root_key_history (
id INTEGER not null PRIMARY KEY, id INTEGER not null PRIMARY KEY,
-- root key stored as aead encrypted artifact, with only difference that it's decrypted by unseal key (derived from user password) -- root key stored as aead encrypted artifact, with only difference that it's decrypted by unseal key (derived from user password)
@@ -18,6 +9,21 @@ create table if not exists root_key_history (
salt blob not null -- for key deriviation salt blob not null -- for key deriviation
) STRICT; ) STRICT;
create table if not exists aead_encrypted (
id INTEGER not null PRIMARY KEY,
current_nonce blob not null default(1), -- if re-encrypted, this should be incremented
ciphertext blob not null,
tag blob not null,
schema_version integer not null default(1), -- server would need to reencrypt, because this means that we have changed algorithm
associated_root_key_id integer not null references root_key_history (id) on delete RESTRICT,
created_at integer not null default(unixepoch ('now'))
) STRICT;
create unique index if not exists uniq_nonce_per_root_key on aead_encrypted (
current_nonce,
associated_root_key_id
);
-- This is a singleton -- This is a singleton
create table if not exists arbiter_settings ( create table if not exists arbiter_settings (
id INTEGER not null PRIMARY KEY CHECK (id = 1), -- singleton row, id must be 1 id INTEGER not null PRIMARY KEY CHECK (id = 1), -- singleton row, id must be 1

View File

@@ -3,8 +3,9 @@ use diesel::{
dsl::{insert_into, update}, dsl::{insert_into, update},
}; };
use diesel_async::{AsyncConnection, RunQueryDsl}; use diesel_async::{AsyncConnection, RunQueryDsl};
use kameo::{Actor, messages}; use kameo::{Actor, Reply, messages};
use memsafe::MemSafe; use memsafe::MemSafe;
use strum::{EnumDiscriminants, IntoDiscriminant};
use tracing::{error, info}; use tracing::{error, info};
use crate::{ use crate::{
@@ -18,19 +19,17 @@ use crate::{
pub mod v1; pub mod v1;
#[derive(Default)] #[derive(Default, EnumDiscriminants)]
#[strum_discriminants(derive(Reply), vis(pub))]
enum State { enum State {
#[default] #[default]
Unbootstrapped, Unbootstrapped,
Sealed { Sealed {
encrypted_root_key: RootKeyHistory, root_key_history_id: i32,
data_encryption_nonce: v1::Nonce,
root_key_encryption_nonce: v1::Nonce,
}, },
Unsealed { Unsealed {
root_key_history_id: i32, root_key_history_id: i32,
root_key: KeyCell, root_key: KeyCell,
nonce: v1::Nonce,
}, },
} }
@@ -71,13 +70,13 @@ pub enum Error {
/// Provides API for encrypting and decrypting data using the vault root key. /// 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. /// Abstraction over database to make sure nonces are never reused and encryption keys are never exposed in plaintext outside of this actor.
#[derive(Actor)] #[derive(Actor)]
pub struct KeyHolderActor { pub struct KeyHolder {
db: db::DatabasePool, db: db::DatabasePool,
state: State, state: State,
} }
#[messages] #[messages]
impl KeyHolderActor { impl KeyHolder {
pub async fn new(db: db::DatabasePool) -> Result<Self, Error> { pub async fn new(db: db::DatabasePool) -> Result<Self, Error> {
let state = { let state = {
let mut conn = db.get().await?; let mut conn = db.get().await?;
@@ -90,21 +89,7 @@ impl KeyHolderActor {
match root_key_history { match root_key_history {
Some(root_key_history) => State::Sealed { Some(root_key_history) => State::Sealed {
data_encryption_nonce: Nonce::try_from( root_key_history_id: root_key_history.id,
root_key_history.data_encryption_nonce.as_slice(),
)
.map_err(|_| {
error!("Broken database: invalid data encryption nonce");
Error::BrokenDatabase
})?,
root_key_encryption_nonce: Nonce::try_from(
root_key_history.root_key_encryption_nonce.as_slice(),
)
.map_err(|_| {
error!("Broken database: invalid root key encryption nonce");
Error::BrokenDatabase
})?,
encrypted_root_key: root_key_history,
}, },
None => State::Unbootstrapped, None => State::Unbootstrapped,
} }
@@ -113,6 +98,44 @@ impl KeyHolderActor {
Ok(Self { db, state }) Ok(Self { db, state })
} }
// Exclusive transaction to avoid race condtions if multiple keyholders write
// additional layer of protection against nonce-reuse
async fn get_new_nonce(pool: &db::DatabasePool, root_key_id: i32) -> Result<Nonce, Error> {
let mut conn = pool.get().await?;
let nonce = conn
.exclusive_transaction(|conn| {
Box::pin(async move {
let current_nonce: Vec<u8> = schema::root_key_history::table
.filter(schema::root_key_history::id.eq(root_key_id))
.select(schema::root_key_history::data_encryption_nonce)
.first(conn)
.await?;
let mut nonce =
v1::Nonce::try_from(current_nonce.as_slice()).map_err(|_| {
error!(
"Broken database: invalid nonce for root key history id={}",
root_key_id
);
Error::BrokenDatabase
})?;
nonce.increment();
update(schema::root_key_history::table)
.filter(schema::root_key_history::id.eq(root_key_id))
.set(schema::root_key_history::data_encryption_nonce.eq(nonce.to_vec()))
.execute(conn)
.await?;
Result::<_, Error>::Ok(nonce)
})
})
.await?;
Ok(nonce)
}
#[message] #[message]
pub async fn bootstrap(&mut self, seal_key_raw: MemSafe<Vec<u8>>) -> Result<(), Error> { pub async fn bootstrap(&mut self, seal_key_raw: MemSafe<Vec<u8>>) -> Result<(), Error> {
if !matches!(self.state, State::Unbootstrapped) { if !matches!(self.state, State::Unbootstrapped) {
@@ -122,6 +145,7 @@ impl KeyHolderActor {
let mut seal_key = v1::derive_seal_key(seal_key_raw, &salt); let mut seal_key = v1::derive_seal_key(seal_key_raw, &salt);
let mut root_key = KeyCell::new_secure_random(); let mut root_key = KeyCell::new_secure_random();
// Zero nonces are fine because they are one-time
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();
@@ -168,7 +192,6 @@ impl KeyHolderActor {
self.state = State::Unsealed { self.state = State::Unsealed {
root_key, root_key,
root_key_history_id, root_key_history_id,
nonce: data_encryption_nonce,
}; };
info!("Keyholder bootstrapped successfully"); info!("Keyholder bootstrapped successfully");
@@ -179,36 +202,52 @@ impl KeyHolderActor {
#[message] #[message]
pub async fn try_unseal(&mut self, seal_key_raw: MemSafe<Vec<u8>>) -> Result<(), Error> { pub async fn try_unseal(&mut self, seal_key_raw: MemSafe<Vec<u8>>) -> Result<(), Error> {
let State::Sealed { let State::Sealed {
encrypted_root_key, root_key_history_id,
data_encryption_nonce, } = &self.state
root_key_encryption_nonce,
} = &mut self.state
else { else {
return Err(Error::NotBootstrapped); return Err(Error::NotBootstrapped);
}; };
let salt = &encrypted_root_key.salt; // We don't want to hold connection while doing expensive KDF work
let current_key = {
let mut conn = self.db.get().await?;
schema::root_key_history::table
.filter(schema::root_key_history::id.eq(*root_key_history_id))
.select(schema::root_key_history::data_encryption_nonce )
.select(RootKeyHistory::as_select() )
.first(&mut conn)
.await?
};
let salt = &current_key.salt;
let salt = v1::Salt::try_from(salt.as_slice()).map_err(|_| { let salt = v1::Salt::try_from(salt.as_slice()).map_err(|_| {
error!("Broken database: invalid salt for root key"); error!("Broken database: invalid salt for root key");
Error::BrokenDatabase Error::BrokenDatabase
})?; })?;
let mut seal_key = v1::derive_seal_key(seal_key_raw, &salt); let mut seal_key = v1::derive_seal_key(seal_key_raw, &salt);
let mut root_key = MemSafe::new(encrypted_root_key.ciphertext.clone()).unwrap(); let mut root_key = MemSafe::new(current_key.ciphertext.clone()).unwrap();
let nonce = v1::Nonce::try_from(current_key.root_key_encryption_nonce.as_slice()).map_err(
|_| {
error!("Broken database: invalid nonce for root key");
Error::BrokenDatabase
},
)?;
seal_key seal_key
.decrypt_in_place(root_key_encryption_nonce, v1::ROOT_KEY_TAG, &mut root_key) .decrypt_in_place(&nonce, v1::ROOT_KEY_TAG, &mut root_key)
.map_err(|err| { .map_err(|err| {
error!(?err, "Failed to unseal root key: invalid seal key"); error!(?err, "Failed to unseal root key: invalid seal key");
Error::InvalidKey Error::InvalidKey
})?; })?;
self.state = State::Unsealed { self.state = State::Unsealed {
root_key_history_id: encrypted_root_key.id, root_key_history_id: current_key.id,
root_key: v1::KeyCell::try_from(root_key).map_err(|err| { root_key: v1::KeyCell::try_from(root_key).map_err(|err| {
error!(?err, "Broken database: invalid encryption key size"); error!(?err, "Broken database: invalid encryption key size");
Error::BrokenDatabase Error::BrokenDatabase
})?, })?,
nonce: std::mem::take(data_encryption_nonce), // we are replacing state, so it's safe to take the nonce out of it
}; };
info!("Keyholder unsealed successfully"); info!("Keyholder unsealed successfully");
@@ -222,14 +261,17 @@ impl KeyHolderActor {
let State::Unsealed { root_key, .. } = &mut self.state else { let State::Unsealed { root_key, .. } = &mut self.state else {
return Err(Error::NotBootstrapped); return Err(Error::NotBootstrapped);
}; };
let row: models::AeadEncrypted = {
let mut conn = self.db.get().await?; let mut conn = self.db.get().await?;
let row: models::AeadEncrypted = schema::aead_encrypted::table schema::aead_encrypted::table
.select(models::AeadEncrypted::as_select()) .select(models::AeadEncrypted::as_select())
.filter(schema::aead_encrypted::id.eq(aead_id)) .filter(schema::aead_encrypted::id.eq(aead_id))
.first(&mut conn) .first(&mut conn)
.await .await
.optional()? .optional()?
.ok_or(Error::NotFound)?; .ok_or(Error::NotFound)?
};
let nonce = v1::Nonce::try_from(row.current_nonce.as_slice()).map_err(|_| { let nonce = v1::Nonce::try_from(row.current_nonce.as_slice()).map_err(|_| {
error!( error!(
@@ -249,14 +291,14 @@ impl KeyHolderActor {
let State::Unsealed { let State::Unsealed {
root_key, root_key,
root_key_history_id, root_key_history_id,
nonce,
} = &mut self.state } = &mut self.state
else { else {
return Err(Error::NotBootstrapped); return Err(Error::NotBootstrapped);
}; };
let mut conn = self.db.get().await?; // Order matters here - `get_new_nonce` acquires connection, so we need to call it before next acquire
nonce.increment(); // 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().unwrap();
let ciphertext_buffer: &mut Vec<u8> = ciphertext_buffer.as_mut(); let ciphertext_buffer: &mut Vec<u8> = ciphertext_buffer.as_mut();
@@ -264,43 +306,41 @@ impl KeyHolderActor {
let ciphertext = std::mem::take(ciphertext_buffer); let ciphertext = std::mem::take(ciphertext_buffer);
let aead_id: i32 = conn let mut conn = self.db.get().await?;
.transaction(|conn| {
Box::pin(async move {
let aead_id: i32 = insert_into(schema::aead_encrypted::table) let aead_id: i32 = insert_into(schema::aead_encrypted::table)
.values(&models::NewAeadEncrypted { .values(&models::NewAeadEncrypted {
ciphertext, ciphertext,
tag: v1::TAG.to_vec(), tag: v1::TAG.to_vec(),
current_nonce: nonce.to_vec(), current_nonce: nonce.to_vec(),
schema_version: 1, schema_version: 1,
associated_root_key_id: *root_key_history_id,
created_at: chrono::Utc::now().timestamp() as i32, created_at: chrono::Utc::now().timestamp() as i32,
}) })
.returning(schema::aead_encrypted::id) .returning(schema::aead_encrypted::id)
.get_result(conn) .get_result(&mut conn)
.await?;
update(schema::root_key_history::table)
.filter(schema::root_key_history::id.eq(*root_key_history_id))
.set(schema::root_key_history::data_encryption_nonce.eq(nonce.to_vec()))
.execute(conn)
.await?;
Result::<_, diesel::result::Error>::Ok(aead_id)
})
})
.await?; .await?;
Ok(aead_id) Ok(aead_id)
} }
#[message]
pub fn get_state(&self) -> StateDiscriminants {
self.state.discriminant()
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::HashSet; use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use diesel::dsl::insert_into; use diesel::dsl::{insert_into, sql_query, update};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use futures::stream::TryUnfold;
use kameo::actor::{ActorRef, Spawn as _};
use memsafe::MemSafe; use memsafe::MemSafe;
use tokio::sync::Mutex;
use tokio::task::JoinSet;
use crate::db::{self, models::ArbiterSetting}; use crate::db::{self, models::ArbiterSetting};
@@ -320,20 +360,49 @@ mod tests {
.unwrap(); .unwrap();
} }
async fn bootstrapped_actor(db: &db::DatabasePool) -> KeyHolderActor { async fn bootstrapped_actor(db: &db::DatabasePool) -> KeyHolder {
seed_settings(db).await; seed_settings(db).await;
let mut actor = KeyHolderActor::new(db.clone()).await.unwrap(); let mut actor = KeyHolder::new(db.clone()).await.unwrap();
let seal_key = MemSafe::new(b"test-seal-key".to_vec()).unwrap(); let seal_key = MemSafe::new(b"test-seal-key".to_vec()).unwrap();
actor.bootstrap(seal_key).await.unwrap(); actor.bootstrap(seal_key).await.unwrap();
actor actor
} }
async fn write_concurrently(
actor: ActorRef<KeyHolder>,
prefix: &'static str,
count: usize,
) -> Vec<(i32, Vec<u8>)> {
let mut set = JoinSet::new();
for i in 0..count {
let actor = actor.clone();
set.spawn(async move {
let plaintext = format!("{prefix}-{i}").into_bytes();
let id = {
actor
.ask(CreateNew {
plaintext: MemSafe::new(plaintext.clone()).unwrap(),
})
.await
.unwrap()
};
(id, plaintext)
});
}
let mut out = Vec::with_capacity(count);
while let Some(res) = set.join_next().await {
out.push(res.unwrap());
}
out
}
#[tokio::test] #[tokio::test]
#[test_log::test] #[test_log::test]
async fn test_bootstrap() { async fn test_bootstrap() {
let db = db::create_test_pool().await; let db = db::create_test_pool().await;
seed_settings(&db).await; seed_settings(&db).await;
let mut actor = KeyHolderActor::new(db.clone()).await.unwrap(); let mut actor = KeyHolder::new(db.clone()).await.unwrap();
assert!(matches!(actor.state, State::Unbootstrapped)); assert!(matches!(actor.state, State::Unbootstrapped));
@@ -389,7 +458,7 @@ mod tests {
async fn test_create_new_before_bootstrap_fails() { async fn test_create_new_before_bootstrap_fails() {
let db = db::create_test_pool().await; let db = db::create_test_pool().await;
seed_settings(&db).await; seed_settings(&db).await;
let mut actor = KeyHolderActor::new(db).await.unwrap(); let mut actor = KeyHolder::new(db).await.unwrap();
let err = actor let err = actor
.create_new(MemSafe::new(b"data".to_vec()).unwrap()) .create_new(MemSafe::new(b"data".to_vec()).unwrap())
@@ -403,7 +472,7 @@ mod tests {
async fn test_decrypt_before_bootstrap_fails() { async fn test_decrypt_before_bootstrap_fails() {
let db = db::create_test_pool().await; let db = db::create_test_pool().await;
seed_settings(&db).await; seed_settings(&db).await;
let mut actor = KeyHolderActor::new(db).await.unwrap(); let mut actor = KeyHolder::new(db).await.unwrap();
let err = actor.decrypt(1).await.unwrap_err(); let err = actor.decrypt(1).await.unwrap_err();
assert!(matches!(err, Error::NotBootstrapped)); assert!(matches!(err, Error::NotBootstrapped));
@@ -426,7 +495,7 @@ mod tests {
let actor = bootstrapped_actor(&db).await; let actor = bootstrapped_actor(&db).await;
drop(actor); drop(actor);
let actor2 = KeyHolderActor::new(db).await.unwrap(); let actor2 = KeyHolder::new(db).await.unwrap();
assert!(matches!(actor2.state, State::Sealed { .. })); assert!(matches!(actor2.state, State::Sealed { .. }));
} }
@@ -495,7 +564,7 @@ mod tests {
.unwrap(); .unwrap();
drop(actor); drop(actor);
let mut actor = KeyHolderActor::new(db.clone()).await.unwrap(); let mut actor = KeyHolder::new(db.clone()).await.unwrap();
assert!(matches!(actor.state, State::Sealed { .. })); assert!(matches!(actor.state, State::Sealed { .. }));
let seal_key = MemSafe::new(b"test-seal-key".to_vec()).unwrap(); let seal_key = MemSafe::new(b"test-seal-key".to_vec()).unwrap();
@@ -520,7 +589,7 @@ mod tests {
.unwrap(); .unwrap();
drop(actor); drop(actor);
let mut actor = KeyHolderActor::new(db.clone()).await.unwrap(); let mut actor = KeyHolder::new(db.clone()).await.unwrap();
assert!(matches!(actor.state, State::Sealed { .. })); assert!(matches!(actor.state, State::Sealed { .. }));
// wrong password // wrong password
@@ -580,4 +649,291 @@ mod tests {
assert_eq!(*d1.read().unwrap(), plaintext); assert_eq!(*d1.read().unwrap(), plaintext);
assert_eq!(*d2.read().unwrap(), plaintext); assert_eq!(*d2.read().unwrap(), plaintext);
} }
#[tokio::test]
#[test_log::test]
async fn concurrent_create_new_no_duplicate_nonces_() {
let db = db::create_test_pool().await;
let actor = KeyHolder::spawn(bootstrapped_actor(&db).await);
let writes = write_concurrently(actor, "nonce-unique", 32).await;
assert_eq!(writes.len(), 32);
let mut conn = db.get().await.unwrap();
let rows: Vec<models::AeadEncrypted> = schema::aead_encrypted::table
.select(models::AeadEncrypted::as_select())
.load(&mut conn)
.await
.unwrap();
assert_eq!(rows.len(), 32);
let nonces: Vec<&Vec<u8>> = rows.iter().map(|r| &r.current_nonce).collect();
let unique: HashSet<&Vec<u8>> = nonces.iter().copied().collect();
assert_eq!(nonces.len(), unique.len(), "all nonces must be unique");
}
#[tokio::test]
#[test_log::test]
async fn concurrent_create_new_root_nonce_never_moves_backward() {
let db = db::create_test_pool().await;
let actor = KeyHolder::spawn(bootstrapped_actor(&db).await);
write_concurrently(actor, "root-max", 24).await;
let mut conn = db.get().await.unwrap();
let rows: Vec<models::AeadEncrypted> = schema::aead_encrypted::table
.select(models::AeadEncrypted::as_select())
.load(&mut conn)
.await
.unwrap();
let max_nonce = rows
.iter()
.map(|r| r.current_nonce.clone())
.max()
.expect("at least one row");
let root_row: models::RootKeyHistory = schema::root_key_history::table
.select(models::RootKeyHistory::as_select())
.first(&mut conn)
.await
.unwrap();
assert_eq!(root_row.data_encryption_nonce, max_nonce);
}
#[tokio::test]
#[test_log::test]
async fn nonce_monotonic_even_when_nonce_allocation_interleaves() {
let db = db::create_test_pool().await;
let mut actor = bootstrapped_actor(&db).await;
let root_key_history_id = match actor.state {
State::Unsealed {
root_key_history_id,
..
} => root_key_history_id,
_ => panic!("expected unsealed state"),
};
let n1 = KeyHolder::get_new_nonce(&db, root_key_history_id)
.await
.unwrap();
let n2 = KeyHolder::get_new_nonce(&db, root_key_history_id)
.await
.unwrap();
assert!(n2.to_vec() > n1.to_vec(), "nonce must increase");
let mut conn = db.get().await.unwrap();
let root_row: models::RootKeyHistory = schema::root_key_history::table
.select(models::RootKeyHistory::as_select())
.first(&mut conn)
.await
.unwrap();
assert_eq!(root_row.data_encryption_nonce, n2.to_vec());
let id = actor
.create_new(MemSafe::new(b"post-interleave".to_vec()).unwrap())
.await
.unwrap();
let row: models::AeadEncrypted = schema::aead_encrypted::table
.filter(schema::aead_encrypted::id.eq(id))
.select(models::AeadEncrypted::as_select())
.first(&mut conn)
.await
.unwrap();
assert!(
row.current_nonce > n2.to_vec(),
"next write must advance nonce"
);
}
#[tokio::test]
#[test_log::test]
async fn insert_failure_does_not_create_partial_row() {
let db = db::create_test_pool().await;
let mut actor = bootstrapped_actor(&db).await;
let root_key_history_id = match actor.state {
State::Unsealed {
root_key_history_id,
..
} => root_key_history_id,
_ => panic!("expected unsealed state"),
};
let mut conn = db.get().await.unwrap();
let before_count: i64 = schema::aead_encrypted::table
.count()
.get_result(&mut conn)
.await
.unwrap();
let before_root_nonce: Vec<u8> = schema::root_key_history::table
.filter(schema::root_key_history::id.eq(root_key_history_id))
.select(schema::root_key_history::data_encryption_nonce)
.first(&mut conn)
.await
.unwrap();
sql_query(
"CREATE TRIGGER fail_aead_insert BEFORE INSERT ON aead_encrypted BEGIN SELECT RAISE(ABORT, 'forced test failure'); END;",
)
.execute(&mut conn)
.await
.unwrap();
drop(conn);
let err = actor
.create_new(MemSafe::new(b"should fail".to_vec()).unwrap())
.await
.unwrap_err();
assert!(matches!(err, Error::DatabaseTransaction(_)));
let mut conn = db.get().await.unwrap();
sql_query("DROP TRIGGER fail_aead_insert;")
.execute(&mut conn)
.await
.unwrap();
let after_count: i64 = schema::aead_encrypted::table
.count()
.get_result(&mut conn)
.await
.unwrap();
assert_eq!(
before_count, after_count,
"failed insert must not create row"
);
let after_root_nonce: Vec<u8> = schema::root_key_history::table
.filter(schema::root_key_history::id.eq(root_key_history_id))
.select(schema::root_key_history::data_encryption_nonce)
.first(&mut conn)
.await
.unwrap();
assert!(
after_root_nonce > before_root_nonce,
"current behavior allows nonce gap on failed insert"
);
}
#[tokio::test]
#[test_log::test]
async fn decrypt_roundtrip_after_high_concurrency() {
let db = db::create_test_pool().await;
let actor = KeyHolder::spawn(bootstrapped_actor(&db).await);
let writes = write_concurrently(actor, "roundtrip", 40).await;
let expected: HashMap<i32, Vec<u8>> = writes.into_iter().collect();
let mut decryptor = KeyHolder::new(db.clone()).await.unwrap();
decryptor
.try_unseal(MemSafe::new(b"test-seal-key".to_vec()).unwrap())
.await
.unwrap();
for (id, plaintext) in expected {
let mut decrypted = decryptor.decrypt(id).await.unwrap();
assert_eq!(*decrypted.read().unwrap(), plaintext);
}
}
// #[tokio::test]
// #[test_log::test]
// async fn swapping_ciphertext_and_nonce_between_rows_changes_logical_binding() {
// let db = db::create_test_pool().await;
// let mut actor = bootstrapped_actor(&db).await;
// let plaintext1 = b"entry-one";
// let plaintext2 = b"entry-two";
// let id1 = actor
// .create_new(MemSafe::new(plaintext1.to_vec()).unwrap())
// .await
// .unwrap();
// let id2 = actor
// .create_new(MemSafe::new(plaintext2.to_vec()).unwrap())
// .await
// .unwrap();
// let mut conn = db.get().await.unwrap();
// let row1: models::AeadEncrypted = schema::aead_encrypted::table
// .filter(schema::aead_encrypted::id.eq(id1))
// .select(models::AeadEncrypted::as_select())
// .first(&mut conn)
// .await
// .unwrap();
// let row2: models::AeadEncrypted = schema::aead_encrypted::table
// .filter(schema::aead_encrypted::id.eq(id2))
// .select(models::AeadEncrypted::as_select())
// .first(&mut conn)
// .await
// .unwrap();
// update(schema::aead_encrypted::table.filter(schema::aead_encrypted::id.eq(id1)))
// .set((
// schema::aead_encrypted::ciphertext.eq(row2.ciphertext.clone()),
// schema::aead_encrypted::current_nonce.eq(row2.current_nonce.clone()),
// ))
// .execute(&mut conn)
// .await
// .unwrap();
// update(schema::aead_encrypted::table.filter(schema::aead_encrypted::id.eq(id2)))
// .set((
// schema::aead_encrypted::ciphertext.eq(row1.ciphertext.clone()),
// schema::aead_encrypted::current_nonce.eq(row1.current_nonce.clone()),
// ))
// .execute(&mut conn)
// .await
// .unwrap();
// let mut d1 = actor.decrypt(id1).await.unwrap();
// let mut d2 = actor.decrypt(id2).await.unwrap();
// assert_eq!(*d1.read().unwrap(), plaintext2);
// assert_eq!(*d2.read().unwrap(), plaintext1);
// }
#[tokio::test]
#[test_log::test]
async fn broken_db_nonce_format_fails_closed() {
// malformed root_key_history nonce must fail create_new
let db = db::create_test_pool().await;
let mut actor = bootstrapped_actor(&db).await;
let root_key_history_id = match actor.state {
State::Unsealed {
root_key_history_id,
..
} => root_key_history_id,
_ => panic!("expected unsealed state"),
};
let mut conn = db.get().await.unwrap();
update(
schema::root_key_history::table
.filter(schema::root_key_history::id.eq(root_key_history_id)),
)
.set(schema::root_key_history::data_encryption_nonce.eq(vec![1, 2, 3]))
.execute(&mut conn)
.await
.unwrap();
drop(conn);
let err = actor
.create_new(MemSafe::new(b"must fail".to_vec()).unwrap())
.await
.unwrap_err();
assert!(matches!(err, Error::BrokenDatabase));
// malformed per-row nonce must fail decrypt
let db = db::create_test_pool().await;
let mut actor = bootstrapped_actor(&db).await;
let id = actor
.create_new(MemSafe::new(b"decrypt target".to_vec()).unwrap())
.await
.unwrap();
let mut conn = db.get().await.unwrap();
update(schema::aead_encrypted::table.filter(schema::aead_encrypted::id.eq(id)))
.set(schema::aead_encrypted::current_nonce.eq(vec![7, 8]))
.execute(&mut conn)
.await
.unwrap();
drop(conn);
let err = actor.decrypt(id).await.unwrap_err();
assert!(matches!(err, Error::BrokenDatabase));
}
} }

View File

@@ -13,6 +13,7 @@ use rand::{
pub const ROOT_KEY_TAG: &[u8] = "arbiter/seal/v1".as_bytes(); pub const ROOT_KEY_TAG: &[u8] = "arbiter/seal/v1".as_bytes();
pub const TAG: &[u8] = "arbiter/private-key/v1".as_bytes(); pub const TAG: &[u8] = "arbiter/private-key/v1".as_bytes();
pub const NONCE_LENGTH: usize = 24; pub const NONCE_LENGTH: usize = 24;
#[derive(Default)] #[derive(Default)]

View File

@@ -24,6 +24,7 @@ pub struct AeadEncrypted {
pub tag: Vec<u8>, pub tag: Vec<u8>,
pub current_nonce: Vec<u8>, pub current_nonce: Vec<u8>,
pub schema_version: i32, pub schema_version: i32,
pub associated_root_key_id: i32, // references root_key_history.id
pub created_at: i32, pub created_at: i32,
} }

View File

@@ -7,6 +7,7 @@ diesel::table! {
ciphertext -> Binary, ciphertext -> Binary,
tag -> Binary, tag -> Binary,
schema_version -> Integer, schema_version -> Integer,
associated_root_key_id -> Integer,
created_at -> Integer, created_at -> Integer,
} }
} }
@@ -52,6 +53,7 @@ diesel::table! {
} }
} }
diesel::joinable!(aead_encrypted -> root_key_history (associated_root_key_id));
diesel::joinable!(arbiter_settings -> root_key_history (root_key_id)); diesel::joinable!(arbiter_settings -> root_key_history (root_key_id));
diesel::allow_tables_to_appear_in_same_query!( diesel::allow_tables_to_appear_in_same_query!(

View File

@@ -1,6 +1,11 @@
# cargo-vet audits file # cargo-vet audits file
[[audits.similar]]
who = "hdbg <httpdebugger@protonmail.com>"
criteria = "safe-to-deploy"
version = "2.2.1"
[[audits.test-log]] [[audits.test-log]]
who = "hdbg <httpdebugger@protonmail.com>" who = "hdbg <httpdebugger@protonmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -11,6 +16,12 @@ who = "hdbg <httpdebugger@protonmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "0.2.18 -> 0.2.19" delta = "0.2.18 -> 0.2.19"
[[trusted.cc]]
criteria = "safe-to-deploy"
user-id = 55123 # rust-lang-owner
start = "2022-10-29"
end = "2027-02-16"
[[trusted.h2]] [[trusted.h2]]
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
user-id = 359 # Sean McArthur (seanmonstar) user-id = 359 # Sean McArthur (seanmonstar)
@@ -29,6 +40,12 @@ user-id = 359 # Sean McArthur (seanmonstar)
start = "2022-01-15" start = "2022-01-15"
end = "2027-02-14" end = "2027-02-14"
[[trusted.libc]]
criteria = "safe-to-deploy"
user-id = 55123 # rust-lang-owner
start = "2024-08-15"
end = "2027-02-16"
[[trusted.rustix]] [[trusted.rustix]]
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
user-id = 6825 # Dan Gohman (sunfishcode) user-id = 6825 # Dan Gohman (sunfishcode)
@@ -46,3 +63,33 @@ criteria = "safe-to-deploy"
user-id = 3618 # David Tolnay (dtolnay) user-id = 3618 # David Tolnay (dtolnay)
start = "2019-03-01" start = "2019-03-01"
end = "2027-02-14" end = "2027-02-14"
[[trusted.thread_local]]
criteria = "safe-to-deploy"
user-id = 2915 # Amanieu d'Antras (Amanieu)
start = "2019-09-07"
end = "2027-02-16"
[[trusted.toml]]
criteria = "safe-to-deploy"
user-id = 6743 # Ed Page (epage)
start = "2022-12-14"
end = "2027-02-16"
[[trusted.toml_parser]]
criteria = "safe-to-deploy"
user-id = 6743 # Ed Page (epage)
start = "2025-07-08"
end = "2027-02-16"
[[trusted.tonic-build]]
criteria = "safe-to-deploy"
user-id = 10
start = "2019-09-10"
end = "2027-02-16"
[[trusted.windows-sys]]
criteria = "safe-to-deploy"
user-id = 64539 # Kenny Kerr (kennykerr)
start = "2021-11-15"
end = "2027-02-16"

View File

@@ -13,6 +13,9 @@ url = "https://raw.githubusercontent.com/google/supply-chain/main/audits.toml"
[imports.mozilla] [imports.mozilla]
url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml" url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml"
[imports.zcash]
url = "https://raw.githubusercontent.com/zcash/rust-ecosystem/main/supply-chain/audits.toml"
[[exemptions.addr2line]] [[exemptions.addr2line]]
version = "0.25.1" version = "0.25.1"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -41,10 +44,6 @@ criteria = "safe-to-deploy"
version = "0.1.89" version = "0.1.89"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.autocfg]]
version = "1.5.0"
criteria = "safe-to-deploy"
[[exemptions.aws-lc-rs]] [[exemptions.aws-lc-rs]]
version = "1.15.4" version = "1.15.4"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -193,10 +192,6 @@ criteria = "safe-to-deploy"
version = "0.2.0" version = "0.2.0"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.dunce]]
version = "1.0.5"
criteria = "safe-to-deploy"
[[exemptions.dyn-clone]] [[exemptions.dyn-clone]]
version = "1.0.20" version = "1.0.20"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -209,10 +204,6 @@ criteria = "safe-to-deploy"
version = "3.0.0-pre.6" version = "3.0.0-pre.6"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.errno]]
version = "0.3.14"
criteria = "safe-to-deploy"
[[exemptions.fiat-crypto]] [[exemptions.fiat-crypto]]
version = "0.3.0" version = "0.3.0"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -261,10 +252,6 @@ criteria = "safe-to-deploy"
version = "1.4.0" version = "1.4.0"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.http-body]]
version = "1.0.1"
criteria = "safe-to-deploy"
[[exemptions.http-body-util]] [[exemptions.http-body-util]]
version = "0.1.3" version = "0.1.3"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -329,10 +316,6 @@ criteria = "safe-to-deploy"
version = "0.19.0" version = "0.19.0"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.libc]]
version = "0.2.181"
criteria = "safe-to-deploy"
[[exemptions.libsqlite3-sys]] [[exemptions.libsqlite3-sys]]
version = "0.35.0" version = "0.35.0"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -525,10 +508,6 @@ criteria = "safe-to-deploy"
version = "0.1.27" version = "0.1.27"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.rustc_version]]
version = "0.4.1"
criteria = "safe-to-deploy"
[[exemptions.rusticata-macros]] [[exemptions.rusticata-macros]]
version = "4.1.0" version = "4.1.0"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -545,10 +524,6 @@ criteria = "safe-to-deploy"
version = "0.103.9" version = "0.103.9"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.rustversion]]
version = "1.0.22"
criteria = "safe-to-deploy"
[[exemptions.scoped-futures]] [[exemptions.scoped-futures]]
version = "0.1.4" version = "0.1.4"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -653,10 +628,6 @@ criteria = "safe-to-deploy"
version = "2.0.18" version = "2.0.18"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.thread_local]]
version = "1.1.9"
criteria = "safe-to-run"
[[exemptions.time]] [[exemptions.time]]
version = "0.3.47" version = "0.3.47"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -689,14 +660,6 @@ criteria = "safe-to-deploy"
version = "0.7.18" version = "0.7.18"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.toml]]
version = "0.9.11+spec-1.1.0"
criteria = "safe-to-deploy"
[[exemptions.toml_parser]]
version = "1.0.6+spec-1.1.0"
criteria = "safe-to-deploy"
[[exemptions.tonic]] [[exemptions.tonic]]
version = "0.14.3" version = "0.14.3"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -741,10 +704,6 @@ criteria = "safe-to-deploy"
version = "0.3.22" version = "0.3.22"
criteria = "safe-to-run" criteria = "safe-to-run"
[[exemptions.try-lock]]
version = "0.2.5"
criteria = "safe-to-deploy"
[[exemptions.typenum]] [[exemptions.typenum]]
version = "1.19.0" version = "1.19.0"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -769,10 +728,6 @@ criteria = "safe-to-deploy"
version = "1.20.0" version = "1.20.0"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.want]]
version = "0.3.1"
criteria = "safe-to-deploy"
[[exemptions.wasi]] [[exemptions.wasi]]
version = "0.11.1+wasi-snapshot-preview1" version = "0.11.1+wasi-snapshot-preview1"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -817,10 +772,6 @@ criteria = "safe-to-deploy"
version = "0.59.3" version = "0.59.3"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.windows-link]]
version = "0.2.1"
criteria = "safe-to-deploy"
[[exemptions.windows-result]] [[exemptions.windows-result]]
version = "0.4.1" version = "0.4.1"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -829,18 +780,6 @@ criteria = "safe-to-deploy"
version = "0.5.1" version = "0.5.1"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.windows-sys]]
version = "0.52.0"
criteria = "safe-to-deploy"
[[exemptions.windows-sys]]
version = "0.60.2"
criteria = "safe-to-deploy"
[[exemptions.windows-sys]]
version = "0.61.2"
criteria = "safe-to-deploy"
[[exemptions.windows-targets]] [[exemptions.windows-targets]]
version = "0.52.6" version = "0.52.6"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -925,10 +864,6 @@ criteria = "safe-to-deploy"
version = "0.5.2" version = "0.5.2"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
[[exemptions.zeroize]]
version = "1.8.2"
criteria = "safe-to-deploy"
[[exemptions.zmij]] [[exemptions.zmij]]
version = "1.0.20" version = "1.0.20"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"

View File

@@ -41,6 +41,12 @@ user-id = 359
user-login = "seanmonstar" user-login = "seanmonstar"
user-name = "Sean McArthur" user-name = "Sean McArthur"
[[publisher.libc]]
version = "0.2.182"
when = "2026-02-13"
user-id = 55123
user-login = "rust-lang-owner"
[[publisher.rustix]] [[publisher.rustix]]
version = "1.1.3" version = "1.1.3"
when = "2025-12-23" when = "2025-12-23"
@@ -63,12 +69,33 @@ user-login = "dtolnay"
user-name = "David Tolnay" user-name = "David Tolnay"
[[publisher.syn]] [[publisher.syn]]
version = "2.0.114" version = "2.0.115"
when = "2026-01-07" when = "2026-02-12"
user-id = 3618 user-id = 3618
user-login = "dtolnay" user-login = "dtolnay"
user-name = "David Tolnay" user-name = "David Tolnay"
[[publisher.thread_local]]
version = "1.1.9"
when = "2025-06-12"
user-id = 2915
user-login = "Amanieu"
user-name = "Amanieu d'Antras"
[[publisher.toml]]
version = "0.9.12+spec-1.1.0"
when = "2026-02-10"
user-id = 6743
user-login = "epage"
user-name = "Ed Page"
[[publisher.toml_parser]]
version = "1.0.8+spec-1.1.0"
when = "2026-02-12"
user-id = 6743
user-login = "epage"
user-name = "Ed Page"
[[publisher.unicode-width]] [[publisher.unicode-width]]
version = "0.1.14" version = "0.1.14"
when = "2024-09-19" when = "2024-09-19"
@@ -120,6 +147,34 @@ version = "0.244.0"
when = "2026-01-06" when = "2026-01-06"
trusted-publisher = "github:bytecodealliance/wasm-tools" trusted-publisher = "github:bytecodealliance/wasm-tools"
[[publisher.windows-sys]]
version = "0.52.0"
when = "2023-11-15"
user-id = 64539
user-login = "kennykerr"
user-name = "Kenny Kerr"
[[publisher.windows-sys]]
version = "0.59.0"
when = "2024-07-30"
user-id = 64539
user-login = "kennykerr"
user-name = "Kenny Kerr"
[[publisher.windows-sys]]
version = "0.60.2"
when = "2025-06-12"
user-id = 64539
user-login = "kennykerr"
user-name = "Kenny Kerr"
[[publisher.windows-sys]]
version = "0.61.2"
when = "2025-10-06"
user-id = 64539
user-login = "kennykerr"
user-name = "Kenny Kerr"
[[publisher.wit-bindgen]] [[publisher.wit-bindgen]]
version = "0.51.0" version = "0.51.0"
when = "2026-01-12" when = "2026-01-12"
@@ -265,6 +320,12 @@ criteria = "safe-to-deploy"
version = "1.1.2" version = "1.1.2"
notes = "Contains `unsafe` code but it's well-documented and scoped to what it's intended to be doing. Otherwise a well-focused and straightforward crate." notes = "Contains `unsafe` code but it's well-documented and scoped to what it's intended to be doing. Otherwise a well-focused and straightforward crate."
[[audits.bytecode-alliance.audits.cipher]]
who = "Andrew Brown <andrew.brown@intel.com>"
criteria = "safe-to-deploy"
version = "0.4.4"
notes = "Most unsafe is hidden by `inout` dependency; only remaining unsafe is raw-splitting a slice and an unreachable hint. Older versions of this regularly reach ~150k daily downloads."
[[audits.bytecode-alliance.audits.core-foundation-sys]] [[audits.bytecode-alliance.audits.core-foundation-sys]]
who = "Dan Gohman <dev@sunfishcode.online>" who = "Dan Gohman <dev@sunfishcode.online>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -279,6 +340,23 @@ who = "Nick Fitzgerald <fitzgen@gmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "0.2.4 -> 0.2.5" delta = "0.2.4 -> 0.2.5"
[[audits.bytecode-alliance.audits.errno]]
who = "Dan Gohman <dev@sunfishcode.online>"
criteria = "safe-to-deploy"
version = "0.3.0"
notes = "This crate uses libc and windows-sys APIs to get and set the raw OS error value."
[[audits.bytecode-alliance.audits.errno]]
who = "Dan Gohman <dev@sunfishcode.online>"
criteria = "safe-to-deploy"
delta = "0.3.0 -> 0.3.1"
notes = "Just a dependency version bump and a bug fix for redox"
[[audits.bytecode-alliance.audits.errno]]
who = "Dan Gohman <dev@sunfishcode.online>"
criteria = "safe-to-deploy"
delta = "0.3.9 -> 0.3.10"
[[audits.bytecode-alliance.audits.fastrand]] [[audits.bytecode-alliance.audits.fastrand]]
who = "Alex Crichton <alex@alexcrichton.com>" who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -385,11 +463,28 @@ criteria = "safe-to-deploy"
delta = "0.4.1 -> 0.5.0" delta = "0.4.1 -> 0.5.0"
notes = "Minor changes for a `no_std` upgrade but otherwise everything looks as expected." notes = "Minor changes for a `no_std` upgrade but otherwise everything looks as expected."
[[audits.bytecode-alliance.audits.http-body]]
who = "Pat Hickey <phickey@fastly.com>"
criteria = "safe-to-deploy"
version = "1.0.0-rc.2"
[[audits.bytecode-alliance.audits.http-body]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
delta = "1.0.0-rc.2 -> 1.0.0"
notes = "Only minor changes made for a stable release."
[[audits.bytecode-alliance.audits.iana-time-zone-haiku]] [[audits.bytecode-alliance.audits.iana-time-zone-haiku]]
who = "Dan Gohman <dev@sunfishcode.online>" who = "Dan Gohman <dev@sunfishcode.online>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
version = "0.1.2" version = "0.1.2"
[[audits.bytecode-alliance.audits.inout]]
who = "Andrew Brown <andrew.brown@intel.com>"
criteria = "safe-to-deploy"
version = "0.1.3"
notes = "A part of RustCrypto/utils, this crate is designed to handle unsafe buffers and carefully documents the safety concerns throughout. Older versions of this tally up to ~130k daily downloads."
[[audits.bytecode-alliance.audits.leb128fmt]] [[audits.bytecode-alliance.audits.leb128fmt]]
who = "Alex Crichton <alex@alexcrichton.com>" who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -443,6 +538,24 @@ criteria = "safe-to-deploy"
delta = "0.8.5 -> 0.8.9" delta = "0.8.5 -> 0.8.9"
notes = "No new unsafe code, just refactorings." notes = "No new unsafe code, just refactorings."
[[audits.bytecode-alliance.audits.nu-ansi-term]]
who = "Pat Hickey <phickey@fastly.com>"
criteria = "safe-to-deploy"
version = "0.46.0"
notes = "one use of unsafe to call windows specific api to get console handle."
[[audits.bytecode-alliance.audits.nu-ansi-term]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
delta = "0.46.0 -> 0.50.1"
notes = "Lots of stylistic/rust-related chanegs, plus new features, but nothing out of the ordrinary."
[[audits.bytecode-alliance.audits.nu-ansi-term]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
delta = "0.50.1 -> 0.50.3"
notes = "CI changes, Rust changes, nothing out of the ordinary."
[[audits.bytecode-alliance.audits.num-traits]] [[audits.bytecode-alliance.audits.num-traits]]
who = "Andrew Brown <andrew.brown@intel.com>" who = "Andrew Brown <andrew.brown@intel.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -537,12 +650,38 @@ criteria = "safe-to-run"
delta = "0.2.16 -> 0.2.18" delta = "0.2.16 -> 0.2.18"
notes = "Standard macro changes, nothing out of place" notes = "Standard macro changes, nothing out of place"
[[audits.bytecode-alliance.audits.tracing-log]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.1.3"
notes = """
This is a standard adapter between the `log` ecosystem and the `tracing`
ecosystem. There's one `unsafe` block in this crate and it's well-scoped.
"""
[[audits.bytecode-alliance.audits.tracing-log]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
delta = "0.1.3 -> 0.2.0"
notes = "Nothing out of the ordinary, a typical major version update and nothing awry."
[[audits.bytecode-alliance.audits.try-lock]]
who = "Pat Hickey <phickey@fastly.com>"
criteria = "safe-to-deploy"
version = "0.2.4"
notes = "Implements a concurrency primitive with atomics, and is not obviously incorrect"
[[audits.bytecode-alliance.audits.vcpkg]] [[audits.bytecode-alliance.audits.vcpkg]]
who = "Pat Hickey <phickey@fastly.com>" who = "Pat Hickey <phickey@fastly.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
version = "0.2.15" version = "0.2.15"
notes = "no build.rs, no macros, no unsafe. It reads the filesystem and makes copies of DLLs into OUT_DIR." notes = "no build.rs, no macros, no unsafe. It reads the filesystem and makes copies of DLLs into OUT_DIR."
[[audits.bytecode-alliance.audits.want]]
who = "Pat Hickey <phickey@fastly.com>"
criteria = "safe-to-deploy"
version = "0.3.0"
[[audits.bytecode-alliance.audits.wasm-metadata]] [[audits.bytecode-alliance.audits.wasm-metadata]]
who = "Alex Crichton <alex@alexcrichton.com>" who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -591,6 +730,13 @@ criteria = "safe-to-deploy"
delta = "0.243.0 -> 0.244.0" delta = "0.243.0 -> 0.244.0"
notes = "The Bytecode Alliance is the author of this crate" notes = "The Bytecode Alliance is the author of this crate"
[[audits.google.audits.autocfg]]
who = "Manish Goregaokar <manishearth@google.com>"
criteria = "safe-to-deploy"
version = "1.4.0"
notes = "Contains no unsafe"
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.base64]] [[audits.google.audits.base64]]
who = "amarjotgill <amarjotgill@google.com>" who = "amarjotgill <amarjotgill@google.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -719,6 +865,89 @@ delta = "0.2.9 -> 0.2.13"
notes = "Audited at https://fxrev.dev/946396" notes = "Audited at https://fxrev.dev/946396"
aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.proc-macro-error-attr]]
who = "George Burgess IV <gbiv@google.com>"
criteria = "safe-to-deploy"
version = "1.0.4"
aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
[[audits.google.audits.rand_core]]
who = "Lukasz Anforowicz <lukasza@chromium.org>"
criteria = "safe-to-deploy"
version = "0.6.4"
notes = """
For more detailed unsafe review notes please see https://crrev.com/c/6362797
"""
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.rustversion]]
who = "Lukasz Anforowicz <lukasza@chromium.org>"
criteria = "safe-to-deploy"
version = "1.0.14"
notes = """
Grepped for `-i cipher`, `-i crypto`, `'\bfs\b'``, `'\bnet\b'``, `'\bunsafe\b'``
and there were no hits except for:
* Using trivially-safe `unsafe` in test code:
```
tests/test_const.rs:unsafe fn _unsafe() {}
tests/test_const.rs:const _UNSAFE: () = unsafe { _unsafe() };
```
* Using `unsafe` in a string:
```
src/constfn.rs: "unsafe" => Qualifiers::Unsafe,
```
* Using `std::fs` in `build/build.rs` to write `${OUT_DIR}/version.expr`
which is later read back via `include!` used in `src/lib.rs`.
Version `1.0.6` of this crate has been added to Chromium in
https://source.chromium.org/chromium/chromium/src/+/28841c33c77833cc30b286f9ae24c97e7a8f4057
"""
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.rustversion]]
who = "Adrian Taylor <adetaylor@chromium.org>"
criteria = "safe-to-deploy"
delta = "1.0.14 -> 1.0.15"
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.rustversion]]
who = "danakj <danakj@chromium.org>"
criteria = "safe-to-deploy"
delta = "1.0.15 -> 1.0.16"
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.rustversion]]
who = "Dustin J. Mitchell <djmitche@chromium.org>"
criteria = "safe-to-deploy"
delta = "1.0.16 -> 1.0.17"
notes = "Just updates windows compat"
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.rustversion]]
who = "Liza Burakova <liza@chromium.org>"
criteria = "safe-to-deploy"
delta = "1.0.17 -> 1.0.18"
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.rustversion]]
who = "Dustin J. Mitchell <djmitche@chromium.org>"
criteria = "safe-to-deploy"
delta = "1.0.18 -> 1.0.19"
notes = "No unsafe, just doc changes"
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.rustversion]]
who = "Daniel Cheng <dcheng@chromium.org>"
criteria = "safe-to-deploy"
delta = "1.0.19 -> 1.0.20"
notes = "Only minor updates to documentation and the mock today used for testing."
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.smallvec]] [[audits.google.audits.smallvec]]
who = "Manish Goregaokar <manishearth@google.com>" who = "Manish Goregaokar <manishearth@google.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -736,6 +965,28 @@ Previously reviewed during security review and the audit is grandparented in.
""" """
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.strum]]
who = "danakj@chromium.org"
criteria = "safe-to-deploy"
version = "0.25.0"
notes = """
Reviewed in https://crrev.com/c/5171063
Previously reviewed during security review and the audit is grandparented in.
"""
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.google.audits.strum_macros]]
who = "danakj@chromium.org"
criteria = "safe-to-deploy"
version = "0.25.3"
notes = """
Reviewed in https://crrev.com/c/5171063
Previously reviewed during security review and the audit is grandparented in.
"""
aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT"
[[audits.mozilla.wildcard-audits.core-foundation-sys]] [[audits.mozilla.wildcard-audits.core-foundation-sys]]
who = "Bobby Holley <bobbyholley@gmail.com>" who = "Bobby Holley <bobbyholley@gmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -812,6 +1063,12 @@ criteria = "safe-to-deploy"
delta = "0.2.3 -> 0.2.4" delta = "0.2.3 -> 0.2.4"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.errno]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "0.3.1 -> 0.3.3"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.fastrand]] [[audits.mozilla.audits.fastrand]]
who = "Mike Hommey <mh+mozilla@glandium.org>" who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -929,6 +1186,16 @@ yet, but it's all valid. Otherwise it's a pretty simple crate.
""" """
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.rustc_version]]
who = "Nika Layzell <nika@thelayzells.com>"
criteria = "safe-to-deploy"
version = "0.4.0"
notes = """
Use of powerful capabilities is limited to invoking `rustc -vV` to get version
information for parsing version information.
"""
aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml"
[[audits.mozilla.audits.serde_spanned]] [[audits.mozilla.audits.serde_spanned]]
who = "Ben Dean-Kawamura <bdk@mozilla.com>" who = "Ben Dean-Kawamura <bdk@mozilla.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -955,6 +1222,12 @@ criteria = "safe-to-deploy"
delta = "1.1.0 -> 1.3.0" delta = "1.1.0 -> 1.3.0"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.similar]]
who = "Nika Layzell <nika@thelayzells.com>"
criteria = "safe-to-deploy"
delta = "2.2.1 -> 2.7.0"
aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml"
[[audits.mozilla.audits.smallvec]] [[audits.mozilla.audits.smallvec]]
who = "Erich Gubler <erichdongubler@gmail.com>" who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -967,6 +1240,30 @@ criteria = "safe-to-deploy"
delta = "0.10.0 -> 0.11.1" delta = "0.10.0 -> 0.11.1"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.strum]]
who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.25.0 -> 0.26.3"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.strum]]
who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy"
delta = "0.26.3 -> 0.27.1"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.strum_macros]]
who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.25.3 -> 0.26.4"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.strum_macros]]
who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy"
delta = "0.26.4 -> 0.27.1"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.synstructure]] [[audits.mozilla.audits.synstructure]]
who = "Nika Layzell <nika@thelayzells.com>" who = "Nika Layzell <nika@thelayzells.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -1038,3 +1335,153 @@ who = "Jan-Erik Rediger <jrediger@mozilla.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
version = "0.1.5" version = "0.1.5"
aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml"
[[audits.mozilla.audits.windows-link]]
who = "Mark Hammond <mhammond@skippinet.com.au>"
criteria = "safe-to-deploy"
version = "0.1.1"
notes = "A microsoft crate allowing unsafe calls to windows apis."
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.windows-link]]
who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy"
delta = "0.1.1 -> 0.2.0"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.zeroize]]
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
criteria = "safe-to-deploy"
version = "1.8.1"
notes = """
This code DOES contain unsafe code required to internally call volatiles
for deleting data. This is expected and documented behavior.
"""
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.zcash.audits.autocfg]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "1.4.0 -> 1.5.0"
notes = "Filesystem change is to remove the generated LLVM IR output file after probing."
aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml"
[[audits.zcash.audits.dunce]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
version = "1.0.5"
notes = """
Does what it says on the tin. No `unsafe`, and the only IO is `std::fs::canonicalize`.
Path and string handling looks plausibly correct.
"""
aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml"
[[audits.zcash.audits.errno]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.3.3 -> 0.3.8"
aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml"
[[audits.zcash.audits.errno]]
who = "Daira-Emma Hopwood <daira@jacaranda.org>"
criteria = "safe-to-deploy"
delta = "0.3.8 -> 0.3.9"
aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml"
[[audits.zcash.audits.errno]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.3.10 -> 0.3.11"
notes = "The `__errno` location for vxworks and cygwin looks correct from a quick search."
aggregated-from = "https://raw.githubusercontent.com/zcash/wallet/main/supply-chain/audits.toml"
[[audits.zcash.audits.errno]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.3.11 -> 0.3.13"
aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml"
[[audits.zcash.audits.errno]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.3.13 -> 0.3.14"
aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml"
[[audits.zcash.audits.http-body]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "1.0.0 -> 1.0.1"
aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml"
[[audits.zcash.audits.inout]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.1.3 -> 0.1.4"
aggregated-from = "https://raw.githubusercontent.com/zcash/wallet/main/supply-chain/audits.toml"
[[audits.zcash.audits.rustc_version]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.4.0 -> 0.4.1"
notes = "Changes to `Command` usage are to add support for `RUSTC_WRAPPER`."
aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml"
[[audits.zcash.audits.rustversion]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "1.0.20 -> 1.0.21"
notes = "Build script change is to fix building with `-Zfmt-debug=none`."
aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml"
[[audits.zcash.audits.rustversion]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "1.0.21 -> 1.0.22"
notes = "Changes to generated code are to prepend a clippy annotation."
aggregated-from = "https://raw.githubusercontent.com/zcash/wallet/main/supply-chain/audits.toml"
[[audits.zcash.audits.strum]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.27.1 -> 0.27.2"
aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml"
[[audits.zcash.audits.strum_macros]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.27.1 -> 0.27.2"
aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml"
[[audits.zcash.audits.try-lock]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.2.4 -> 0.2.5"
notes = "Bumps MSRV to remove unsafe code block."
aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml"
[[audits.zcash.audits.want]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.3.0 -> 0.3.1"
notes = """
Migrates to `try-lock 0.2.4` to replace some unsafe APIs that were not marked
`unsafe` (but that were being used safely).
"""
aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml"
[[audits.zcash.audits.windows-link]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "0.2.0 -> 0.2.1"
notes = "No code changes at all."
aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml"
[[audits.zcash.audits.zeroize]]
who = "Jack Grigg <jack@electriccoin.co>"
criteria = "safe-to-deploy"
delta = "1.8.1 -> 1.8.2"
notes = """
Changes to `unsafe` code are to alter how `core::mem::size_of` is named; no actual changes
to the `unsafe` logic.
"""
aggregated-from = "https://raw.githubusercontent.com/zcash/wallet/main/supply-chain/audits.toml"