What changed, and why it matters
This is a small internal code cleanup in the BitBox02 firmware's Rust code. It changes a helper function so it borrows only the memory/storage part of the hardware abstraction layer, rather than the whole hardware abstraction layer. The commit message says this is preparation for a future change where the user interface part of the hardware layer will be used at the same time during wallet unlocking. There is no direct security fix here and no vulnerability is described.
No security action required. Treat as normal code maintenance. Review the follow-up unlock_bip39() work if evaluating future security posture, since this change appears to enable parallel UI access.
Security signals we found
Refactor narrows mutable borrow scope in Rust code
Commit message references parallel use during unlock_bip39(), suggesting future concurrency work
No change to cryptographic algorithms, salt handling, or secret material
Evidence from the diff
The patch refactors salt::hash_data to accept &mut impl Memory instead of &mut impl Hal. Callers in keystore.rs and firmware_c_api.rs are updated to pass hal.memory(). The function only needs the memory interface to call get_salt_root(), so this narrows the required borrow. The commit explicitly states the reason is to avoid holding a mutable borrow on Hal Ui (part of Hal) so it can be used in parallel during unlock_bip39(). This is a borrow-checker/architectural change, not a cryptographic or security-bug fix.
Changed components
src/rust/bitbox02-rust/src/salt.rssrc/rust/bitbox02-rust/src/keystore.rssrc/rust/bitbox02-rust-c/src/firmware_c_api.rsInspect captured patch +20 / −14
diff --git a/src/rust/bitbox02-rust-c/src/firmware_c_api.rs b/src/rust/bitbox02-rust-c/src/firmware_c_api.rs
index a9fb4c2..37eec41 100644
--- a/src/rust/bitbox02-rust-c/src/firmware_c_api.rs
+++ b/src/rust/bitbox02-rust-c/src/firmware_c_api.rs
@@ -3,6 +3,8 @@
use core::ffi::c_char;
use util::bytes::{Bytes, BytesMut};
+use bitbox_hal::Hal;
+
#[cfg(not(any(feature = "c-unit-testing", feature = "simulator-graphical")))]
#[unsafe(no_mangle)]
pub extern "C" fn rust_main_loop() -> ! {
@@ -22,7 +24,8 @@ pub unsafe extern "C" fn rust_salt_hash_data(
Ok(purpose) => purpose,
Err(()) => return false,
};
- match bitbox02_rust::salt::hash_data(&mut crate::HalImpl::new(), data.as_ref(), purpose_str) {
+ let mut hal = crate::HalImpl::new();
+ match bitbox02_rust::salt::hash_data(hal.memory(), data.as_ref(), purpose_str) {
Ok(hash) => {
hash_out.as_mut()[..32].copy_from_slice(&hash);
true
diff --git a/src/rust/bitbox02-rust/src/keystore.rs b/src/rust/bitbox02-rust/src/keystore.rs
index 41bc069..403c546 100644
--- a/src/rust/bitbox02-rust/src/keystore.rs
+++ b/src/rust/bitbox02-rust/src/keystore.rs
@@ -179,8 +179,8 @@ fn verify_seed(
}
fn hash_seed(hal: &mut impl crate::hal::Hal, seed: &[u8]) -> Result<[u8; 32], Error> {
- let salted_key =
- crate::salt::hash_data(hal, &[], "keystore_retain_seed_hash").map_err(|_| Error::Salt)?;
+ let salted_key = crate::salt::hash_data(hal.memory(), &[], "keystore_retain_seed_hash")
+ .map_err(|_| Error::Salt)?;
let mut engine = HmacEngine::<sha256::Hash>::new(salted_key.as_slice());
engine.input(seed);
@@ -472,9 +472,12 @@ pub fn create_and_store_seed(
}
// Mix in entropy derived from the user password.
- let password_salted_hashed =
- crate::salt::hash_data(hal, password.as_bytes(), "keystore_seed_generation")
- .map_err(|_| Error::Salt)?;
+ let password_salted_hashed = crate::salt::hash_data(
+ hal.memory(),
+ password.as_bytes(),
+ "keystore_seed_generation",
+ )
+ .map_err(|_| Error::Salt)?;
for (i, &hash_byte) in password_salted_hashed.iter().take(seed_len).enumerate() {
seed[i] ^= hash_byte;
@@ -609,13 +612,13 @@ pub fn stretch_retained_seed_encryption_key(
purpose_in: &str,
purpose_out: &str,
) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
- let salted_in =
- crate::salt::hash_data(hal, encryption_key, purpose_in).map_err(|_| Error::Salt)?;
+ let salted_in = crate::salt::hash_data(hal.memory(), encryption_key, purpose_in)
+ .map_err(|_| Error::Salt)?;
let kdf = hal.securechip().kdf(salted_in.as_slice())?;
- let salted_out =
- crate::salt::hash_data(hal, encryption_key, purpose_out).map_err(|_| Error::Salt)?;
+ let salted_out = crate::salt::hash_data(hal.memory(), encryption_key, purpose_out)
+ .map_err(|_| Error::Salt)?;
let mut engine = HmacEngine::<sha256::Hash>::new(salted_out.as_slice());
engine.input(kdf.as_slice());
diff --git a/src/rust/bitbox02-rust/src/salt.rs b/src/rust/bitbox02-rust/src/salt.rs
index 1442637..fac8904 100644
--- a/src/rust/bitbox02-rust/src/salt.rs
+++ b/src/rust/bitbox02-rust/src/salt.rs
@@ -12,11 +12,11 @@ use zeroize::Zeroizing;
///
/// Returns `Err(())` if the salt root cannot be retrieved from persistent storage.
pub fn hash_data(
- hal: &mut impl crate::hal::Hal,
+ memory: &mut impl Memory,
data: &[u8],
purpose: &str,
) -> Result<Zeroizing<Vec<u8>>, ()> {
- let salt_root = hal.memory().get_salt_root()?;
+ let salt_root = memory.get_salt_root()?;
let mut hasher = sha2::Sha256::new();
hasher.update(salt_root.as_slice());
@@ -46,7 +46,7 @@ mod tests {
let data = hex!("001122334455667788");
let expected = hex!("62db8dcd47ddf8e81809c377ed96643855d3052bb73237100ca81f0f5a7611e6");
- let hash = hash_data(&mut mock_hal, &data, "test purpose").unwrap();
+ let hash = hash_data(&mut mock_hal.memory, &data, "test purpose").unwrap();
assert_eq!(hash.as_slice(), &expected);
}
@@ -58,7 +58,7 @@ mod tests {
let expected = hex!("2dbb05dd73d94edba6946611aaca367f76c809e96f20499ad674e596050f9833");
- let hash = hash_data(&mut mock_hal, &[], "").unwrap();
+ let hash = hash_data(&mut mock_hal.memory, &[], "").unwrap();
assert_eq!(hash.as_slice(), &expected);
}
}
Why this scored 12/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.