da14531: move Rust logic to dedicated crate
What changed, and why it matters
This commit is a routine code reorganization: it moves the Rust code that controls the Bluetooth Low Energy (DA14531) chip from one internal module into its own separate crate. The actual logic, commands, and behavior are copied almost unchanged; only the file paths and import names change. There is no indication this fixes or introduces a security problem.
No security action required. Treat as normal refactoring/dependency cleanup.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors the da14531 module out of bitbox02 into a new workspace crate bitbox-da14531. The new crate exposes the same functions (set_product, set_name, power_down, reset, get_connection_state) and the same framing logic using bitbox_bytequeue and bitbox_framed_serial_link. Callers in bitbox02-rust/src/main_loop.rs are updated from bitbox02::da14531::... to bitbox_da14531::.... Cargo manifests and lockfiles are updated accordingly. No functional changes are visible in the diff.
Changed components
src/rust/bitbox-da14531/src/lib.rssrc/rust/bitbox02/src/da14531.rssrc/rust/bitbox02-rust/src/main_loop.rssrc/rust/Cargo.tomlsrc/rust/bitbox02-rust/Cargo.tomlsrc/rust/bitbox-da14531/Cargo.tomlInspect captured patch +218 / −171
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index af2fbe5..6d88b61 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -107,6 +107,15 @@ dependencies = [
name = "bitbox-bytequeue"
version = "0.1.0"
+[[package]]
+name = "bitbox-da14531"
+version = "0.1.0"
+dependencies = [
+ "bitbox-bytequeue",
+ "bitbox-framed-serial-link",
+ "hex_lit",
+]
+
[[package]]
name = "bitbox-executor"
version = "0.1.0"
@@ -176,6 +185,7 @@ dependencies = [
"bip39",
"bitbox-aes",
"bitbox-bytequeue",
+ "bitbox-da14531",
"bitbox-executor",
"bitbox-hal",
"bitbox-secp256k1",
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index 43c51a3..d793ef9 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -6,6 +6,7 @@ members = [
"bitbox02-rust-c",
"bitbox02-rust",
"bitbox-bytequeue",
+ "bitbox-da14531",
"bitbox-hal",
"bitbox-framed-serial-link",
"util",
diff --git a/src/rust/bitbox-da14531/Cargo.toml b/src/rust/bitbox-da14531/Cargo.toml
new file mode 100644
index 0000000..3c909a0
--- /dev/null
+++ b/src/rust/bitbox-da14531/Cargo.toml
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: Apache-2.0
+
+[package]
+name = "bitbox-da14531"
+version = "0.1.0"
+authors = ["Shift Crypto AG <support@bitbox.swiss>"]
+edition = "2024"
+license = "Apache-2.0"
+
+[dependencies]
+bitbox-framed-serial-link = { path = "../bitbox-framed-serial-link" }
+bitbox-bytequeue = { path = "../bitbox-bytequeue" }
+
+[dev-dependencies]
+hex_lit = { workspace = true }
diff --git a/src/rust/bitbox-da14531/src/lib.rs b/src/rust/bitbox-da14531/src/lib.rs
new file mode 100644
index 0000000..5a266bf
--- /dev/null
+++ b/src/rust/bitbox-da14531/src/lib.rs
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#![no_std]
+
+extern crate alloc;
+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_BLE_STATUS: u8 = 5;
+const CTRL_CMD_PRODUCT_STRING: u8 = 7;
+const CTRL_CMD_BLE_CHIP_RESET: u8 = 8;
+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();
+ 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.
+pub fn set_name(name: &str, queue: &mut ByteQueue) {
+ 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) {
+ enqueue_ctrl_data(&[CTRL_CMD_BLE_POWER_DOWN, 0], queue);
+}
+
+/// Reset the BLE chip.
+pub fn reset(queue: &mut ByteQueue) {
+ enqueue_ctrl_data(&[CTRL_CMD_BLE_CHIP_RESET], queue);
+}
+
+/// Ask the BLE chip for its current connection state.
+pub fn get_connection_state(queue: &mut ByteQueue) {
+ enqueue_ctrl_data(&[CTRL_CMD_BLE_STATUS], queue);
+}
+
+#[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_BLE_STATUS: u8 = 5;
+ const CTRL_CMD_PRODUCT_STRING: u8 = 7;
+ const CTRL_CMD_BLE_CHIP_RESET: u8 = 8;
+ const CTRL_CMD_BLE_POWER_DOWN: u8 = 12;
+
+ fn drain(queue: &mut ByteQueue) -> Vec<u8> {
+ let mut out = Vec::new();
+ while queue.num() > 0 {
+ out.push(queue.get().unwrap());
+ }
+ out
+ }
+
+ #[test]
+ fn test_set_product() {
+ let product = "foo bar";
+ let mut queue = ByteQueue::with_capacity(64);
+
+ 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 queue = ByteQueue::with_capacity(64);
+
+ 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 queue = ByteQueue::with_capacity(64);
+
+ 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());
+ }
+
+ #[test]
+ fn test_reset() {
+ let mut queue = ByteQueue::with_capacity(64);
+
+ reset(&mut queue);
+
+ let actual = drain(&mut queue);
+
+ let payload = vec![CTRL_CMD_BLE_CHIP_RESET];
+ 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!("7eb401000877f67e").to_vec());
+ }
+
+ #[test]
+ fn test_get_connection_state() {
+ let mut queue = ByteQueue::with_capacity(64);
+
+ get_connection_state(&mut queue);
+
+ let actual = drain(&mut queue);
+
+ let payload = vec![CTRL_CMD_BLE_STATUS];
+ 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!("7eb4010005b6337e").to_vec());
+ }
+}
diff --git a/src/rust/bitbox02-rust/Cargo.toml b/src/rust/bitbox02-rust/Cargo.toml
index ed63157..c98c4ab 100644
--- a/src/rust/bitbox02-rust/Cargo.toml
+++ b/src/rust/bitbox02-rust/Cargo.toml
@@ -15,6 +15,7 @@ doctest = false
[dependencies]
bitbox-hal = { path = "../bitbox-hal" }
+bitbox-da14531 = { path = "../bitbox-da14531" }
bitbox02 = { path = "../bitbox02" }
bitbox-bytequeue = { path = "../bitbox-bytequeue" }
bitbox-secp256k1 = { path = "../bitbox-secp256k1" }
diff --git a/src/rust/bitbox02-rust/src/main_loop.rs b/src/rust/bitbox02-rust/src/main_loop.rs
index 165ca63..85c1331 100644
--- a/src/rust/bitbox02-rust/src/main_loop.rs
+++ b/src/rust/bitbox02-rust/src/main_loop.rs
@@ -31,7 +31,7 @@ pub fn main_loop<H: crate::hal::Hal>(hal: &mut H) -> ! {
// If the bootloader has booted the BLE chip, the BLE chip isn't aware of the name according to
// the fw. Send it over.
let device_name = hal.memory().get_device_name();
- bitbox02::da14531::set_name(&device_name, &mut uart_write_queue);
+ bitbox_da14531::set_name(&device_name, &mut uart_write_queue);
// This starts the async startup workflow, which is processed by the loop below.
spawn(Box::pin(async {
@@ -93,7 +93,7 @@ pub fn main_loop<H: crate::hal::Hal>(hal: &mut H) -> ! {
if bitbox02::usb_packet::process(&hww_frame) {
if crate::communication_mode::ble_enabled(hal) {
// Enqueue a power down command to the da14531
- bitbox02::da14531::power_down(&mut uart_write_queue);
+ bitbox_da14531::power_down(&mut uart_write_queue);
// Flush out the power down command. This will be the last UART communication
// we do.
while uart_write_queue.num() > 0 {
@@ -163,7 +163,7 @@ pub fn main_loop<H: crate::hal::Hal>(hal: &mut H) -> ! {
if let Ok(crate::hal::memory::Platform::BitBox02Plus) = hal.memory().get_platform() {
let product = bitbox02::platform::product();
bitbox02::da14531_handler::set_product(product);
- bitbox02::da14531::set_product(product, &mut uart_write_queue)
+ bitbox_da14531::set_product(product, &mut uart_write_queue)
}
bitbox02::usb::start();
}
diff --git a/src/rust/bitbox02/src/da14531.rs b/src/rust/bitbox02/src/da14531.rs
deleted file mode 100644
index 6cec8a5..0000000
--- a/src/rust/bitbox02/src/da14531.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-// 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_BLE_STATUS: u8 = 5;
-const CTRL_CMD_PRODUCT_STRING: u8 = 7;
-const CTRL_CMD_BLE_CHIP_RESET: u8 = 8;
-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();
- 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.
-pub fn set_name(name: &str, queue: &mut ByteQueue) {
- 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) {
- enqueue_ctrl_data(&[CTRL_CMD_BLE_POWER_DOWN, 0], queue);
-}
-
-/// Reset the BLE chip.
-pub fn reset(queue: &mut ByteQueue) {
- enqueue_ctrl_data(&[CTRL_CMD_BLE_CHIP_RESET], queue);
-}
-
-/// Ask the BLE chip for its current connection state.
-pub fn get_connection_state(queue: &mut ByteQueue) {
- enqueue_ctrl_data(&[CTRL_CMD_BLE_STATUS], queue);
-}
-
-#[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_BLE_STATUS: u8 = 5;
- const CTRL_CMD_PRODUCT_STRING: u8 = 7;
- const CTRL_CMD_BLE_CHIP_RESET: u8 = 8;
- const CTRL_CMD_BLE_POWER_DOWN: u8 = 12;
-
- fn drain(queue: &mut ByteQueue) -> Vec<u8> {
- let mut out = Vec::new();
- while queue.num() > 0 {
- out.push(queue.get().unwrap());
- }
- out
- }
-
- #[test]
- fn test_set_product() {
- let product = "foo bar";
- let mut queue = ByteQueue::with_capacity(64);
-
- 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 queue = ByteQueue::with_capacity(64);
-
- 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 queue = ByteQueue::with_capacity(64);
-
- 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());
- }
-
- #[test]
- fn test_reset() {
- let mut queue = ByteQueue::with_capacity(64);
-
- reset(&mut queue);
-
- let actual = drain(&mut queue);
-
- let payload = vec![CTRL_CMD_BLE_CHIP_RESET];
- 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!("7eb401000877f67e").to_vec());
- }
-
- #[test]
- fn test_get_connection_state() {
- let mut queue = ByteQueue::with_capacity(64);
-
- get_connection_state(&mut queue);
-
- let actual = drain(&mut queue);
-
- let payload = vec![CTRL_CMD_BLE_STATUS];
- 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!("7eb4010005b6337e").to_vec());
- }
-}
diff --git a/src/rust/bitbox02/src/lib.rs b/src/rust/bitbox02/src/lib.rs
index f371cd5..0422c57 100644
--- a/src/rust/bitbox02/src/lib.rs
+++ b/src/rust/bitbox02/src/lib.rs
@@ -19,7 +19,6 @@ extern crate alloc;
#[cfg(any(feature = "testing", feature = "simulator-graphical"))]
pub mod testing;
-pub mod da14531;
pub mod da14531_handler;
pub mod da14531_protocol;
pub mod delay;
diff --git a/test/simulator-graphical-bb03/Cargo.lock b/test/simulator-graphical-bb03/Cargo.lock
index 043d249..60fd422 100644
--- a/test/simulator-graphical-bb03/Cargo.lock
+++ b/test/simulator-graphical-bb03/Cargo.lock
@@ -333,6 +333,14 @@ dependencies = [
name = "bitbox-bytequeue"
version = "0.1.0"
+[[package]]
+name = "bitbox-da14531"
+version = "0.1.0"
+dependencies = [
+ "bitbox-bytequeue",
+ "bitbox-framed-serial-link",
+]
+
[[package]]
name = "bitbox-executor"
version = "0.1.0"
@@ -398,6 +406,7 @@ dependencies = [
"bip39",
"bitbox-aes",
"bitbox-bytequeue",
+ "bitbox-da14531",
"bitbox-executor",
"bitbox-hal",
"bitbox-secp256k1",
diff --git a/test/simulator-graphical/Cargo.lock b/test/simulator-graphical/Cargo.lock
index 88a59e5..47a2b15 100644
--- a/test/simulator-graphical/Cargo.lock
+++ b/test/simulator-graphical/Cargo.lock
@@ -295,6 +295,14 @@ dependencies = [
name = "bitbox-bytequeue"
version = "0.1.0"
+[[package]]
+name = "bitbox-da14531"
+version = "0.1.0"
+dependencies = [
+ "bitbox-bytequeue",
+ "bitbox-framed-serial-link",
+]
+
[[package]]
name = "bitbox-executor"
version = "0.1.0"
@@ -360,6 +368,7 @@ dependencies = [
"bip39",
"bitbox-aes",
"bitbox-bytequeue",
+ "bitbox-da14531",
"bitbox-executor",
"bitbox-hal",
"bitbox-secp256k1",
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.