Merge remote-tracking branch 'agent/benma-agent/zeroize-derived-encryption-keys'
What changed, and why it matters
This commit improves how sensitive cryptographic key material is erased from memory in the BitBox02 hardware wallet. It wraps temporary derived keys (SHA-512 outputs and a stretched seed encryption key) with a Rust helper that automatically clears the memory when the value is no longer needed. This reduces the chance that fragments of private-key-related material remain in RAM longer than necessary, but it is a hardening change rather than a fix for an obvious, directly exploitable bug.
Treat as a defensive hardening commit. Include it in routine firmware updates. Users do not need to take urgent action, but should keep firmware current. Developers should verify that upstream dependencies (`bitcoin::hashes`, Rust allocator) do not leave additional copies of the wrapped values and consider auditing other derived-key sites for similar zeroization.
Security signals we found
Use of `zeroize::Zeroizing` for derived cryptographic key material
Reduction of sensitive-data lifetime in memory
Hardening of seed-encryption key derivation path
No change to cryptographic algorithms or protocol logic
No bounds-check, input-validation, or access-control changes
Evidence from the diff
The patch changes two Rust modules to use zeroize::Zeroizing<T> for intermediate key material. In bitbox-aes/src/lib.rs, the sha512() helper now returns Zeroizing<[u8; 64]>; the resulting 64-byte value is split into encryption and HMAC keys and is zeroed when dropped. In keystore.rs, stretch_retained_seed_encryption_key() wraps the stretched HMAC-SHA256 output in Zeroizing<[u8; 32]> before converting it to a Zeroizing<Vec<u8>>. The change reduces the window in which derived key bytes may persist on the stack/heap after use, but it does not address how the underlying bitcoin::hashes or Hmac types handle their internal state, nor does it guarantee zeroization of all copies made by the compiler or allocator.
Changed components
src/rust/bitbox-aes/src/lib.rssrc/rust/bitbox02-rust/src/keystore.rsBitBox02 firmware cryptographic key handlingInspect captured patch +7 / −5
### src/rust/bitbox-aes/src/lib.rs
@@ -8,6 +8,7 @@ extern crate alloc;
use alloc::vec::Vec;
use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine, sha256};
+use zeroize::Zeroizing;
// AES block size.
const BLOCK_SIZE: usize = 16;
@@ -64,8 +65,8 @@ fn decrypt(key: &[u8; 32], cipher: &[u8]) -> Result<zeroize::Zeroizing<Vec<u8>>,
Ok(result)
}
-fn sha512(buf: &[u8]) -> [u8; 64] {
- bitcoin::hashes::sha512::Hash::hash(buf).to_byte_array()
+fn sha512(buf: &[u8]) -> Zeroizing<[u8; 64]> {
+ Zeroizing::new(bitcoin::hashes::sha512::Hash::hash(buf).to_byte_array())
}
fn hmac_sha256(key: &[u8], msg: &[u8]) -> [u8; 32] {
@@ -76,7 +77,7 @@ fn hmac_sha256(key: &[u8], msg: &[u8]) -> [u8; 32] {
}
pub fn encrypt_with_hmac(iv: &[u8; 16], key: &[u8], plain: &[u8]) -> Vec<u8> {
- let hash: [u8; 64] = sha512(key);
+ let hash = sha512(key);
let (encryption_key, authentication_key) = hash.split_at(32);
let mut cipher = encrypt(iv, encryption_key.try_into().unwrap(), plain);
let mac: [u8; 32] = hmac_sha256(authentication_key, &cipher);
@@ -85,7 +86,7 @@ pub fn encrypt_with_hmac(iv: &[u8; 16], key: &[u8], plain: &[u8]) -> Vec<u8> {
}
pub fn decrypt_with_hmac(key: &[u8], cipher: &[u8]) -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
- let hash: [u8; 64] = sha512(key);
+ let hash = sha512(key);
let (encryption_key, authentication_key) = hash.split_at(32);
if cipher.len() < 32 {
return Err(());
### src/rust/bitbox02-rust/src/keystore.rs
@@ -764,7 +764,8 @@ pub async fn stretch_retained_seed_encryption_key(
let mut engine = HmacEngine::<sha256::Hash>::new(salted_out.as_slice());
engine.input(kdf.as_slice());
- let stretched = Hmac::<sha256::Hash>::from_engine(engine).to_byte_array();
+ let stretched =
+ zeroize::Zeroizing::new(Hmac::<sha256::Hash>::from_engine(engine).to_byte_array());
Ok(zeroize::Zeroizing::new(stretched.to_vec()))
}Why this scored 37/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.