bitbox02/da14531: port functions to Rust
What changed, and why it matters
This commit rewrites low-level Bluetooth setup commands for the BitBox02 hardware wallet from C bindings into safer Rust code. It is a routine refactoring/porting change with no obvious security bug, though it removes an old C-string conversion and adds a length check for the product string.
No immediate action required. As a defensive reviewer, recommend a follow-up audit of `bitbox-framed-serial-link::protocol_format` to confirm it correctly handles the maximum payload length and does not introduce framing errors or panics, and verify that downstream C firmware parsing the control commands accepts the new framing and null-byte behavior for device names.
Security signals we found
Removal of unsafe FFI calls to BLE control functions
Addition of an explicit length assertion on the product string (CTRL_PAYLOAD_MAX_LEN)
Change in set_name input handling: C-string conversion removed, embedded null bytes now allowed in the payload
New dependency introduced: bitbox-framed-serial-link
Evidence from the diff
The patch ports da14531_set_product, da14531_set_name, and da14531_power_down from unsafe FFI calls in bitbox02_sys to a Rust implementation that builds control payloads and frames them via bitbox-framed-serial-link::protocol_format. Notable changes: set_product now asserts the byte length is < 64; set_name no longer requires null termination and no longer rejects embedded null bytes; power_down sends a 2-byte payload instead of calling C. The commit adds a new workspace dependency and updates two simulator Cargo.lock files.
Changed components
src/rust/bitbox02/src/da14531.rssrc/rust/bitbox02/Cargo.tomltest/simulator-graphical-bb03/Cargo.locktest/simulator-graphical/Cargo.lockInspect captured patch +34 / −11
diff --git a/src/rust/bitbox02/Cargo.toml b/src/rust/bitbox02/Cargo.toml
index f73ec49..2a4a541 100644
--- a/src/rust/bitbox02/Cargo.toml
+++ b/src/rust/bitbox02/Cargo.toml
@@ -13,6 +13,7 @@ bitbox02-sys = {path="../bitbox02-sys"}
bitbox02-noise = { path = "../bitbox02-noise" }
bitbox-hal = { path = "../bitbox-hal" }
bitbox-bytequeue = { path = "../bitbox-bytequeue" }
+bitbox-framed-serial-link = { path = "../bitbox-framed-serial-link" }
util = {path = "../util"}
zeroize = { workspace = true }
bip39 = { workspace = true }
diff --git a/src/rust/bitbox02/src/da14531.rs b/src/rust/bitbox02/src/da14531.rs
index 5de5f30..cd93368 100644
--- a/src/rust/bitbox02/src/da14531.rs
+++ b/src/rust/bitbox02/src/da14531.rs
@@ -1,28 +1,48 @@
// SPDX-License-Identifier: Apache-2.0
+use alloc::vec;
+use alloc::vec::Vec;
use bitbox_bytequeue::ByteQueue;
+use bitbox_framed_serial_link::{ProtocolPacketType, protocol_format};
+
+const CTRL_CMD_DEVICE_NAME: u8 = 1;
+const CTRL_CMD_PRODUCT_STRING: u8 = 7;
+const CTRL_CMD_BLE_POWER_DOWN: u8 = 12;
+const CTRL_PAYLOAD_MAX_LEN: usize = 64;
+
+fn enqueue_ctrl_data(payload: &[u8], queue: &mut ByteQueue) {
+ let mut frame = vec![0u8; 12 + payload.len() * 2];
+ let frame_len = protocol_format(&mut frame, ProtocolPacketType::CtrlData, payload);
+ for &byte in &frame[..frame_len] {
+ queue.put(byte);
+ }
+}
/// Set the product string of the BLE chip. The product string must be smaller than 64 bytes.
pub fn set_product(product: &str, queue: &mut ByteQueue) {
let product = product.as_bytes();
- unsafe {
- bitbox02_sys::da14531_set_product(
- product.as_ptr(),
- product.len() as u16,
- queue as *mut _ as *mut _,
- )
- }
+ assert!(
+ product.len() < CTRL_PAYLOAD_MAX_LEN,
+ "product string too large"
+ );
+
+ let mut payload = Vec::with_capacity(1 + product.len());
+ payload.push(CTRL_CMD_PRODUCT_STRING);
+ payload.extend_from_slice(product);
+ enqueue_ctrl_data(&payload, queue);
}
-/// Set the device name of the BLE chip. The name must contain no null bytes.
+/// Set the device name of the BLE chip.
pub fn set_name(name: &str, queue: &mut ByteQueue) {
- let c_name = util::strings::str_to_cstr_vec(name).unwrap();
- unsafe { bitbox02_sys::da14531_set_name(c_name.as_ptr(), queue as *mut _ as *mut _) }
+ let mut payload = Vec::with_capacity(1 + name.len());
+ payload.push(CTRL_CMD_DEVICE_NAME);
+ payload.extend_from_slice(name.as_bytes());
+ enqueue_ctrl_data(&payload, queue);
}
/// Power down the BLE chip.
pub fn power_down(queue: &mut ByteQueue) {
- unsafe { bitbox02_sys::da14531_power_down(queue as *mut _ as *mut _) }
+ enqueue_ctrl_data(&[CTRL_CMD_BLE_POWER_DOWN, 0], queue);
}
#[cfg(test)]
diff --git a/test/simulator-graphical-bb03/Cargo.lock b/test/simulator-graphical-bb03/Cargo.lock
index fe709d8..043d249 100644
--- a/test/simulator-graphical-bb03/Cargo.lock
+++ b/test/simulator-graphical-bb03/Cargo.lock
@@ -370,6 +370,7 @@ version = "0.1.0"
dependencies = [
"bip39",
"bitbox-bytequeue",
+ "bitbox-framed-serial-link",
"bitbox-hal",
"bitbox02-noise",
"bitbox02-sys",
diff --git a/test/simulator-graphical/Cargo.lock b/test/simulator-graphical/Cargo.lock
index 5fdf6f9..88a59e5 100644
--- a/test/simulator-graphical/Cargo.lock
+++ b/test/simulator-graphical/Cargo.lock
@@ -332,6 +332,7 @@ version = "0.1.0"
dependencies = [
"bip39",
"bitbox-bytequeue",
+ "bitbox-framed-serial-link",
"bitbox-hal",
"bitbox02-noise",
"bitbox02-sys",
Why this scored 17/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.