bitbox02: add some da14531 function wrappers
What changed, and why it matters
This commit adds new Rust wrappers that let the firmware talk to the Bluetooth Low Energy (BLE) chip in the BitBox02 hardware wallet. The wrappers simply forward commands to set the BLE product name, device name, and power-down state. There is no indication in the commit that this fixes a security bug or introduces a vulnerability; it appears to be routine feature plumbing for an upcoming Rust mainloop integration.
No security action required. Treat as normal code-review item; if reviewing further, verify that the C implementations of da14531_set_product, da14531_set_name, and da14531_power_down validate lengths and null-termination, and that the unwrap in set_name cannot be triggered by attacker-controlled input.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change adds three FFI bindings (da14531_set_product, da14531_set_name, da14531_power_down) in bitbox02-sys, exposes a new da14531 Rust module in bitbox02, and adds unit tests that verify the serialized control-packet output sent to a ring buffer. The set_product wrapper passes a byte slice and length directly to C; set_name converts a Rust &str to a C string via util::strings::str_to_cstr_vec and unwraps; power_down sends a power-down command. No unsafe memory handling beyond the required FFI boundary is visible, and the commit message describes the change as additive wrappers for future use.
Changed components
src/rust/bitbox02-sys/build.rssrc/rust/bitbox02-sys/wrapper.hsrc/rust/bitbox02/src/da14531.rssrc/rust/bitbox02/src/lib.rsInspect captured patch +107 / −0
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index 7f664e5..afdd864 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -121,6 +121,7 @@ version = "0.1.0"
dependencies = [
"bip39",
"bitbox-aes",
+ "bitbox-framed-serial-link",
"bitbox02-rust",
"bitbox02-sys",
"hex_lit",
diff --git a/src/rust/bitbox02-sys/build.rs b/src/rust/bitbox02-sys/build.rs
index 015d984..bb178c8 100644
--- a/src/rust/bitbox02-sys/build.rs
+++ b/src/rust/bitbox02-sys/build.rs
@@ -64,6 +64,9 @@ const ALLOWLIST_FNS: &[&str] = &[
"delay_is_elapsed",
"delay_ms",
"delay_us",
+ "da14531_set_product",
+ "da14531_set_name",
+ "da14531_power_down",
"emit_event",
"empty_create",
"fake_memory_factoryreset",
diff --git a/src/rust/bitbox02-sys/wrapper.h b/src/rust/bitbox02-sys/wrapper.h
index 730f2f4..a4dae44 100644
--- a/src/rust/bitbox02-sys/wrapper.h
+++ b/src/rust/bitbox02-sys/wrapper.h
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
#include <delay.h>
+#include <da14531/da14531.h>
#include <memory/bitbox02_smarteeprom.h>
#include <memory/memory.h>
#include <memory/memory_shared.h>
diff --git a/src/rust/bitbox02/Cargo.toml b/src/rust/bitbox02/Cargo.toml
index f8d903b..275da85 100644
--- a/src/rust/bitbox02/Cargo.toml
+++ b/src/rust/bitbox02/Cargo.toml
@@ -16,6 +16,7 @@ bip39 = { workspace = true }
[dev-dependencies]
bitbox-aes = { path = "../bitbox-aes" }
+bitbox-framed-serial-link = { path = "../bitbox-framed-serial-link" }
bitbox02-rust = { path = "../bitbox02-rust" }
hex_lit = { workspace = true }
diff --git a/src/rust/bitbox02/src/da14531.rs b/src/rust/bitbox02/src/da14531.rs
new file mode 100644
index 0000000..1f17c3c
--- /dev/null
+++ b/src/rust/bitbox02/src/da14531.rs
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::ringbuffer::RingBuffer;
+
+/// 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 RingBuffer) {
+ let product = product.as_bytes();
+ unsafe {
+ bitbox02_sys::da14531_set_product(product.as_ptr(), product.len() as u16, &mut queue.inner)
+ }
+}
+
+/// Set the device name of the BLE chip. The name must contain no null bytes.
+pub fn set_name(name: &str, queue: &mut RingBuffer) {
+ let c_name = util::strings::str_to_cstr_vec(name).unwrap();
+ unsafe { bitbox02_sys::da14531_set_name(c_name.as_ptr(), &mut queue.inner) }
+}
+
+/// Power down the BLE chip.
+pub fn power_down(queue: &mut RingBuffer) {
+ unsafe { bitbox02_sys::da14531_power_down(&mut queue.inner) }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ extern crate alloc;
+ use alloc::vec;
+ use alloc::vec::Vec;
+ use bitbox_framed_serial_link::{ProtocolPacketType, protocol_format};
+ use hex_lit::hex;
+
+ const CTRL_CMD_DEVICE_NAME: u8 = 1;
+ const CTRL_CMD_PRODUCT_STRING: u8 = 7;
+ const CTRL_CMD_BLE_POWER_DOWN: u8 = 12;
+
+ fn drain(queue: &mut RingBuffer) -> Vec<u8> {
+ let mut out = Vec::new();
+ while queue.len() > 0 {
+ out.push(queue.get().unwrap());
+ }
+ out
+ }
+
+ #[test]
+ fn test_set_product() {
+ let product = "foo bar";
+ let mut buf = [0u8; 256];
+ let mut queue = RingBuffer::new(&mut buf);
+
+ set_product(product, &mut queue);
+
+ let actual = drain(&mut queue);
+
+ let mut payload = vec![CTRL_CMD_PRODUCT_STRING];
+ payload.extend_from_slice(product.as_bytes());
+ let mut expected = vec![0u8; 140];
+ let expected_len = protocol_format(&mut expected, ProtocolPacketType::CtrlData, &payload);
+ expected.truncate(expected_len);
+ assert_eq!(actual, expected);
+ assert_eq!(actual, hex!("7eb4080007666f6f206261725b057e").to_vec());
+ }
+
+ #[test]
+ fn test_power_down() {
+ let mut buf = [0u8; 256];
+ let mut queue = RingBuffer::new(&mut buf);
+
+ power_down(&mut queue);
+
+ let actual = drain(&mut queue);
+
+ let payload = vec![CTRL_CMD_BLE_POWER_DOWN, 0];
+ let mut expected = vec![0u8; 140];
+ let expected_len = protocol_format(&mut expected, ProtocolPacketType::CtrlData, &payload);
+ expected.truncate(expected_len);
+ assert_eq!(actual, expected);
+ assert_eq!(actual, hex!("7eb402000c00b4a27e").to_vec());
+ }
+
+ #[test]
+ fn test_set_name() {
+ let name = "foo bar";
+ let mut buf = [0u8; 256];
+ let mut queue = RingBuffer::new(&mut buf);
+
+ set_name(name, &mut queue);
+
+ let actual = drain(&mut queue);
+
+ let mut payload = vec![CTRL_CMD_DEVICE_NAME];
+ payload.extend_from_slice(name.as_bytes());
+ let mut expected = vec![0u8; 140];
+ let expected_len = protocol_format(&mut expected, ProtocolPacketType::CtrlData, &payload);
+ expected.truncate(expected_len);
+ assert_eq!(actual, expected);
+ assert_eq!(actual, hex!("7eb4080001666f6f20626172db2f7e").to_vec());
+ }
+}
diff --git a/src/rust/bitbox02/src/lib.rs b/src/rust/bitbox02/src/lib.rs
index 0da3f85..fb3a2ec 100644
--- a/src/rust/bitbox02/src/lib.rs
+++ b/src/rust/bitbox02/src/lib.rs
@@ -22,6 +22,7 @@ pub mod testing;
#[cfg(test)]
extern crate bitbox02_rust;
+pub mod da14531;
pub mod delay;
#[cfg(feature = "simulator-graphical")]
pub mod event;
Why this scored 15/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.