hal/securechip: make kdf output 32 bytes
What changed, and why it matters
This commit is a small code cleanup in the BitBox hardware wallet firmware. It changes the way a 32-byte secret produced by the secure chip's key-derivation function (KDF) is represented in code, switching from a variable-length vector to a fixed 32-byte array. The commit message says this is 'for better clarity.' There is no direct evidence in the commit or supplied references that this fixes a security vulnerability.
No immediate security action required. Treat as a normal refactoring/code-quality change. Reviewers may optionally verify that all call sites handle the new `Box<Zeroizing<[u8; 32]>>` type correctly and that no `.to_vec()` conversions reintroduce unnecessary heap copies.
Security signals we found
Use of `zeroize::Zeroizing` confirms secrets are cleared from memory after use, which is a defensive practice but unchanged by this patch.
No bounds-checking, input-validation, or cryptographic logic changes are present.
No mention of CVE, security bug, vulnerability, or researcher attribution in commit message or diff.
Evidence from the diff
The patch refactors the return type of the secure chip KDF across multiple Rust modules from zeroize::Zeroizing<Vec<u8>> to Box<zeroize::Zeroizing<[u8; 32]>>. This makes the 32-byte output size explicit in the type system and avoids heap-allocated vectors for a fixed-size secret. The underlying KDF behavior (HMAC-SHA256 on host fake implementations, ATECC/OPTIGA KDF calls on device) is unchanged. One placeholder implementation in bitbox03 is updated to match the new signature but remains a todo!() stub.
Changed components
src/rust/bitbox-hal/src/securechip.rssrc/rust/bitbox-platform-host/src/securechip.rssrc/rust/bitbox-securechip/src/atecc.rssrc/rust/bitbox-securechip/src/optiga.rssrc/rust/bitbox02/src/hal/securechip.rssrc/rust/bitbox02/src/securechip/imp.rssrc/rust/bitbox02/src/securechip/imp_fake.rssrc/rust/bitbox03/src/securechip.rsInspect captured patch +15 / −14
diff --git a/src/rust/bitbox-hal/src/securechip.rs b/src/rust/bitbox-hal/src/securechip.rs
index d778bb0..8c13822 100644
--- a/src/rust/bitbox-hal/src/securechip.rs
+++ b/src/rust/bitbox-hal/src/securechip.rs
@@ -78,7 +78,7 @@ pub trait SecureChip {
/// This must not increment a monotonic counter.
///
/// `msg` must be 32 bytes long.
- fn kdf(&mut self, msg: &[u8; 32]) -> Result<zeroize::Zeroizing<Vec<u8>>, Error>;
+ fn kdf(&mut self, msg: &[u8; 32]) -> Result<Box<zeroize::Zeroizing<[u8; 32]>>, Error>;
/// Signs a 32-byte attestation challenge and writes the raw 64-byte P-256 signature to
/// `signature`.
diff --git a/src/rust/bitbox-platform-host/src/securechip.rs b/src/rust/bitbox-platform-host/src/securechip.rs
index 4880a25..7c06d8a 100644
--- a/src/rust/bitbox-platform-host/src/securechip.rs
+++ b/src/rust/bitbox-platform-host/src/securechip.rs
@@ -119,7 +119,7 @@ impl bitbox_hal::SecureChip for FakeSecureChip {
))
}
- fn kdf(&mut self, msg: &[u8; 32]) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
+ fn kdf(&mut self, msg: &[u8; 32]) -> Result<Box<zeroize::Zeroizing<[u8; 32]>>, Error> {
self.event_counter += 1;
use bitcoin::hashes::{HashEngine, Hmac, HmacEngine, sha256};
@@ -128,9 +128,9 @@ impl bitbox_hal::SecureChip for FakeSecureChip {
));
engine.input(msg);
let hmac_result: Hmac<sha256::Hash> = Hmac::from_engine(engine);
- Ok(zeroize::Zeroizing::new(
- hmac_result.to_byte_array().to_vec(),
- ))
+ Ok(Box::new(zeroize::Zeroizing::new(
+ hmac_result.to_byte_array(),
+ )))
}
fn attestation_sign(
diff --git a/src/rust/bitbox-securechip/src/atecc.rs b/src/rust/bitbox-securechip/src/atecc.rs
index e2dc022..fa6d9e3 100644
--- a/src/rust/bitbox-securechip/src/atecc.rs
+++ b/src/rust/bitbox-securechip/src/atecc.rs
@@ -79,8 +79,8 @@ pub fn stretch_password(
}
}
-pub fn kdf(msg: &[u8; 32]) -> Result<Zeroizing<Vec<u8>>, Error> {
- let mut result = Zeroizing::new(vec![0u8; 32]);
+pub fn kdf(msg: &[u8; 32]) -> Result<Box<Zeroizing<[u8; 32]>>, Error> {
+ let mut result = Box::new(Zeroizing::new([0u8; 32]));
let status =
unsafe { bitbox_securechip_sys::atecc_kdf(msg.as_ptr(), msg.len(), result.as_mut_ptr()) };
if status == 0 {
diff --git a/src/rust/bitbox-securechip/src/optiga.rs b/src/rust/bitbox-securechip/src/optiga.rs
index 7acf575..5f3d567 100644
--- a/src/rust/bitbox-securechip/src/optiga.rs
+++ b/src/rust/bitbox-securechip/src/optiga.rs
@@ -88,8 +88,8 @@ pub fn stretch_password(
}
}
-pub fn kdf(msg: &[u8; 32]) -> Result<Zeroizing<Vec<u8>>, Error> {
- let mut result = Zeroizing::new(vec![0u8; 32]);
+pub fn kdf(msg: &[u8; 32]) -> Result<Box<Zeroizing<[u8; 32]>>, Error> {
+ let mut result = Box::new(Zeroizing::new([0u8; 32]));
let status = unsafe {
bitbox_securechip_sys::optiga_kdf_external(msg.as_ptr(), msg.len(), result.as_mut_ptr())
};
diff --git a/src/rust/bitbox02/src/hal/securechip.rs b/src/rust/bitbox02/src/hal/securechip.rs
index b2a7ece..ddb4595 100644
--- a/src/rust/bitbox02/src/hal/securechip.rs
+++ b/src/rust/bitbox02/src/hal/securechip.rs
@@ -105,7 +105,7 @@ impl SecureChip for BitBox02SecureChip {
.map_err(to_hal_error)
}
- fn kdf(&mut self, msg: &[u8; 32]) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
+ fn kdf(&mut self, msg: &[u8; 32]) -> Result<Box<zeroize::Zeroizing<[u8; 32]>>, Error> {
crate::securechip::kdf(msg).map_err(to_hal_error)
}
diff --git a/src/rust/bitbox02/src/securechip/imp.rs b/src/rust/bitbox02/src/securechip/imp.rs
index f1aba91..24fbc39 100644
--- a/src/rust/bitbox02/src/securechip/imp.rs
+++ b/src/rust/bitbox02/src/securechip/imp.rs
@@ -68,7 +68,7 @@ pub fn stretch_password(
/// Perform the secure chip KDF with the message in `msg` and return the zeroizing 32-byte
/// result.
-pub fn kdf(msg: &[u8; 32]) -> Result<Zeroizing<Vec<u8>>, Error> {
+pub fn kdf(msg: &[u8; 32]) -> Result<Box<Zeroizing<[u8; 32]>>, Error> {
match backend() {
Backend::Atecc => atecc::kdf(msg),
Backend::Optiga => optiga::kdf(msg),
diff --git a/src/rust/bitbox02/src/securechip/imp_fake.rs b/src/rust/bitbox02/src/securechip/imp_fake.rs
index 24962e9..d1a1c45 100644
--- a/src/rust/bitbox02/src/securechip/imp_fake.rs
+++ b/src/rust/bitbox02/src/securechip/imp_fake.rs
@@ -65,8 +65,8 @@ pub fn stretch_password(
/// Perform the secure chip KDF with the message in `msg` and return the zeroizing 32-byte
/// result.
-pub fn kdf(msg: &[u8; 32]) -> Result<Zeroizing<Vec<u8>>, Error> {
- Ok(Zeroizing::new(hmac_sha256(&KDF_KEY, msg).to_vec()))
+pub fn kdf(msg: &[u8; 32]) -> Result<Box<Zeroizing<[u8; 32]>>, Error> {
+ Ok(Box::new(Zeroizing::new(hmac_sha256(&KDF_KEY, msg))))
}
#[cfg(feature = "app-u2f")]
diff --git a/src/rust/bitbox03/src/securechip.rs b/src/rust/bitbox03/src/securechip.rs
index 22a79f5..a96cd50 100644
--- a/src/rust/bitbox03/src/securechip.rs
+++ b/src/rust/bitbox03/src/securechip.rs
@@ -29,7 +29,8 @@ impl hal::securechip::SecureChip for BitBox03SecureChip {
fn kdf(
&mut self,
_msg: &[u8; 32],
- ) -> Result<zeroize::Zeroizing<alloc::vec::Vec<u8>>, bitbox_hal::securechip::Error> {
+ ) -> Result<alloc::boxed::Box<zeroize::Zeroizing<[u8; 32]>>, bitbox_hal::securechip::Error>
+ {
todo!()
}
Why this scored 18/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.