What changed, and why it matters
This commit is a build-system and test-infrastructure fix. It changes Rust conditional-compilation flags so that unit tests can compile and run on a normal computer (host target) instead of only on the embedded device. It also limits the custom panic handler to bare-metal ARM builds and exposes some test-only helper functions under the test configuration. There is no change to runtime security behavior for end users.
No security action required. Treat as a normal build/test maintenance commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch adjusts cfg gates across the Rust workspace: #[cfg(feature = "testing")] is widened to #[cfg(any(test, feature = "testing"))] in several modules, the panic handler is now gated to target_arch = "arm" + target_os = "none", and test-only dev-dependencies are added to bitbox02-rust/Cargo.toml. A test that requires U2F is gated behind feature = "app-u2f". These are purely developer-facing changes to make cargo test work without enabling all firmware features.
Changed components
Rust workspace build configurationUnit-test infrastructureConditional compilation (`cfg`) attributesInspect captured patch +32 / −21
### src/rust/bitbox-securechip/src/optiga.rs
@@ -16,7 +16,7 @@ mod ops;
mod ops;
const OID_AES_SYMKEY: u16 = bitbox_securechip_sys::OID_AES_SYMKEY as u16;
-#[cfg(any(feature = "app-u2f", feature = "factory-setup"))]
+#[cfg(any(test, feature = "app-u2f", feature = "factory-setup"))]
const OID_ARBITRARY_DATA: u16 = bitbox_securechip_sys::OID_ARBITRARY_DATA as u16;
const OID_COUNTER: u16 = bitbox_securechip_sys::OID_COUNTER as u16;
const OID_COUNTER_HMAC_WRITEPROTECTED: u16 =
@@ -28,7 +28,7 @@ const OID_PASSWORD: u16 = bitbox_securechip_sys::OID_PASSWORD as u16;
const OID_PASSWORD_SECRET: u16 = bitbox_securechip_sys::OID_PASSWORD_SECRET as u16;
const MONOTONIC_COUNTER_MAX_USE: u32 = bitbox_securechip_sys::MONOTONIC_COUNTER_MAX_USE;
const SMALL_MONOTONIC_COUNTER_MAX_USE: u32 = bitbox_securechip_sys::SMALL_MONOTONIC_COUNTER_MAX_USE;
-#[cfg(any(feature = "app-u2f", feature = "factory-setup"))]
+#[cfg(any(test, feature = "app-u2f", feature = "factory-setup"))]
const ARBITRARY_DATA_LEN: usize =
bitbox_securechip_sys::ARBITRARY_DATA_OBJECT_TYPE_3_MAX_SIZE as usize;
const KDF_LEN: usize = 32;
### src/rust/bitbox02-rust-c/src/lib.rs
@@ -112,11 +112,16 @@ const _: [(); 0] = [(); (BITBOX02_MEMORY_SPI_BLE_FIRMWARE_2_ADDR
// handler will print the available information on the screen and over RTT. If we compile with
// `panic=abort` this code will never get executed.
#[cfg_attr(feature = "bootloader", allow(unused_variables))]
-#[cfg(not(any(
- feature = "testing",
- feature = "c-unit-testing",
- feature = "simulator-graphical"
-)))]
+#[cfg(all(
+ target_arch = "arm",
+ target_os = "none",
+ not(any(
+ test,
+ feature = "testing",
+ feature = "c-unit-testing",
+ feature = "simulator-graphical"
+ ))
+))]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
#[cfg(feature = "firmware")]
### src/rust/bitbox02-rust/Cargo.toml
@@ -122,7 +122,10 @@ firmware = []
[dev-dependencies]
async_test = { path = "../async_test" }
+bitbox-platform-host = { path = "../bitbox-platform-host", features = ["testing"] }
+bitbox-secp256k1 = { path = "../bitbox-secp256k1", features = ["testing"] }
bitbox-test-vectors = { path = "../bitbox-test-vectors" }
+bitbox02 = { path = "../bitbox02", features = ["testing"] }
semver = "1"
serde = { workspace = true }
serde_json = { workspace = true }
### src/rust/bitbox02-rust/src/hal.rs
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
-#[cfg(feature = "testing")]
+#[cfg(any(test, feature = "testing"))]
pub mod testing;
pub use bitbox_hal::*;
### src/rust/bitbox02-rust/src/hww.rs
@@ -18,7 +18,7 @@ const OP_STATUS_FAILURE_UNINITIALIZED: u8 = 2;
/// Must be called during the execution of a usb task. This sends out the response to the host and
/// awaits the next request. If the request is not a valid noise encrypted protofbuf api request
/// message, `Err(Error::InvalidInput)` is returned.
-#[cfg(not(feature = "testing"))]
+#[cfg(not(any(test, feature = "testing")))]
pub async fn next_request(
response: crate::pb::response::Response,
) -> Result<crate::pb::request::Request, api::error::Error> {
@@ -35,13 +35,13 @@ pub async fn next_request(
}
}
-#[cfg(feature = "testing")]
+#[cfg(any(test, feature = "testing"))]
pub struct SafeData<T>(T);
// Safety: must not be accessed concurrently.
-#[cfg(feature = "testing")]
+#[cfg(any(test, feature = "testing"))]
unsafe impl<T> Sync for SafeData<T> {}
-#[cfg(feature = "testing")]
+#[cfg(any(test, feature = "testing"))]
pub static MOCK_NEXT_REQUEST: SafeData<
core::cell::RefCell<
Option<
@@ -55,7 +55,7 @@ pub static MOCK_NEXT_REQUEST: SafeData<
> = SafeData(core::cell::RefCell::new(None));
/// Set `MOCK_NEXT_REQUEST` to mock requests from the host.
-#[cfg(feature = "testing")]
+#[cfg(any(test, feature = "testing"))]
pub async fn next_request(
response: crate::pb::response::Response,
) -> Result<crate::pb::request::Request, api::error::Error> {
@@ -468,6 +468,7 @@ mod tests {
}
/// Test creating a seed, backing it up on SD, checking the backup, and restoring from the that backup.
+ #[cfg(feature = "app-u2f")]
#[async_test::test]
async fn test_backup_create_check_list_restore() {
// Test everything with a 32 and 16 byte seed (determined by the host entropy when creating the seed).
### src/rust/bitbox02-rust/src/hww/api/payment_request.rs
@@ -243,7 +243,7 @@ fn compute_sighash(
Ok(sighash.finalize().to_vec())
}
-#[cfg(feature = "testing")]
+#[cfg(any(test, feature = "testing"))]
#[allow(dead_code)]
pub fn tst_sign_payment_request_btc(
coin: pb::BtcCoin,
@@ -260,7 +260,7 @@ pub fn tst_sign_payment_request_btc(
);
}
-#[cfg(feature = "testing")]
+#[cfg(any(test, feature = "testing"))]
#[allow(dead_code)]
pub fn tst_sign_payment_request_eth(
source_coin_type: u32,
@@ -276,7 +276,7 @@ pub fn tst_sign_payment_request_eth(
);
}
-#[cfg(feature = "testing")]
+#[cfg(any(test, feature = "testing"))]
#[allow(dead_code)]
fn tst_sign_payment_request(
source_coin_type: u32,
### src/rust/bitbox02-rust/src/keystore.rs
@@ -903,7 +903,7 @@ pub async fn secp256k1_schnorr_sign(
}
/// Get the seed to be used for u2f
-#[cfg(feature = "app-u2f")]
+#[cfg(any(test, feature = "app-u2f"))]
pub async fn get_u2f_seed(
hal: &mut impl crate::hal::Hal,
) -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
@@ -917,7 +917,7 @@ pub async fn get_u2f_seed(
))
}
-#[cfg(feature = "testing")]
+#[cfg(any(test, feature = "testing"))]
pub mod testing {
/// This mocks an unlocked keystore with the given bip39 recovery words and bip39 passphrase.
pub fn mock_unlocked_using_mnemonic(mnemonic: &str, passphrase: &str) {
### src/rust/bitbox02-rust/src/reset.rs
@@ -71,7 +71,7 @@ pub(crate) async fn reset(hal: &mut impl crate::hal::Hal, status: bool) {
hal.system().reset_ble();
}
- #[cfg(not(any(feature = "testing", feature = "c-unit-testing")))]
+ #[cfg(not(any(test, feature = "testing", feature = "c-unit-testing")))]
hal.system().reboot();
}
@@ -98,6 +98,7 @@ mod tests {
hal.securechip.mock_reset_keys_fails();
// Simulate a non-zero U2F counter before reset.
+ #[cfg(feature = "app-u2f")]
hal.securechip.u2f_counter_set(42).await.unwrap();
hal.securechip.event_counter_reset();
@@ -115,6 +116,7 @@ mod tests {
// SmartEEPROM was disabled as part of the reset.
assert!(!hal.eeprom.enabled);
+ #[cfg(feature = "app-u2f")]
assert_eq!(hal.securechip.get_u2f_counter(), 0);
assert_eq!(
### src/rust/util/src/sha2.rs
@@ -90,7 +90,7 @@ pub unsafe extern "C" fn rust_sha256(data: *const c_void, len: usize, out: *mut
/// long.
/// `out` may overlap with `data` (and/or `key`). This is supported safely: the HMAC is computed
/// first and only then written to `out`.
-#[cfg(feature = "firmware")]
+#[cfg(any(test, feature = "firmware"))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rust_hmac_sha256(
key: *const c_void,
@@ -113,7 +113,7 @@ pub unsafe extern "C" fn rust_hmac_sha256(
/// long.
/// `out` may overlap with `data` (and/or `key`). This is supported safely: the HMAC is computed
/// first and only then written to `out`.
-#[cfg(feature = "firmware")]
+#[cfg(any(test, feature = "firmware"))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rust_hmac_sha512(
key: *const c_void,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.