hal: put noise key functions into Memory HAL
What changed, and why it matters
This commit is a routine code reorganization. It moves functions that manage Noise protocol cryptographic keys (used for secure communication between the BitBox device and apps) into a common hardware abstraction layer called the Memory HAL. The actual behavior of these functions is not changed; they are simply routed through a new trait interface so the same code can be reused across different BitBox hardware models and test environments. There is no indication this fixes a security bug.
No security action required. Treat as normal maintainability refactoring. Reviewers may optionally verify that the new HAL implementations preserve the prior zeroization behavior and that the `todo!()` stubs in bitbox03 are completed before any production bitbox03 firmware is built.
Security signals we found
Refactoring only: no change to cryptographic logic or trust boundaries
Noise static private key handling moved behind HAL trait; return type remains `zeroize::Zeroizing<[u8; 32]>`
Test fake uses deterministic, non-random private key generation (intentional for simulator/tests)
New `todo!()` stubs in bitbox03 memory HAL indicate incomplete future hardware support, not a vulnerability
Evidence from the diff
The change refactors Noise key storage/retrieval APIs into the Memory HAL trait. bitbox02-rust/src/hww/noise.rs now calls hal.memory().get_noise_static_private_key(), check_noise_remote_static_pubkey(), and add_noise_remote_static_pubkey() instead of calling bitbox02::memory directly. Concrete implementations are added to bitbox02/src/hal/memory.rs (delegating to existing C-backed memory functions), bitbox-platform-host/src/memory.rs (a test fake with deterministic key generation and a bounded remote pubkey cache), and bitbox03/src/memory.rs (stubs marked todo!()). The fake implementation derives a static private key from a generation counter using a non-cryptographic deterministic formula, but this is only for unit/integration testing, not production firmware.
Changed components
src/rust/bitbox-hal/src/memory.rssrc/rust/bitbox-platform-host/src/memory.rssrc/rust/bitbox02-rust/src/hww/noise.rssrc/rust/bitbox02/src/hal/memory.rssrc/rust/bitbox03/src/memory.rsInspect captured patch +76 / −6
diff --git a/src/rust/bitbox-hal/src/memory.rs b/src/rust/bitbox-hal/src/memory.rs
index 8edbea8..e66ee6f 100644
--- a/src/rust/bitbox-hal/src/memory.rs
+++ b/src/rust/bitbox-hal/src/memory.rs
@@ -95,6 +95,9 @@ pub trait Memory {
password_stretch_algo: PasswordStretchAlgo,
) -> Result<(), ()>;
fn reset_hww(&mut self) -> Result<(), ()>;
+ fn get_noise_static_private_key(&mut self) -> Result<zeroize::Zeroizing<[u8; 32]>, ()>;
+ fn check_noise_remote_static_pubkey(&mut self, pubkey: &[u8; 32]) -> bool;
+ fn add_noise_remote_static_pubkey(&mut self, pubkey: &[u8; 32]) -> Result<(), ()>;
fn get_io_protection_key(&mut self, out: &mut [u8; 32]);
fn get_salt_root(&mut self) -> Result<zeroize::Zeroizing<Vec<u8>>, ()>;
fn get_attestation_pubkey_and_certificate(
diff --git a/src/rust/bitbox-platform-host/src/memory.rs b/src/rust/bitbox-platform-host/src/memory.rs
index c460726..7758772 100644
--- a/src/rust/bitbox-platform-host/src/memory.rs
+++ b/src/rust/bitbox-platform-host/src/memory.rs
@@ -21,6 +21,9 @@ pub struct FakeMemory {
mnemonic_passphrase_enabled: bool,
seed_birthdate: u32,
encrypted_seed_and_hmac: Option<(Vec<u8>, PasswordStretchAlgo)>,
+ noise_static_private_key_generation: u8,
+ noise_static_private_key: [u8; 32],
+ noise_remote_static_pubkeys: Vec<[u8; 32]>,
device_name: Option<String>,
salt_root: [u8; 32],
attestation_device_pubkey: Option<[u8; 64]>,
@@ -32,6 +35,15 @@ pub struct FakeMemory {
// Same as MEMORY_MULTISIG_NUM_ENTRIES in memory.h.
const MULTISIG_LIMIT: usize = 25;
+const NOISE_REMOTE_STATIC_PUBKEYS_LIMIT: usize = 5;
+
+fn make_noise_static_private_key(generation: u8) -> [u8; 32] {
+ let mut key = [generation.wrapping_add(1); 32];
+ key[0] &= 248;
+ key[31] &= 127;
+ key[31] |= 64;
+ key
+}
impl FakeMemory {
pub fn new() -> Self {
@@ -56,6 +68,9 @@ impl FakeMemory {
mnemonic_passphrase_enabled: false,
seed_birthdate: 0,
encrypted_seed_and_hmac: None,
+ noise_static_private_key_generation: 0,
+ noise_static_private_key: make_noise_static_private_key(0),
+ noise_remote_static_pubkeys: Vec::new(),
device_name: None,
salt_root: *b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
attestation_device_pubkey: None,
@@ -237,11 +252,37 @@ impl bitbox_hal::Memory for FakeMemory {
self.mnemonic_passphrase_enabled = false;
self.seed_birthdate = 0;
self.encrypted_seed_and_hmac = None;
+ self.noise_static_private_key_generation =
+ self.noise_static_private_key_generation.wrapping_add(1);
+ self.noise_static_private_key =
+ make_noise_static_private_key(self.noise_static_private_key_generation);
+ self.noise_remote_static_pubkeys = Vec::new();
self.device_name = None;
self.multisig_entries = Vec::new();
Ok(())
}
+ fn get_noise_static_private_key(&mut self) -> Result<zeroize::Zeroizing<[u8; 32]>, ()> {
+ Ok(zeroize::Zeroizing::new(self.noise_static_private_key))
+ }
+
+ fn check_noise_remote_static_pubkey(&mut self, pubkey: &[u8; 32]) -> bool {
+ self.noise_remote_static_pubkeys
+ .iter()
+ .any(|stored_pubkey| stored_pubkey == pubkey)
+ }
+
+ fn add_noise_remote_static_pubkey(&mut self, pubkey: &[u8; 32]) -> Result<(), ()> {
+ if self.check_noise_remote_static_pubkey(pubkey) {
+ return Ok(());
+ }
+ if self.noise_remote_static_pubkeys.len() == NOISE_REMOTE_STATIC_PUBKEYS_LIMIT {
+ self.noise_remote_static_pubkeys.remove(0);
+ }
+ self.noise_remote_static_pubkeys.push(*pubkey);
+ Ok(())
+ }
+
fn get_io_protection_key(&mut self, _out: &mut [u8; 32]) {
panic!("unused")
}
diff --git a/src/rust/bitbox02-rust/src/hww/noise.rs b/src/rust/bitbox02-rust/src/hww/noise.rs
index 8509155..e95f597 100644
--- a/src/rust/bitbox02-rust/src/hww/noise.rs
+++ b/src/rust/bitbox02-rust/src/hww/noise.rs
@@ -1,9 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
-use crate::hal::Ui;
+use crate::hal::{Memory, Ui};
use crate::workflow::pairing;
use alloc::vec::Vec;
-use bitbox02::memory;
use core::cell::RefCell;
const OP_I_CAN_HAS_HANDSHAEK: u8 = b'h';
@@ -70,7 +69,7 @@ pub(crate) async fn process(
.0
.borrow_mut()
.init(bitbox02_noise::Sensitive::from(
- memory::get_noise_static_private_key()?,
+ hal.memory().get_noise_static_private_key()?,
));
Ok(())
}
@@ -82,8 +81,9 @@ pub(crate) async fn process(
Ok(())
}
bitbox02_noise::HandshakeResult::Done => {
- let already_verified =
- memory::check_noise_remote_static_pubkey(&state.remote_static_pubkey()?);
+ let already_verified = hal
+ .memory()
+ .check_noise_remote_static_pubkey(&state.remote_static_pubkey()?);
// When communicating over BLE, we don't require noise pairing code
// confirmation, as BLE already requires pairing with a pairing code.
if crate::communication_mode::ble_enabled(hal) || already_verified {
@@ -109,7 +109,8 @@ pub(crate) async fn process(
// If this fails, we continue anyway, as the communication still works (just the
// pubkey is not stored and we need to perform the pairing verification again
// next time).
- memory::add_noise_remote_static_pubkey(&state.remote_static_pubkey()?)
+ hal.memory()
+ .add_noise_remote_static_pubkey(&state.remote_static_pubkey()?)
};
Ok(())
}
diff --git a/src/rust/bitbox02/src/hal/memory.rs b/src/rust/bitbox02/src/hal/memory.rs
index 18f260c..145aef6 100644
--- a/src/rust/bitbox02/src/hal/memory.rs
+++ b/src/rust/bitbox02/src/hal/memory.rs
@@ -215,6 +215,18 @@ impl Memory for BitBox02Memory {
crate::memory::reset_hww()
}
+ fn get_noise_static_private_key(&mut self) -> Result<zeroize::Zeroizing<[u8; 32]>, ()> {
+ crate::memory::get_noise_static_private_key()
+ }
+
+ fn check_noise_remote_static_pubkey(&mut self, pubkey: &[u8; 32]) -> bool {
+ crate::memory::check_noise_remote_static_pubkey(pubkey)
+ }
+
+ fn add_noise_remote_static_pubkey(&mut self, pubkey: &[u8; 32]) -> Result<(), ()> {
+ crate::memory::add_noise_remote_static_pubkey(pubkey)
+ }
+
fn get_io_protection_key(&mut self, out: &mut [u8; 32]) {
crate::memory::get_io_protection_key(out)
}
diff --git a/src/rust/bitbox03/src/memory.rs b/src/rust/bitbox03/src/memory.rs
index 3fe2db9..68f9ce3 100644
--- a/src/rust/bitbox03/src/memory.rs
+++ b/src/rust/bitbox03/src/memory.rs
@@ -100,6 +100,19 @@ impl hal::memory::Memory for BitBox03Memory {
fn reset_hww(&mut self) -> Result<(), ()> {
todo!()
}
+
+ fn get_noise_static_private_key(&mut self) -> Result<zeroize::Zeroizing<[u8; 32]>, ()> {
+ todo!()
+ }
+
+ fn check_noise_remote_static_pubkey(&mut self, _pubkey: &[u8; 32]) -> bool {
+ todo!()
+ }
+
+ fn add_noise_remote_static_pubkey(&mut self, _pubkey: &[u8; 32]) -> Result<(), ()> {
+ todo!()
+ }
+
fn get_salt_root(&mut self) -> Result<zeroize::Zeroizing<alloc::vec::Vec<u8>>, ()> {
todo!()
}
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.