hal: add ble_get_metadata and set_ble_metadata to Memory
What changed, and why it matters
This commit is a routine code reorganization. It moves the functions that read and write Bluetooth firmware metadata out of a low-level C-style module and into a Rust 'Memory' hardware-abstraction trait used by the rest of the firmware. The actual logic for reading, writing, and validating the metadata does not change. There is no indication this fixes a security bug; it appears to be part of making the code more testable and portable.
No security action required. Treat as normal refactoring. If reviewing a larger series, confirm that any later commits that change the metadata semantics (e.g., allowed firmware hash checks, rollback counters, or write authorization) are reviewed separately.
Security signals we found
No change to security logic: same metadata fields, same sizes, same callers.
Refactoring only: moves Bluetooth metadata accessors behind a trait boundary.
Adds unit-test coverage for conversion helpers, which is a defensive-quality signal.
No new input parsing, no new privileged operations, no new external interfaces.
Evidence from the diff
The patch adds ble_get_metadata and set_ble_metadata to the bitbox_hal::Memory trait and implements them for both the real BitBox02Memory HAL and the TestingMemory mock. It then updates bluetooth.rs and device_info.rs to call these trait methods through the HAL instead of calling bitbox02::memory::{get,set}_ble_metadata directly. The data structure BleMetadata is duplicated at the HAL layer with conversion helpers. The change is structural: no validation rules, sizes, or access controls are modified.
Changed components
src/rust/bitbox-hal/src/memory.rssrc/rust/bitbox02-rust/src/hal/testing/memory.rssrc/rust/bitbox02-rust/src/hww/api/bluetooth.rssrc/rust/bitbox02-rust/src/hww/api/device_info.rssrc/rust/bitbox02/src/hal/memory.rsInspect captured patch +105 / −8
diff --git a/src/rust/bitbox-hal/src/memory.rs b/src/rust/bitbox-hal/src/memory.rs
index 7eefa2b..6269262 100644
--- a/src/rust/bitbox-hal/src/memory.rs
+++ b/src/rust/bitbox-hal/src/memory.rs
@@ -35,9 +35,19 @@ pub enum Error {
Unknown,
}
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct BleMetadata {
+ pub allowed_firmware_hash: [u8; 32],
+ pub active_index: u8,
+ pub firmware_sizes: [u16; 2],
+ pub firmware_checksums: [u8; 2],
+}
+
pub trait Memory {
fn ble_enabled(&mut self) -> bool;
fn ble_enable(&mut self, enable: bool) -> Result<(), ()>;
+ fn ble_get_metadata(&mut self) -> BleMetadata;
+ fn set_ble_metadata(&mut self, metadata: &BleMetadata) -> Result<(), Error>;
fn get_securechip_type(&mut self) -> Result<SecurechipType, ()>;
fn get_platform(&mut self) -> Result<Platform, ()>;
fn get_device_name(&mut self) -> String;
diff --git a/src/rust/bitbox02-rust/src/hal/testing/memory.rs b/src/rust/bitbox02-rust/src/hal/testing/memory.rs
index a315fd3..eda5083 100644
--- a/src/rust/bitbox02-rust/src/hal/testing/memory.rs
+++ b/src/rust/bitbox02-rust/src/hal/testing/memory.rs
@@ -3,10 +3,11 @@
use alloc::string::String;
use alloc::vec::Vec;
-use crate::hal::memory::{Error, PasswordStretchAlgo, Platform, SecurechipType};
+use crate::hal::memory::{BleMetadata, Error, PasswordStretchAlgo, Platform, SecurechipType};
pub struct TestingMemory {
ble_enabled: bool,
+ ble_metadata: BleMetadata,
securechip_type: SecurechipType,
platform: Platform,
initialized: bool,
@@ -31,6 +32,12 @@ impl TestingMemory {
pub fn new() -> Self {
Self {
ble_enabled: true,
+ ble_metadata: BleMetadata {
+ allowed_firmware_hash: [0; 32],
+ active_index: 0,
+ firmware_sizes: [0; 2],
+ firmware_checksums: [0; 2],
+ },
securechip_type: SecurechipType::Optiga,
platform: Platform::BitBox02,
initialized: false,
@@ -91,6 +98,15 @@ impl crate::hal::Memory for TestingMemory {
Ok(())
}
+ fn ble_get_metadata(&mut self) -> BleMetadata {
+ self.ble_metadata
+ }
+
+ fn set_ble_metadata(&mut self, metadata: &BleMetadata) -> Result<(), Error> {
+ self.ble_metadata = *metadata;
+ Ok(())
+ }
+
fn get_securechip_type(&mut self) -> Result<SecurechipType, ()> {
Ok(self.securechip_type)
}
diff --git a/src/rust/bitbox02-rust/src/hww/api/bluetooth.rs b/src/rust/bitbox02-rust/src/hww/api/bluetooth.rs
index 0721f0b..83287c4 100644
--- a/src/rust/bitbox02-rust/src/hww/api/bluetooth.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/bluetooth.rs
@@ -15,7 +15,7 @@ use crate::hal::{Memory, Ui, memory as hal_memory};
use alloc::vec::Vec;
-use bitbox02::{memory, spi_mem};
+use bitbox02::spi_mem;
// See also bitbox-da14531-firmware.bin.sha256.
const ALLOWED_HASH: [u8; 32] =
@@ -57,6 +57,7 @@ trait Funcs {
}
async fn _process_upgrade(
+ memory: &mut impl Memory,
funcs: &mut impl Funcs,
progress: &mut impl Progress,
request: &pb::BluetoothUpgradeInitRequest,
@@ -66,7 +67,7 @@ async fn _process_upgrade(
return Err(Error::InvalidInput);
}
- let mut ble_metadata = memory::get_ble_metadata();
+ let mut ble_metadata = memory.ble_get_metadata();
// We work on the inactive firmware memory area.
let inactive_index: u8 = if ble_metadata.active_index == 0 { 1 } else { 0 };
@@ -116,7 +117,7 @@ async fn _process_upgrade(
ble_metadata.firmware_sizes[inactive_index as usize] = request.firmware_length as u16;
ble_metadata.firmware_checksums[inactive_index as usize] = firmware_checksum;
- memory::set_ble_metadata(&ble_metadata).map_err(|_| Error::Memory)?;
+ memory.set_ble_metadata(&ble_metadata)?;
Ok(pb::bluetooth_response::Response::Success(
pb::BluetoothSuccess {},
@@ -144,7 +145,14 @@ async fn process_upgrade(
.await?;
let mut progress = hal.ui().progress_create("Upgrading...");
- let response = _process_upgrade(&mut RealFuncs, &mut progress, request, &ALLOWED_HASH).await;
+ let response = _process_upgrade(
+ hal.memory(),
+ &mut RealFuncs,
+ &mut progress,
+ request,
+ &ALLOWED_HASH,
+ )
+ .await;
drop(progress);
if response.is_ok() {
@@ -282,6 +290,7 @@ mod tests {
];
for test in test_cases {
+ let mut memory = crate::hal::testing::TestingMemory::new();
let mut mock_funcs = MockFuncs {
chunk_requests: vec![],
};
@@ -289,6 +298,7 @@ mod tests {
Sha256::digest(vec![0; test.firmware_length as usize]).into();
assert!(
block_on(_process_upgrade(
+ &mut memory,
&mut mock_funcs,
&mut crate::hal::testing::ui::NoopProgress,
&pb::BluetoothUpgradeInitRequest {
diff --git a/src/rust/bitbox02-rust/src/hww/api/device_info.rs b/src/rust/bitbox02-rust/src/hww/api/device_info.rs
index f6b7b5d..99d8e62 100644
--- a/src/rust/bitbox02-rust/src/hww/api/device_info.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/device_info.rs
@@ -4,13 +4,13 @@ use super::Error;
use crate::hal::{Memory, SecureChip, memory as hal_memory, securechip};
use crate::pb;
-use bitbox02::{memory, spi_mem};
+use bitbox02::spi_mem;
use pb::response::Response;
pub fn process(hal: &mut impl crate::hal::Hal) -> Result<Response, Error> {
let bluetooth = match hal.memory().get_platform().map_err(|_| Error::Memory)? {
hal_memory::Platform::BitBox02Plus => {
- let ble_metadata = memory::get_ble_metadata();
+ let ble_metadata = hal.memory().ble_get_metadata();
Some(pb::device_info_response::Bluetooth {
firmware_hash: ble_metadata.allowed_firmware_hash.to_vec(),
firmware_version: spi_mem::get_active_ble_firmware_version()
diff --git a/src/rust/bitbox02/src/hal/memory.rs b/src/rust/bitbox02/src/hal/memory.rs
index c08b0b4..b60b19b 100644
--- a/src/rust/bitbox02/src/hal/memory.rs
+++ b/src/rust/bitbox02/src/hal/memory.rs
@@ -4,7 +4,7 @@ use alloc::string::String;
use alloc::vec::Vec;
use bitbox_hal::Memory;
-use bitbox_hal::memory::{Error, PasswordStretchAlgo, Platform, SecurechipType};
+use bitbox_hal::memory::{BleMetadata, Error, PasswordStretchAlgo, Platform, SecurechipType};
pub struct BitBox02Memory;
@@ -45,6 +45,15 @@ fn to_hal_error(error: crate::memory::MemoryError) -> Error {
}
}
+fn to_hal_ble_metadata(metadata: crate::memory::BleMetadata) -> BleMetadata {
+ BleMetadata {
+ allowed_firmware_hash: metadata.allowed_firmware_hash,
+ active_index: metadata.active_index,
+ firmware_sizes: metadata.firmware_sizes,
+ firmware_checksums: metadata.firmware_checksums,
+ }
+}
+
pub(super) fn to_bitbox02_password_stretch_algo(
algo: PasswordStretchAlgo,
) -> crate::memory::PasswordStretchAlgo {
@@ -58,6 +67,15 @@ pub(super) fn to_bitbox02_password_stretch_algo(
}
}
+fn to_bitbox02_ble_metadata(metadata: &BleMetadata) -> crate::memory::BleMetadata {
+ crate::memory::BleMetadata {
+ allowed_firmware_hash: metadata.allowed_firmware_hash,
+ active_index: metadata.active_index,
+ firmware_sizes: metadata.firmware_sizes,
+ firmware_checksums: metadata.firmware_checksums,
+ }
+}
+
impl Memory for BitBox02Memory {
fn ble_enabled(&mut self) -> bool {
crate::memory::ble_enabled()
@@ -67,6 +85,15 @@ impl Memory for BitBox02Memory {
crate::memory::ble_enable(enable)
}
+ fn ble_get_metadata(&mut self) -> BleMetadata {
+ to_hal_ble_metadata(crate::memory::get_ble_metadata())
+ }
+
+ fn set_ble_metadata(&mut self, metadata: &BleMetadata) -> Result<(), Error> {
+ let metadata = to_bitbox02_ble_metadata(metadata);
+ crate::memory::set_ble_metadata(&metadata).map_err(|_| Error::Unknown)
+ }
+
fn get_securechip_type(&mut self) -> Result<SecurechipType, ()> {
crate::memory::get_securechip_type().map(to_hal_securechip_type)
}
@@ -252,4 +279,38 @@ mod tests {
crate::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V1 as i32,
);
}
+
+ #[test]
+ fn test_to_hal_ble_metadata() {
+ let input = crate::memory::BleMetadata {
+ allowed_firmware_hash: [0x11; 32],
+ active_index: 1,
+ firmware_sizes: [1234, 5678],
+ firmware_checksums: [0xaa, 0xbb],
+ };
+
+ let output = to_hal_ble_metadata(input);
+
+ assert_eq!(output.allowed_firmware_hash, [0x11; 32]);
+ assert_eq!(output.active_index, 1);
+ assert_eq!(output.firmware_sizes, [1234, 5678]);
+ assert_eq!(output.firmware_checksums, [0xaa, 0xbb]);
+ }
+
+ #[test]
+ fn test_to_bitbox02_ble_metadata() {
+ let input = BleMetadata {
+ allowed_firmware_hash: [0x22; 32],
+ active_index: 0,
+ firmware_sizes: [100, 200],
+ firmware_checksums: [0xcc, 0xdd],
+ };
+
+ let output = to_bitbox02_ble_metadata(&input);
+
+ assert_eq!(output.allowed_firmware_hash, [0x22; 32]);
+ assert_eq!(output.active_index, 0);
+ assert_eq!(output.firmware_sizes, [100, 200]);
+ assert_eq!(output.firmware_checksums, [0xcc, 0xdd]);
+ }
}
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.