hal: move bitbox02 HAL impl to bitbox02 crate
What changed, and why it matters
This commit is a pure internal code reorganization: it moves the BitBox02 hardware-abstraction-layer implementation from one Rust crate (bitbox02-rust) into another (bitbox02). The actual logic, behavior, and security-sensitive operations such as memory access, random number generation, secure-chip handling, SD card access, and user-interface prompts remain identical; only the file paths and import paths change. There is no indication of a security fix or vulnerability being addressed.
No security action required. Treat as routine refactoring; verify that the build and tests pass after the crate reorganization.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change relocates the BitBox02 HAL implementation modules (memory, random, sd, securechip, system, ui) and the BitBox02Hal struct from bitbox02-rust/src/hal/bitbox02 to bitbox02/src/hal. Imports are updated from crate::hal:: / bitbox02:: to bitbox_hal:: / crate::. Cargo.toml files are adjusted to add bitbox-hal, futures-lite, and grounded as dependencies of bitbox02, and to promote grounded to a workspace dependency. The diff is almost entirely path renames and import rewrites; no functional logic changes are visible.
Changed components
src/rust/bitbox02-rust/src/hal.rssrc/rust/bitbox02/src/hal/*src/rust/bitbox02/Cargo.tomlsrc/rust/Cargo.tomlsrc/rust/Cargo.lockInspect captured patch +941 / −935
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index ba8cc4f..5633c2b 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -143,8 +143,11 @@ dependencies = [
"bip39",
"bitbox-aes",
"bitbox-framed-serial-link",
+ "bitbox-hal",
"bitbox02-rust",
"bitbox02-sys",
+ "futures-lite",
+ "grounded",
"hex_lit",
"util",
"zeroize",
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index 0645e31..5a3053c 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -49,6 +49,7 @@ hex_lit = { version = "0.1.1", default-features = false }
crc = "3.0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
+grounded = { version = "0.2.0", default-features = false, features = ["critical-section"] }
[patch.crates-io]
rtt-target = { git = "https://github.com/probe-rs/rtt-target.git", rev = "117d9519a5d3b1f4bc024bc05f9e3c5dec0a57f5" }
diff --git a/src/rust/bitbox02-rust/Cargo.toml b/src/rust/bitbox02-rust/Cargo.toml
index 4639a70..27354cc 100644
--- a/src/rust/bitbox02-rust/Cargo.toml
+++ b/src/rust/bitbox02-rust/Cargo.toml
@@ -50,7 +50,7 @@ bip39 = { workspace = true }
bitcoin_hashes = { version = "0.14.0", default-features = false, features = ["small-hash"] }
futures-lite = { workspace = true }
hex_lit = { workspace = true, features = ["rust_v_1_46"] }
-grounded = { version = "0.2.0", default-features = false, features = ["critical-section"] }
+grounded = { workspace = true }
[dependencies.prost]
# keep version in sync with tools/prost-build/Cargo.toml.
diff --git a/src/rust/bitbox02-rust/src/hal.rs b/src/rust/bitbox02-rust/src/hal.rs
index 791d78b..fdd0d72 100644
--- a/src/rust/bitbox02-rust/src/hal.rs
+++ b/src/rust/bitbox02-rust/src/hal.rs
@@ -1,9 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
-pub mod bitbox02;
-
#[cfg(feature = "testing")]
pub mod testing;
pub use bitbox_hal::*;
-pub use bitbox02::BitBox02Hal;
+pub use bitbox02::hal::BitBox02Hal;
diff --git a/src/rust/bitbox02-rust/src/hal/bitbox02.rs b/src/rust/bitbox02-rust/src/hal/bitbox02.rs
deleted file mode 100644
index b8f2075..0000000
--- a/src/rust/bitbox02-rust/src/hal/bitbox02.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-pub mod memory;
-pub mod random;
-pub mod sd;
-pub mod securechip;
-pub mod system;
-pub mod ui;
-
-use crate::hal::Hal;
-
-pub struct BitBox02Hal {
- ui: ui::BitBox02Ui,
- sd: sd::BitBox02Sd,
- random: random::BitBox02Random,
- securechip: securechip::BitBox02SecureChip,
- memory: memory::BitBox02Memory,
- system: system::BitBox02System,
-}
-
-impl grounded::const_init::ConstInit for BitBox02Hal {
- const VAL: Self = Self::new();
-}
-
-impl BitBox02Hal {
- pub const fn new() -> Self {
- Self {
- ui: ui::BitBox02Ui,
- sd: sd::BitBox02Sd,
- random: random::BitBox02Random,
- securechip: securechip::BitBox02SecureChip,
- memory: memory::BitBox02Memory,
- system: system::BitBox02System,
- }
- }
-}
-
-impl Hal for BitBox02Hal {
- type Ui = ui::BitBox02Ui;
- type Random = random::BitBox02Random;
- type Sd = sd::BitBox02Sd;
- type SecureChip = securechip::BitBox02SecureChip;
- type Memory = memory::BitBox02Memory;
- type System = system::BitBox02System;
-
- fn subsystems(
- &mut self,
- ) -> crate::hal::HalSubsystems<
- '_,
- Self::Ui,
- Self::Random,
- Self::Sd,
- Self::SecureChip,
- Self::Memory,
- Self::System,
- > {
- crate::hal::HalSubsystems {
- ui: &mut self.ui,
- random: &mut self.random,
- sd: &mut self.sd,
- securechip: &mut self.securechip,
- memory: &mut self.memory,
- system: &mut self.system,
- }
- }
-}
diff --git a/src/rust/bitbox02-rust/src/hal/bitbox02/memory.rs b/src/rust/bitbox02-rust/src/hal/bitbox02/memory.rs
deleted file mode 100644
index c254b23..0000000
--- a/src/rust/bitbox02-rust/src/hal/bitbox02/memory.rs
+++ /dev/null
@@ -1,257 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use alloc::string::String;
-use alloc::vec::Vec;
-
-use crate::hal::Memory;
-use crate::hal::memory::{Error, PasswordStretchAlgo, Platform, SecurechipType};
-
-pub struct BitBox02Memory;
-
-fn to_hal_securechip_type(securechip_type: bitbox02::memory::SecurechipType) -> SecurechipType {
- match securechip_type {
- bitbox02::memory::SecurechipType::Atecc => SecurechipType::Atecc,
- bitbox02::memory::SecurechipType::Optiga => SecurechipType::Optiga,
- }
-}
-
-fn to_hal_platform(platform: bitbox02::memory::Platform) -> Platform {
- match platform {
- bitbox02::memory::Platform::BitBox02 => Platform::BitBox02,
- bitbox02::memory::Platform::BitBox02Plus => Platform::BitBox02Plus,
- }
-}
-
-fn to_hal_password_stretch_algo(
- algo: bitbox02::memory::PasswordStretchAlgo,
-) -> PasswordStretchAlgo {
- match algo {
- bitbox02::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V0 => {
- PasswordStretchAlgo::V0
- }
- bitbox02::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V1 => {
- PasswordStretchAlgo::V1
- }
- }
-}
-
-fn to_hal_error(error: bitbox02::memory::MemoryError) -> Error {
- match error {
- bitbox02::memory::MemoryError::MEMORY_OK => {
- unreachable!("MEMORY_OK must not be converted to hal::memory::Error")
- }
- bitbox02::memory::MemoryError::MEMORY_ERR_INVALID_INPUT => Error::InvalidInput,
- bitbox02::memory::MemoryError::MEMORY_ERR_FULL => Error::Full,
- bitbox02::memory::MemoryError::MEMORY_ERR_DUPLICATE_NAME => Error::DuplicateName,
- bitbox02::memory::MemoryError::MEMORY_ERR_UNKNOWN => Error::Unknown,
- }
-}
-
-pub(super) fn to_bitbox02_password_stretch_algo(
- algo: PasswordStretchAlgo,
-) -> bitbox02::memory::PasswordStretchAlgo {
- match algo {
- PasswordStretchAlgo::V0 => {
- bitbox02::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V0
- }
- PasswordStretchAlgo::V1 => {
- bitbox02::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V1
- }
- }
-}
-
-impl Memory for BitBox02Memory {
- fn ble_enabled(&mut self) -> bool {
- bitbox02::memory::ble_enabled()
- }
-
- fn ble_enable(&mut self, enable: bool) -> Result<(), ()> {
- bitbox02::memory::ble_enable(enable)
- }
-
- fn get_securechip_type(&mut self) -> Result<SecurechipType, ()> {
- bitbox02::memory::get_securechip_type().map(to_hal_securechip_type)
- }
-
- fn get_platform(&mut self) -> Result<Platform, ()> {
- bitbox02::memory::get_platform().map(to_hal_platform)
- }
-
- fn get_device_name(&mut self) -> String {
- bitbox02::memory::get_device_name()
- }
-
- fn set_device_name(&mut self, name: &str) -> Result<(), Error> {
- bitbox02::memory::set_device_name(name).map_err(to_hal_error)
- }
-
- fn is_mnemonic_passphrase_enabled(&mut self) -> bool {
- bitbox02::memory::is_mnemonic_passphrase_enabled()
- }
-
- fn set_mnemonic_passphrase_enabled(&mut self, enabled: bool) -> Result<(), ()> {
- bitbox02::memory::set_mnemonic_passphrase_enabled(enabled)
- }
-
- fn set_seed_birthdate(&mut self, timestamp: u32) -> Result<(), ()> {
- bitbox02::memory::set_seed_birthdate(timestamp)
- }
-
- fn get_seed_birthdate(&mut self) -> u32 {
- bitbox02::memory::get_seed_birthdate()
- }
-
- fn is_seeded(&mut self) -> bool {
- bitbox02::memory::is_seeded()
- }
-
- fn is_initialized(&mut self) -> bool {
- bitbox02::memory::is_initialized()
- }
-
- fn set_initialized(&mut self) -> Result<(), ()> {
- bitbox02::memory::set_initialized()
- }
-
- fn get_encrypted_seed_and_hmac(&mut self) -> Result<(Vec<u8>, PasswordStretchAlgo), ()> {
- bitbox02::memory::get_encrypted_seed_and_hmac()
- .map(|(seed, algo)| (seed, to_hal_password_stretch_algo(algo)))
- }
-
- fn set_encrypted_seed_and_hmac(
- &mut self,
- data: &[u8],
- password_stretch_algo: PasswordStretchAlgo,
- ) -> Result<(), ()> {
- bitbox02::memory::set_encrypted_seed_and_hmac(
- data,
- to_bitbox02_password_stretch_algo(password_stretch_algo),
- )
- }
-
- fn reset_hww(&mut self) -> Result<(), ()> {
- bitbox02::memory::reset_hww()
- }
-
- fn get_unlock_attempts(&mut self) -> u8 {
- bitbox02::memory::smarteeprom_get_unlock_attempts()
- }
-
- fn increment_unlock_attempts(&mut self) {
- bitbox02::memory::smarteeprom_increment_unlock_attempts()
- }
-
- fn reset_unlock_attempts(&mut self) {
- bitbox02::memory::smarteeprom_reset_unlock_attempts()
- }
-
- fn get_salt_root(&mut self) -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
- bitbox02::memory::get_salt_root()
- }
-
- fn get_attestation_pubkey_and_certificate(
- &mut self,
- pubkey_out: &mut [u8; 64],
- certificate_out: &mut [u8; 64],
- root_pubkey_identifier_out: &mut [u8; 32],
- ) -> Result<(), ()> {
- bitbox02::memory::get_attestation_pubkey_and_certificate(
- pubkey_out,
- certificate_out,
- root_pubkey_identifier_out,
- )
- }
-
- fn get_attestation_bootloader_hash(&mut self) -> [u8; 32] {
- bitbox02::memory::get_attestation_bootloader_hash()
- }
-
- fn multisig_set_by_hash(&mut self, hash: &[u8; 32], name: &str) -> Result<(), Error> {
- bitbox02::memory::multisig_set_by_hash(hash, name).map_err(to_hal_error)
- }
-
- fn multisig_get_by_hash(&self, hash: &[u8; 32]) -> Option<String> {
- bitbox02::memory::multisig_get_by_hash(hash)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_to_hal_securechip_type() {
- assert_eq!(
- to_hal_securechip_type(bitbox02::memory::SecurechipType::Atecc),
- SecurechipType::Atecc,
- );
- assert_eq!(
- to_hal_securechip_type(bitbox02::memory::SecurechipType::Optiga),
- SecurechipType::Optiga,
- );
- }
-
- #[test]
- fn test_to_hal_platform() {
- assert_eq!(
- to_hal_platform(bitbox02::memory::Platform::BitBox02),
- Platform::BitBox02,
- );
- assert_eq!(
- to_hal_platform(bitbox02::memory::Platform::BitBox02Plus),
- Platform::BitBox02Plus,
- );
- }
-
- #[test]
- fn test_to_hal_error() {
- let cases = [
- (
- bitbox02::memory::MemoryError::MEMORY_ERR_INVALID_INPUT,
- Error::InvalidInput,
- ),
- (bitbox02::memory::MemoryError::MEMORY_ERR_FULL, Error::Full),
- (
- bitbox02::memory::MemoryError::MEMORY_ERR_DUPLICATE_NAME,
- Error::DuplicateName,
- ),
- (
- bitbox02::memory::MemoryError::MEMORY_ERR_UNKNOWN,
- Error::Unknown,
- ),
- ];
- for (input, expected) in cases {
- assert_eq!(to_hal_error(input), expected);
- }
- }
-
- #[test]
- #[should_panic(expected = "MEMORY_OK must not be converted to hal::memory::Error")]
- fn test_to_hal_error_memory_ok_panics() {
- let _ = to_hal_error(bitbox02::memory::MemoryError::MEMORY_OK);
- }
-
- #[test]
- fn test_password_stretch_algo_mappings() {
- assert_eq!(
- to_hal_password_stretch_algo(
- bitbox02::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V0,
- ),
- PasswordStretchAlgo::V0,
- );
- assert_eq!(
- to_hal_password_stretch_algo(
- bitbox02::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V1,
- ),
- PasswordStretchAlgo::V1,
- );
- assert_eq!(
- to_bitbox02_password_stretch_algo(PasswordStretchAlgo::V0) as i32,
- bitbox02::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V0 as i32,
- );
- assert_eq!(
- to_bitbox02_password_stretch_algo(PasswordStretchAlgo::V1) as i32,
- bitbox02::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V1 as i32,
- );
- }
-}
diff --git a/src/rust/bitbox02-rust/src/hal/bitbox02/random.rs b/src/rust/bitbox02-rust/src/hal/bitbox02/random.rs
deleted file mode 100644
index e1663fd..0000000
--- a/src/rust/bitbox02-rust/src/hal/bitbox02/random.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use alloc::boxed::Box;
-
-use crate::hal::Random;
-
-pub struct BitBox02Random;
-
-impl Random for BitBox02Random {
- #[inline(always)]
- fn random_32_bytes(&mut self) -> Box<zeroize::Zeroizing<[u8; 32]>> {
- bitbox02::random::random_32_bytes()
- }
-
- #[inline(always)]
- fn mcu_32_bytes(&mut self, out: &mut [u8; 32]) {
- bitbox02::random::mcu_32_bytes(out);
- }
-}
diff --git a/src/rust/bitbox02-rust/src/hal/bitbox02/sd.rs b/src/rust/bitbox02-rust/src/hal/bitbox02/sd.rs
deleted file mode 100644
index 9bebd77..0000000
--- a/src/rust/bitbox02-rust/src/hal/bitbox02/sd.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use alloc::string::String;
-use alloc::vec::Vec;
-
-use futures_lite::future::yield_now;
-
-use crate::hal::Sd;
-
-pub struct BitBox02Sd;
-
-impl Sd for BitBox02Sd {
- #[inline(always)]
- async fn sdcard_inserted(&mut self) -> bool {
- let result = bitbox02::sd::sdcard_inserted();
- yield_now().await;
- result
- }
-
- #[inline(always)]
- async fn list_subdir(&mut self, subdir: Option<&str>) -> Result<Vec<String>, ()> {
- let result = bitbox02::sd::list_subdir(subdir);
- yield_now().await;
- result
- }
-
- #[inline(always)]
- async fn erase_file_in_subdir(&mut self, filename: &str, dir: &str) -> Result<(), ()> {
- let result = bitbox02::sd::erase_file_in_subdir(filename, dir);
- yield_now().await;
- result
- }
-
- #[inline(always)]
- async fn load_bin(
- &mut self,
- filename: &str,
- dir: &str,
- ) -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
- let result = bitbox02::sd::load_bin(filename, dir);
- yield_now().await;
- result
- }
-
- #[inline(always)]
- async fn write_bin(&mut self, filename: &str, dir: &str, data: &[u8]) -> Result<(), ()> {
- let result = bitbox02::sd::write_bin(filename, dir, data);
- yield_now().await;
- result
- }
-}
diff --git a/src/rust/bitbox02-rust/src/hal/bitbox02/securechip.rs b/src/rust/bitbox02-rust/src/hal/bitbox02/securechip.rs
deleted file mode 100644
index 6462b81..0000000
--- a/src/rust/bitbox02-rust/src/hal/bitbox02/securechip.rs
+++ /dev/null
@@ -1,232 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use alloc::vec::Vec;
-
-use crate::hal::SecureChip;
-use crate::hal::memory::PasswordStretchAlgo;
-use crate::hal::securechip::{Error, Model, SecureChipError};
-
-pub struct BitBox02SecureChip;
-
-fn to_hal_model(model: bitbox02::securechip::Model) -> Model {
- match model {
- bitbox02::securechip::Model::ATECC_ATECC608A => Model::Atecc608A,
- bitbox02::securechip::Model::ATECC_ATECC608B => Model::Atecc608B,
- bitbox02::securechip::Model::OPTIGA_TRUST_M_V3 => Model::OptigaTrustM3,
- }
-}
-
-fn to_hal_error(error: bitbox02::securechip::Error) -> Error {
- match error {
- bitbox02::securechip::Error::SecureChip(sc_err) => Error::SecureChip(match sc_err {
- bitbox02::securechip::SecureChipError::SC_ERR_IFS => SecureChipError::Ifs,
- bitbox02::securechip::SecureChipError::SC_ERR_INVALID_ARGS => {
- SecureChipError::InvalidArgs
- }
- bitbox02::securechip::SecureChipError::SC_ERR_CONFIG_MISMATCH => {
- SecureChipError::ConfigMismatch
- }
- bitbox02::securechip::SecureChipError::SC_ERR_SALT => SecureChipError::Salt,
- bitbox02::securechip::SecureChipError::SC_ERR_INCORRECT_PASSWORD => {
- SecureChipError::IncorrectPassword
- }
- bitbox02::securechip::SecureChipError::SC_ERR_INVALID_PASSWORD_STRETCH_ALGO => {
- SecureChipError::InvalidPasswordStretchAlgo
- }
- bitbox02::securechip::SecureChipError::SC_ERR_MEMORY => SecureChipError::Memory,
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_ZONE_UNLOCKED_CONFIG => {
- SecureChipError::AteccZoneUnlockedConfig
- }
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_ZONE_UNLOCKED_DATA => {
- SecureChipError::AteccZoneUnlockedData
- }
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_IO => {
- SecureChipError::AteccSlotUnlockedIo
- }
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_AUTH => {
- SecureChipError::AteccSlotUnlockedAuth
- }
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_ENC => {
- SecureChipError::AteccSlotUnlockedEnc
- }
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_RESET_KEYS => {
- SecureChipError::AteccResetKeys
- }
- bitbox02::securechip::SecureChipError::SC_OPTIGA_ERR_CREATE => {
- SecureChipError::OptigaCreate
- }
- bitbox02::securechip::SecureChipError::SC_OPTIGA_ERR_UNEXPECTED_METADATA => {
- SecureChipError::OptigaUnexpectedMetadata
- }
- bitbox02::securechip::SecureChipError::SC_OPTIGA_ERR_PAL => SecureChipError::OptigaPal,
- bitbox02::securechip::SecureChipError::SC_OPTIGA_ERR_UNEXPECTED_LEN => {
- SecureChipError::OptigaUnexpectedLen
- }
- }),
- bitbox02::securechip::Error::Status(status) => Error::Status(status),
- }
-}
-
-impl SecureChip for BitBox02SecureChip {
- fn init_new_password(
- &mut self,
- password: &str,
- password_stretch_algo: PasswordStretchAlgo,
- ) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
- bitbox02::securechip::init_new_password(
- password,
- super::memory::to_bitbox02_password_stretch_algo(password_stretch_algo),
- )
- .map_err(to_hal_error)
- }
-
- fn stretch_password(
- &mut self,
- password: &str,
- password_stretch_algo: PasswordStretchAlgo,
- ) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
- bitbox02::securechip::stretch_password(
- password,
- super::memory::to_bitbox02_password_stretch_algo(password_stretch_algo),
- )
- .map_err(to_hal_error)
- }
-
- fn kdf(&mut self, msg: &[u8]) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
- bitbox02::securechip::kdf(msg).map_err(to_hal_error)
- }
-
- fn attestation_sign(
- &mut self,
- challenge: &[u8; 32],
- signature: &mut [u8; 64],
- ) -> Result<(), ()> {
- bitbox02::securechip::attestation_sign(challenge, signature)
- }
-
- fn monotonic_increments_remaining(&mut self) -> Result<u32, ()> {
- bitbox02::securechip::monotonic_increments_remaining()
- }
-
- fn model(&mut self) -> Result<Model, ()> {
- bitbox02::securechip::model().map(to_hal_model)
- }
-
- fn reset_keys(&mut self) -> Result<(), ()> {
- bitbox02::securechip::reset_keys()
- }
-
- #[cfg(feature = "app-u2f")]
- fn u2f_counter_set(&mut self, counter: u32) -> Result<(), ()> {
- bitbox02::securechip::u2f_counter_set(counter)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_to_hal_model() {
- assert_eq!(
- to_hal_model(bitbox02::securechip::Model::ATECC_ATECC608A),
- Model::Atecc608A,
- );
- assert_eq!(
- to_hal_model(bitbox02::securechip::Model::ATECC_ATECC608B),
- Model::Atecc608B,
- );
- assert_eq!(
- to_hal_model(bitbox02::securechip::Model::OPTIGA_TRUST_M_V3),
- Model::OptigaTrustM3,
- );
- }
-
- #[test]
- fn test_to_hal_error_securechip() {
- let cases = [
- (
- bitbox02::securechip::SecureChipError::SC_ERR_IFS,
- SecureChipError::Ifs,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ERR_INVALID_ARGS,
- SecureChipError::InvalidArgs,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ERR_CONFIG_MISMATCH,
- SecureChipError::ConfigMismatch,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ERR_SALT,
- SecureChipError::Salt,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ERR_INCORRECT_PASSWORD,
- SecureChipError::IncorrectPassword,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ERR_INVALID_PASSWORD_STRETCH_ALGO,
- SecureChipError::InvalidPasswordStretchAlgo,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ERR_MEMORY,
- SecureChipError::Memory,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_ZONE_UNLOCKED_CONFIG,
- SecureChipError::AteccZoneUnlockedConfig,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_ZONE_UNLOCKED_DATA,
- SecureChipError::AteccZoneUnlockedData,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_IO,
- SecureChipError::AteccSlotUnlockedIo,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_AUTH,
- SecureChipError::AteccSlotUnlockedAuth,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_ENC,
- SecureChipError::AteccSlotUnlockedEnc,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_ATECC_ERR_RESET_KEYS,
- SecureChipError::AteccResetKeys,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_OPTIGA_ERR_CREATE,
- SecureChipError::OptigaCreate,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_OPTIGA_ERR_UNEXPECTED_METADATA,
- SecureChipError::OptigaUnexpectedMetadata,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_OPTIGA_ERR_PAL,
- SecureChipError::OptigaPal,
- ),
- (
- bitbox02::securechip::SecureChipError::SC_OPTIGA_ERR_UNEXPECTED_LEN,
- SecureChipError::OptigaUnexpectedLen,
- ),
- ];
- for (input, expected) in cases {
- assert_eq!(
- to_hal_error(bitbox02::securechip::Error::SecureChip(input)),
- Error::SecureChip(expected),
- );
- }
- }
-
- #[test]
- fn test_to_hal_error_status() {
- assert_eq!(
- to_hal_error(bitbox02::securechip::Error::Status(7)),
- Error::Status(7)
- );
- }
-}
diff --git a/src/rust/bitbox02-rust/src/hal/bitbox02/system.rs b/src/rust/bitbox02-rust/src/hal/bitbox02/system.rs
deleted file mode 100644
index 62be659..0000000
--- a/src/rust/bitbox02-rust/src/hal/bitbox02/system.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use crate::hal::System;
-
-pub struct BitBox02System;
-
-impl System for BitBox02System {
- fn reboot_to_bootloader(&mut self) -> ! {
- bitbox02::reboot_to_bootloader()
- }
-}
diff --git a/src/rust/bitbox02-rust/src/hal/bitbox02/ui.rs b/src/rust/bitbox02-rust/src/hal/bitbox02/ui.rs
deleted file mode 100644
index b36bb75..0000000
--- a/src/rust/bitbox02-rust/src/hal/bitbox02/ui.rs
+++ /dev/null
@@ -1,294 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use alloc::string::String;
-
-use crate::hal::Ui;
-use crate::hal::ui::{CanCancel, ConfirmParams, EnterStringParams, Font, TrinaryChoice, UserAbort};
-
-pub struct BitBox02Ui;
-
-fn to_bitbox02_font(font: Font) -> bitbox02::ui::Font {
- match font {
- Font::Default => bitbox02::ui::Font::Default,
- Font::Password11X12 => bitbox02::ui::Font::Password11X12,
- Font::Monogram5X9 => bitbox02::ui::Font::Monogram5X9,
- }
-}
-
-fn to_bitbox02_confirm_params<'a>(
- params: &'a ConfirmParams<'a>,
-) -> bitbox02::ui::ConfirmParams<'a> {
- bitbox02::ui::ConfirmParams {
- title: params.title,
- title_autowrap: params.title_autowrap,
- body: params.body,
- font: to_bitbox02_font(params.font),
- scrollable: params.scrollable,
- longtouch: params.longtouch,
- accept_only: params.accept_only,
- accept_is_nextarrow: params.accept_is_nextarrow,
- display_size: params.display_size,
- }
-}
-
-fn to_bitbox02_trinary_input_string_params<'a>(
- params: &'a EnterStringParams<'a>,
-) -> bitbox02::ui::TrinaryInputStringParams<'a> {
- bitbox02::ui::TrinaryInputStringParams {
- title: params.title,
- wordlist: params.wordlist,
- number_input: params.number_input,
- hide: params.hide,
- special_chars: params.special_chars,
- longtouch: params.longtouch,
- cancel_is_backbutton: params.cancel_is_backbutton,
- default_to_digits: params.default_to_digits,
- }
-}
-
-fn to_hal_trinary_choice(choice: bitbox02::ui::TrinaryChoice) -> TrinaryChoice {
- match choice {
- bitbox02::ui::TrinaryChoice::TRINARY_CHOICE_LEFT => TrinaryChoice::Left,
- bitbox02::ui::TrinaryChoice::TRINARY_CHOICE_MIDDLE => TrinaryChoice::Middle,
- bitbox02::ui::TrinaryChoice::TRINARY_CHOICE_RIGHT => TrinaryChoice::Right,
- }
-}
-
-impl Ui for BitBox02Ui {
- #[inline(always)]
- async fn confirm(&mut self, params: &ConfirmParams<'_>) -> Result<(), UserAbort> {
- let params = to_bitbox02_confirm_params(params);
- match bitbox02::ui::confirm(¶ms).await {
- bitbox02::ui::ConfirmResponse::Approved => Ok(()),
- bitbox02::ui::ConfirmResponse::Cancelled => Err(UserAbort),
- }
- }
-
- #[inline(always)]
- async fn verify_recipient(&mut self, recipient: &str, amount: &str) -> Result<(), UserAbort> {
- match bitbox02::ui::confirm_transaction_address(amount, recipient).await {
- bitbox02::ui::ConfirmResponse::Approved => Ok(()),
- bitbox02::ui::ConfirmResponse::Cancelled => Err(UserAbort),
- }
- }
-
- #[inline(always)]
- async fn verify_total_fee(
- &mut self,
- total: &str,
- fee: &str,
- longtouch: bool,
- ) -> Result<(), UserAbort> {
- match bitbox02::ui::confirm_transaction_fee(total, fee, longtouch).await {
- bitbox02::ui::ConfirmResponse::Approved => Ok(()),
- bitbox02::ui::ConfirmResponse::Cancelled => Err(UserAbort),
- }
- }
-
- #[inline(always)]
- async fn status(&mut self, title: &str, status_success: bool) {
- bitbox02::ui::status(title, status_success).await
- }
-
- #[inline(always)]
- async fn enter_string(
- &mut self,
- params: &EnterStringParams<'_>,
- can_cancel: CanCancel,
- preset: &str,
- ) -> Result<zeroize::Zeroizing<String>, UserAbort> {
- let params = to_bitbox02_trinary_input_string_params(params);
- let can_cancel = match can_cancel {
- CanCancel::Yes => true,
- CanCancel::No => false,
- };
- bitbox02::ui::trinary_input_string(¶ms, can_cancel, preset)
- .await
- .map_err(|_| UserAbort)
- }
-
- #[inline(always)]
- async fn insert_sdcard(&mut self) -> Result<(), UserAbort> {
- match bitbox02::ui::sdcard().await {
- bitbox02::ui::SdcardResponse::Inserted => Ok(()),
- bitbox02::ui::SdcardResponse::Cancelled => Err(UserAbort),
- }
- }
-
- #[inline(always)]
- async fn menu(&mut self, words: &[&str], title: Option<&str>) -> Result<u8, UserAbort> {
- match bitbox02::ui::menu(bitbox02::ui::MenuParams {
- words,
- title,
- select_word: true,
- continue_on_last: false,
- cancel_confirm_title: None,
- })
- .await
- {
- bitbox02::ui::MenuResponse::SelectWord(choice_idx) => Ok(choice_idx),
- bitbox02::ui::MenuResponse::ContinueOnLast => panic!("unexpected continue-on-last"),
- bitbox02::ui::MenuResponse::Cancel => Err(UserAbort),
- }
- }
-
- #[inline(always)]
- async fn trinary_choice(
- &mut self,
- message: &str,
- label_left: Option<&str>,
- label_middle: Option<&str>,
- label_right: Option<&str>,
- ) -> TrinaryChoice {
- to_hal_trinary_choice(
- bitbox02::ui::trinary_choice(message, label_left, label_middle, label_right).await,
- )
- }
-
- async fn show_mnemonic(&mut self, words: &[&str]) -> Result<(), UserAbort> {
- match bitbox02::ui::menu(bitbox02::ui::MenuParams {
- words,
- title: None,
- select_word: false,
- continue_on_last: true,
- cancel_confirm_title: Some("Recovery\nwords"),
- })
- .await
- {
- bitbox02::ui::MenuResponse::ContinueOnLast => Ok(()),
- bitbox02::ui::MenuResponse::SelectWord(_) => panic!("unexpected select-word"),
- bitbox02::ui::MenuResponse::Cancel => Err(UserAbort),
- }
- }
-
- async fn quiz_mnemonic_word(&mut self, choices: &[&str], title: &str) -> Result<u8, UserAbort> {
- match bitbox02::ui::menu(bitbox02::ui::MenuParams {
- words: choices,
- title: Some(title),
- select_word: true,
- continue_on_last: false,
- cancel_confirm_title: Some("Recovery\nwords"),
- })
- .await
- {
- bitbox02::ui::MenuResponse::SelectWord(choice_idx) => Ok(choice_idx),
- bitbox02::ui::MenuResponse::ContinueOnLast => panic!("unexpected continue-on-last"),
- bitbox02::ui::MenuResponse::Cancel => Err(UserAbort),
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_to_bitbox02_font() {
- let cases = [
- (Font::Default, bitbox02::ui::Font::Default),
- (Font::Password11X12, bitbox02::ui::Font::Password11X12),
- (Font::Monogram5X9, bitbox02::ui::Font::Monogram5X9),
- ];
- for (input, expected) in cases {
- assert_eq!(to_bitbox02_font(input) as i32, expected as i32);
- }
- }
-
- #[test]
- fn test_to_bitbox02_confirm_params() {
- let fonts = [
- (Font::Default, bitbox02::ui::Font::Default),
- (Font::Password11X12, bitbox02::ui::Font::Password11X12),
- (Font::Monogram5X9, bitbox02::ui::Font::Monogram5X9),
- ];
- for (font, expected_font) in fonts {
- let input = ConfirmParams {
- title: "title",
- title_autowrap: true,
- body: "body",
- font,
- scrollable: true,
- longtouch: true,
- accept_only: true,
- accept_is_nextarrow: true,
- display_size: 42,
- };
- let output = to_bitbox02_confirm_params(&input);
- assert_eq!(output.title, "title");
- assert!(output.title_autowrap);
- assert_eq!(output.body, "body");
- assert_eq!(output.font as i32, expected_font as i32);
- assert!(output.scrollable);
- assert!(output.longtouch);
- assert!(output.accept_only);
- assert!(output.accept_is_nextarrow);
- assert_eq!(output.display_size, 42);
- }
- }
-
- #[test]
- fn test_to_bitbox02_trinary_input_string_params() {
- let input_without_wordlist = EnterStringParams {
- title: "Enter",
- wordlist: None,
- number_input: true,
- hide: true,
- special_chars: true,
- longtouch: true,
- cancel_is_backbutton: true,
- default_to_digits: true,
- };
- let output_without_wordlist =
- to_bitbox02_trinary_input_string_params(&input_without_wordlist);
- assert_eq!(output_without_wordlist.title, "Enter");
- assert!(output_without_wordlist.wordlist.is_none());
- assert!(output_without_wordlist.number_input);
- assert!(output_without_wordlist.hide);
- assert!(output_without_wordlist.special_chars);
- assert!(output_without_wordlist.longtouch);
- assert!(output_without_wordlist.cancel_is_backbutton);
- assert!(output_without_wordlist.default_to_digits);
-
- let wordlist = [1u16, 2, 3];
- let input_with_wordlist = EnterStringParams {
- title: "Seed",
- wordlist: Some(&wordlist),
- number_input: false,
- hide: false,
- special_chars: false,
- longtouch: false,
- cancel_is_backbutton: false,
- default_to_digits: false,
- };
- let output_with_wordlist = to_bitbox02_trinary_input_string_params(&input_with_wordlist);
- assert_eq!(output_with_wordlist.title, "Seed");
- assert_eq!(output_with_wordlist.wordlist.unwrap(), wordlist.as_slice());
- assert!(!output_with_wordlist.number_input);
- assert!(!output_with_wordlist.hide);
- assert!(!output_with_wordlist.special_chars);
- assert!(!output_with_wordlist.longtouch);
- assert!(!output_with_wordlist.cancel_is_backbutton);
- assert!(!output_with_wordlist.default_to_digits);
- }
-
- #[test]
- fn test_to_hal_trinary_choice() {
- let cases = [
- (
- bitbox02::ui::TrinaryChoice::TRINARY_CHOICE_LEFT,
- TrinaryChoice::Left,
- ),
- (
- bitbox02::ui::TrinaryChoice::TRINARY_CHOICE_MIDDLE,
- TrinaryChoice::Middle,
- ),
- (
- bitbox02::ui::TrinaryChoice::TRINARY_CHOICE_RIGHT,
- TrinaryChoice::Right,
- ),
- ];
- for (input, expected) in cases {
- assert!(to_hal_trinary_choice(input) == expected);
- }
- }
-}
diff --git a/src/rust/bitbox02/Cargo.toml b/src/rust/bitbox02/Cargo.toml
index 275da85..957ee48 100644
--- a/src/rust/bitbox02/Cargo.toml
+++ b/src/rust/bitbox02/Cargo.toml
@@ -10,9 +10,12 @@ license = "Apache-2.0"
[dependencies]
bitbox02-sys = {path="../bitbox02-sys"}
+bitbox-hal = { path = "../bitbox-hal" }
util = {path = "../util"}
zeroize = { workspace = true }
bip39 = { workspace = true }
+futures-lite = { workspace = true }
+grounded = { workspace = true }
[dev-dependencies]
bitbox-aes = { path = "../bitbox-aes" }
@@ -33,4 +36,4 @@ simulator-graphical = []
app-ethereum = []
app-bitcoin = []
app-litecoin = []
-app-u2f = []
+app-u2f = ["bitbox-hal/app-u2f"]
diff --git a/src/rust/bitbox02/src/hal.rs b/src/rust/bitbox02/src/hal.rs
new file mode 100644
index 0000000..9067d26
--- /dev/null
+++ b/src/rust/bitbox02/src/hal.rs
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: Apache-2.0
+
+pub mod memory;
+pub mod random;
+pub mod sd;
+pub mod securechip;
+pub mod system;
+pub mod ui;
+
+use bitbox_hal::Hal;
+
+pub struct BitBox02Hal {
+ ui: ui::BitBox02Ui,
+ sd: sd::BitBox02Sd,
+ random: random::BitBox02Random,
+ securechip: securechip::BitBox02SecureChip,
+ memory: memory::BitBox02Memory,
+ system: system::BitBox02System,
+}
+
+impl grounded::const_init::ConstInit for BitBox02Hal {
+ const VAL: Self = Self::new();
+}
+
+impl BitBox02Hal {
+ pub const fn new() -> Self {
+ Self {
+ ui: ui::BitBox02Ui,
+ sd: sd::BitBox02Sd,
+ random: random::BitBox02Random,
+ securechip: securechip::BitBox02SecureChip,
+ memory: memory::BitBox02Memory,
+ system: system::BitBox02System,
+ }
+ }
+}
+
+impl Hal for BitBox02Hal {
+ type Ui = ui::BitBox02Ui;
+ type Random = random::BitBox02Random;
+ type Sd = sd::BitBox02Sd;
+ type SecureChip = securechip::BitBox02SecureChip;
+ type Memory = memory::BitBox02Memory;
+ type System = system::BitBox02System;
+
+ fn subsystems(
+ &mut self,
+ ) -> bitbox_hal::HalSubsystems<
+ '_,
+ Self::Ui,
+ Self::Random,
+ Self::Sd,
+ Self::SecureChip,
+ Self::Memory,
+ Self::System,
+ > {
+ bitbox_hal::HalSubsystems {
+ ui: &mut self.ui,
+ random: &mut self.random,
+ sd: &mut self.sd,
+ securechip: &mut self.securechip,
+ memory: &mut self.memory,
+ system: &mut self.system,
+ }
+ }
+}
diff --git a/src/rust/bitbox02/src/hal/memory.rs b/src/rust/bitbox02/src/hal/memory.rs
new file mode 100644
index 0000000..c08b0b4
--- /dev/null
+++ b/src/rust/bitbox02/src/hal/memory.rs
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use alloc::string::String;
+use alloc::vec::Vec;
+
+use bitbox_hal::Memory;
+use bitbox_hal::memory::{Error, PasswordStretchAlgo, Platform, SecurechipType};
+
+pub struct BitBox02Memory;
+
+fn to_hal_securechip_type(securechip_type: crate::memory::SecurechipType) -> SecurechipType {
+ match securechip_type {
+ crate::memory::SecurechipType::Atecc => SecurechipType::Atecc,
+ crate::memory::SecurechipType::Optiga => SecurechipType::Optiga,
+ }
+}
+
+fn to_hal_platform(platform: crate::memory::Platform) -> Platform {
+ match platform {
+ crate::memory::Platform::BitBox02 => Platform::BitBox02,
+ crate::memory::Platform::BitBox02Plus => Platform::BitBox02Plus,
+ }
+}
+
+fn to_hal_password_stretch_algo(algo: crate::memory::PasswordStretchAlgo) -> PasswordStretchAlgo {
+ match algo {
+ crate::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V0 => {
+ PasswordStretchAlgo::V0
+ }
+ crate::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V1 => {
+ PasswordStretchAlgo::V1
+ }
+ }
+}
+
+fn to_hal_error(error: crate::memory::MemoryError) -> Error {
+ match error {
+ crate::memory::MemoryError::MEMORY_OK => {
+ unreachable!("MEMORY_OK must not be converted to hal::memory::Error")
+ }
+ crate::memory::MemoryError::MEMORY_ERR_INVALID_INPUT => Error::InvalidInput,
+ crate::memory::MemoryError::MEMORY_ERR_FULL => Error::Full,
+ crate::memory::MemoryError::MEMORY_ERR_DUPLICATE_NAME => Error::DuplicateName,
+ crate::memory::MemoryError::MEMORY_ERR_UNKNOWN => Error::Unknown,
+ }
+}
+
+pub(super) fn to_bitbox02_password_stretch_algo(
+ algo: PasswordStretchAlgo,
+) -> crate::memory::PasswordStretchAlgo {
+ match algo {
+ PasswordStretchAlgo::V0 => {
+ crate::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V0
+ }
+ PasswordStretchAlgo::V1 => {
+ crate::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V1
+ }
+ }
+}
+
+impl Memory for BitBox02Memory {
+ fn ble_enabled(&mut self) -> bool {
+ crate::memory::ble_enabled()
+ }
+
+ fn ble_enable(&mut self, enable: bool) -> Result<(), ()> {
+ crate::memory::ble_enable(enable)
+ }
+
+ fn get_securechip_type(&mut self) -> Result<SecurechipType, ()> {
+ crate::memory::get_securechip_type().map(to_hal_securechip_type)
+ }
+
+ fn get_platform(&mut self) -> Result<Platform, ()> {
+ crate::memory::get_platform().map(to_hal_platform)
+ }
+
+ fn get_device_name(&mut self) -> String {
+ crate::memory::get_device_name()
+ }
+
+ fn set_device_name(&mut self, name: &str) -> Result<(), Error> {
+ crate::memory::set_device_name(name).map_err(to_hal_error)
+ }
+
+ fn is_mnemonic_passphrase_enabled(&mut self) -> bool {
+ crate::memory::is_mnemonic_passphrase_enabled()
+ }
+
+ fn set_mnemonic_passphrase_enabled(&mut self, enabled: bool) -> Result<(), ()> {
+ crate::memory::set_mnemonic_passphrase_enabled(enabled)
+ }
+
+ fn set_seed_birthdate(&mut self, timestamp: u32) -> Result<(), ()> {
+ crate::memory::set_seed_birthdate(timestamp)
+ }
+
+ fn get_seed_birthdate(&mut self) -> u32 {
+ crate::memory::get_seed_birthdate()
+ }
+
+ fn is_seeded(&mut self) -> bool {
+ crate::memory::is_seeded()
+ }
+
+ fn is_initialized(&mut self) -> bool {
+ crate::memory::is_initialized()
+ }
+
+ fn set_initialized(&mut self) -> Result<(), ()> {
+ crate::memory::set_initialized()
+ }
+
+ fn get_encrypted_seed_and_hmac(&mut self) -> Result<(Vec<u8>, PasswordStretchAlgo), ()> {
+ crate::memory::get_encrypted_seed_and_hmac()
+ .map(|(seed, algo)| (seed, to_hal_password_stretch_algo(algo)))
+ }
+
+ fn set_encrypted_seed_and_hmac(
+ &mut self,
+ data: &[u8],
+ password_stretch_algo: PasswordStretchAlgo,
+ ) -> Result<(), ()> {
+ crate::memory::set_encrypted_seed_and_hmac(
+ data,
+ to_bitbox02_password_stretch_algo(password_stretch_algo),
+ )
+ }
+
+ fn reset_hww(&mut self) -> Result<(), ()> {
+ crate::memory::reset_hww()
+ }
+
+ fn get_unlock_attempts(&mut self) -> u8 {
+ crate::memory::smarteeprom_get_unlock_attempts()
+ }
+
+ fn increment_unlock_attempts(&mut self) {
+ crate::memory::smarteeprom_increment_unlock_attempts()
+ }
+
+ fn reset_unlock_attempts(&mut self) {
+ crate::memory::smarteeprom_reset_unlock_attempts()
+ }
+
+ fn get_salt_root(&mut self) -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
+ crate::memory::get_salt_root()
+ }
+
+ fn get_attestation_pubkey_and_certificate(
+ &mut self,
+ pubkey_out: &mut [u8; 64],
+ certificate_out: &mut [u8; 64],
+ root_pubkey_identifier_out: &mut [u8; 32],
+ ) -> Result<(), ()> {
+ crate::memory::get_attestation_pubkey_and_certificate(
+ pubkey_out,
+ certificate_out,
+ root_pubkey_identifier_out,
+ )
+ }
+
+ fn get_attestation_bootloader_hash(&mut self) -> [u8; 32] {
+ crate::memory::get_attestation_bootloader_hash()
+ }
+
+ fn multisig_set_by_hash(&mut self, hash: &[u8; 32], name: &str) -> Result<(), Error> {
+ crate::memory::multisig_set_by_hash(hash, name).map_err(to_hal_error)
+ }
+
+ fn multisig_get_by_hash(&self, hash: &[u8; 32]) -> Option<String> {
+ crate::memory::multisig_get_by_hash(hash)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_to_hal_securechip_type() {
+ assert_eq!(
+ to_hal_securechip_type(crate::memory::SecurechipType::Atecc),
+ SecurechipType::Atecc,
+ );
+ assert_eq!(
+ to_hal_securechip_type(crate::memory::SecurechipType::Optiga),
+ SecurechipType::Optiga,
+ );
+ }
+
+ #[test]
+ fn test_to_hal_platform() {
+ assert_eq!(
+ to_hal_platform(crate::memory::Platform::BitBox02),
+ Platform::BitBox02,
+ );
+ assert_eq!(
+ to_hal_platform(crate::memory::Platform::BitBox02Plus),
+ Platform::BitBox02Plus,
+ );
+ }
+
+ #[test]
+ fn test_to_hal_error() {
+ let cases = [
+ (
+ crate::memory::MemoryError::MEMORY_ERR_INVALID_INPUT,
+ Error::InvalidInput,
+ ),
+ (crate::memory::MemoryError::MEMORY_ERR_FULL, Error::Full),
+ (
+ crate::memory::MemoryError::MEMORY_ERR_DUPLICATE_NAME,
+ Error::DuplicateName,
+ ),
+ (
+ crate::memory::MemoryError::MEMORY_ERR_UNKNOWN,
+ Error::Unknown,
+ ),
+ ];
+ for (input, expected) in cases {
+ assert_eq!(to_hal_error(input), expected);
+ }
+ }
+
+ #[test]
+ #[should_panic(expected = "MEMORY_OK must not be converted to hal::memory::Error")]
+ fn test_to_hal_error_memory_ok_panics() {
+ let _ = to_hal_error(crate::memory::MemoryError::MEMORY_OK);
+ }
+
+ #[test]
+ fn test_password_stretch_algo_mappings() {
+ assert_eq!(
+ to_hal_password_stretch_algo(
+ crate::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V0,
+ ),
+ PasswordStretchAlgo::V0,
+ );
+ assert_eq!(
+ to_hal_password_stretch_algo(
+ crate::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V1,
+ ),
+ PasswordStretchAlgo::V1,
+ );
+ assert_eq!(
+ to_bitbox02_password_stretch_algo(PasswordStretchAlgo::V0) as i32,
+ crate::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V0 as i32,
+ );
+ assert_eq!(
+ to_bitbox02_password_stretch_algo(PasswordStretchAlgo::V1) as i32,
+ crate::memory::PasswordStretchAlgo::MEMORY_PASSWORD_STRETCH_ALGO_V1 as i32,
+ );
+ }
+}
diff --git a/src/rust/bitbox02/src/hal/random.rs b/src/rust/bitbox02/src/hal/random.rs
new file mode 100644
index 0000000..0b40e14
--- /dev/null
+++ b/src/rust/bitbox02/src/hal/random.rs
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use alloc::boxed::Box;
+
+use bitbox_hal::Random;
+
+pub struct BitBox02Random;
+
+impl Random for BitBox02Random {
+ #[inline(always)]
+ fn random_32_bytes(&mut self) -> Box<zeroize::Zeroizing<[u8; 32]>> {
+ crate::random::random_32_bytes()
+ }
+
+ #[inline(always)]
+ fn mcu_32_bytes(&mut self, out: &mut [u8; 32]) {
+ crate::random::mcu_32_bytes(out);
+ }
+}
diff --git a/src/rust/bitbox02/src/hal/sd.rs b/src/rust/bitbox02/src/hal/sd.rs
new file mode 100644
index 0000000..9ac1c9f
--- /dev/null
+++ b/src/rust/bitbox02/src/hal/sd.rs
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use alloc::string::String;
+use alloc::vec::Vec;
+
+use futures_lite::future::yield_now;
+
+use bitbox_hal::Sd;
+
+pub struct BitBox02Sd;
+
+impl Sd for BitBox02Sd {
+ #[inline(always)]
+ async fn sdcard_inserted(&mut self) -> bool {
+ let result = crate::sd::sdcard_inserted();
+ yield_now().await;
+ result
+ }
+
+ #[inline(always)]
+ async fn list_subdir(&mut self, subdir: Option<&str>) -> Result<Vec<String>, ()> {
+ let result = crate::sd::list_subdir(subdir);
+ yield_now().await;
+ result
+ }
+
+ #[inline(always)]
+ async fn erase_file_in_subdir(&mut self, filename: &str, dir: &str) -> Result<(), ()> {
+ let result = crate::sd::erase_file_in_subdir(filename, dir);
+ yield_now().await;
+ result
+ }
+
+ #[inline(always)]
+ async fn load_bin(
+ &mut self,
+ filename: &str,
+ dir: &str,
+ ) -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
+ let result = crate::sd::load_bin(filename, dir);
+ yield_now().await;
+ result
+ }
+
+ #[inline(always)]
+ async fn write_bin(&mut self, filename: &str, dir: &str, data: &[u8]) -> Result<(), ()> {
+ let result = crate::sd::write_bin(filename, dir, data);
+ yield_now().await;
+ result
+ }
+}
diff --git a/src/rust/bitbox02/src/hal/securechip.rs b/src/rust/bitbox02/src/hal/securechip.rs
new file mode 100644
index 0000000..abf900d
--- /dev/null
+++ b/src/rust/bitbox02/src/hal/securechip.rs
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use alloc::vec::Vec;
+
+use bitbox_hal::SecureChip;
+use bitbox_hal::memory::PasswordStretchAlgo;
+use bitbox_hal::securechip::{Error, Model, SecureChipError};
+
+pub struct BitBox02SecureChip;
+
+fn to_hal_model(model: crate::securechip::Model) -> Model {
+ match model {
+ crate::securechip::Model::ATECC_ATECC608A => Model::Atecc608A,
+ crate::securechip::Model::ATECC_ATECC608B => Model::Atecc608B,
+ crate::securechip::Model::OPTIGA_TRUST_M_V3 => Model::OptigaTrustM3,
+ }
+}
+
+fn to_hal_error(error: crate::securechip::Error) -> Error {
+ match error {
+ crate::securechip::Error::SecureChip(sc_err) => Error::SecureChip(match sc_err {
+ crate::securechip::SecureChipError::SC_ERR_IFS => SecureChipError::Ifs,
+ crate::securechip::SecureChipError::SC_ERR_INVALID_ARGS => SecureChipError::InvalidArgs,
+ crate::securechip::SecureChipError::SC_ERR_CONFIG_MISMATCH => {
+ SecureChipError::ConfigMismatch
+ }
+ crate::securechip::SecureChipError::SC_ERR_SALT => SecureChipError::Salt,
+ crate::securechip::SecureChipError::SC_ERR_INCORRECT_PASSWORD => {
+ SecureChipError::IncorrectPassword
+ }
+ crate::securechip::SecureChipError::SC_ERR_INVALID_PASSWORD_STRETCH_ALGO => {
+ SecureChipError::InvalidPasswordStretchAlgo
+ }
+ crate::securechip::SecureChipError::SC_ERR_MEMORY => SecureChipError::Memory,
+ crate::securechip::SecureChipError::SC_ATECC_ERR_ZONE_UNLOCKED_CONFIG => {
+ SecureChipError::AteccZoneUnlockedConfig
+ }
+ crate::securechip::SecureChipError::SC_ATECC_ERR_ZONE_UNLOCKED_DATA => {
+ SecureChipError::AteccZoneUnlockedData
+ }
+ crate::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_IO => {
+ SecureChipError::AteccSlotUnlockedIo
+ }
+ crate::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_AUTH => {
+ SecureChipError::AteccSlotUnlockedAuth
+ }
+ crate::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_ENC => {
+ SecureChipError::AteccSlotUnlockedEnc
+ }
+ crate::securechip::SecureChipError::SC_ATECC_ERR_RESET_KEYS => {
+ SecureChipError::AteccResetKeys
+ }
+ crate::securechip::SecureChipError::SC_OPTIGA_ERR_CREATE => {
+ SecureChipError::OptigaCreate
+ }
+ crate::securechip::SecureChipError::SC_OPTIGA_ERR_UNEXPECTED_METADATA => {
+ SecureChipError::OptigaUnexpectedMetadata
+ }
+ crate::securechip::SecureChipError::SC_OPTIGA_ERR_PAL => SecureChipError::OptigaPal,
+ crate::securechip::SecureChipError::SC_OPTIGA_ERR_UNEXPECTED_LEN => {
+ SecureChipError::OptigaUnexpectedLen
+ }
+ }),
+ crate::securechip::Error::Status(status) => Error::Status(status),
+ }
+}
+
+impl SecureChip for BitBox02SecureChip {
+ fn init_new_password(
+ &mut self,
+ password: &str,
+ password_stretch_algo: PasswordStretchAlgo,
+ ) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
+ crate::securechip::init_new_password(
+ password,
+ super::memory::to_bitbox02_password_stretch_algo(password_stretch_algo),
+ )
+ .map_err(to_hal_error)
+ }
+
+ fn stretch_password(
+ &mut self,
+ password: &str,
+ password_stretch_algo: PasswordStretchAlgo,
+ ) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
+ crate::securechip::stretch_password(
+ password,
+ super::memory::to_bitbox02_password_stretch_algo(password_stretch_algo),
+ )
+ .map_err(to_hal_error)
+ }
+
+ fn kdf(&mut self, msg: &[u8]) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
+ crate::securechip::kdf(msg).map_err(to_hal_error)
+ }
+
+ fn attestation_sign(
+ &mut self,
+ challenge: &[u8; 32],
+ signature: &mut [u8; 64],
+ ) -> Result<(), ()> {
+ crate::securechip::attestation_sign(challenge, signature)
+ }
+
+ fn monotonic_increments_remaining(&mut self) -> Result<u32, ()> {
+ crate::securechip::monotonic_increments_remaining()
+ }
+
+ fn model(&mut self) -> Result<Model, ()> {
+ crate::securechip::model().map(to_hal_model)
+ }
+
+ fn reset_keys(&mut self) -> Result<(), ()> {
+ crate::securechip::reset_keys()
+ }
+
+ #[cfg(feature = "app-u2f")]
+ fn u2f_counter_set(&mut self, counter: u32) -> Result<(), ()> {
+ crate::securechip::u2f_counter_set(counter)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_to_hal_model() {
+ assert_eq!(
+ to_hal_model(crate::securechip::Model::ATECC_ATECC608A),
+ Model::Atecc608A,
+ );
+ assert_eq!(
+ to_hal_model(crate::securechip::Model::ATECC_ATECC608B),
+ Model::Atecc608B,
+ );
+ assert_eq!(
+ to_hal_model(crate::securechip::Model::OPTIGA_TRUST_M_V3),
+ Model::OptigaTrustM3,
+ );
+ }
+
+ #[test]
+ fn test_to_hal_error_securechip() {
+ let cases = [
+ (
+ crate::securechip::SecureChipError::SC_ERR_IFS,
+ SecureChipError::Ifs,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ERR_INVALID_ARGS,
+ SecureChipError::InvalidArgs,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ERR_CONFIG_MISMATCH,
+ SecureChipError::ConfigMismatch,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ERR_SALT,
+ SecureChipError::Salt,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ERR_INCORRECT_PASSWORD,
+ SecureChipError::IncorrectPassword,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ERR_INVALID_PASSWORD_STRETCH_ALGO,
+ SecureChipError::InvalidPasswordStretchAlgo,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ERR_MEMORY,
+ SecureChipError::Memory,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ATECC_ERR_ZONE_UNLOCKED_CONFIG,
+ SecureChipError::AteccZoneUnlockedConfig,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ATECC_ERR_ZONE_UNLOCKED_DATA,
+ SecureChipError::AteccZoneUnlockedData,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_IO,
+ SecureChipError::AteccSlotUnlockedIo,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_AUTH,
+ SecureChipError::AteccSlotUnlockedAuth,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ATECC_ERR_SLOT_UNLOCKED_ENC,
+ SecureChipError::AteccSlotUnlockedEnc,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_ATECC_ERR_RESET_KEYS,
+ SecureChipError::AteccResetKeys,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_OPTIGA_ERR_CREATE,
+ SecureChipError::OptigaCreate,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_OPTIGA_ERR_UNEXPECTED_METADATA,
+ SecureChipError::OptigaUnexpectedMetadata,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_OPTIGA_ERR_PAL,
+ SecureChipError::OptigaPal,
+ ),
+ (
+ crate::securechip::SecureChipError::SC_OPTIGA_ERR_UNEXPECTED_LEN,
+ SecureChipError::OptigaUnexpectedLen,
+ ),
+ ];
+ for (input, expected) in cases {
+ assert_eq!(
+ to_hal_error(crate::securechip::Error::SecureChip(input)),
+ Error::SecureChip(expected),
+ );
+ }
+ }
+
+ #[test]
+ fn test_to_hal_error_status() {
+ assert_eq!(
+ to_hal_error(crate::securechip::Error::Status(7)),
+ Error::Status(7)
+ );
+ }
+}
diff --git a/src/rust/bitbox02/src/hal/system.rs b/src/rust/bitbox02/src/hal/system.rs
new file mode 100644
index 0000000..4f53c3b
--- /dev/null
+++ b/src/rust/bitbox02/src/hal/system.rs
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use bitbox_hal::System;
+
+pub struct BitBox02System;
+
+impl System for BitBox02System {
+ fn reboot_to_bootloader(&mut self) -> ! {
+ crate::reboot_to_bootloader()
+ }
+}
diff --git a/src/rust/bitbox02/src/hal/ui.rs b/src/rust/bitbox02/src/hal/ui.rs
new file mode 100644
index 0000000..80392bd
--- /dev/null
+++ b/src/rust/bitbox02/src/hal/ui.rs
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use alloc::string::String;
+
+use bitbox_hal::Ui;
+use bitbox_hal::ui::{CanCancel, ConfirmParams, EnterStringParams, Font, TrinaryChoice, UserAbort};
+
+pub struct BitBox02Ui;
+
+fn to_bitbox02_font(font: Font) -> crate::ui::Font {
+ match font {
+ Font::Default => crate::ui::Font::Default,
+ Font::Password11X12 => crate::ui::Font::Password11X12,
+ Font::Monogram5X9 => crate::ui::Font::Monogram5X9,
+ }
+}
+
+fn to_bitbox02_confirm_params<'a>(params: &'a ConfirmParams<'a>) -> crate::ui::ConfirmParams<'a> {
+ crate::ui::ConfirmParams {
+ title: params.title,
+ title_autowrap: params.title_autowrap,
+ body: params.body,
+ font: to_bitbox02_font(params.font),
+ scrollable: params.scrollable,
+ longtouch: params.longtouch,
+ accept_only: params.accept_only,
+ accept_is_nextarrow: params.accept_is_nextarrow,
+ display_size: params.display_size,
+ }
+}
+
+fn to_bitbox02_trinary_input_string_params<'a>(
+ params: &'a EnterStringParams<'a>,
+) -> crate::ui::TrinaryInputStringParams<'a> {
+ crate::ui::TrinaryInputStringParams {
+ title: params.title,
+ wordlist: params.wordlist,
+ number_input: params.number_input,
+ hide: params.hide,
+ special_chars: params.special_chars,
+ longtouch: params.longtouch,
+ cancel_is_backbutton: params.cancel_is_backbutton,
+ default_to_digits: params.default_to_digits,
+ }
+}
+
+fn to_hal_trinary_choice(choice: crate::ui::TrinaryChoice) -> TrinaryChoice {
+ match choice {
+ crate::ui::TrinaryChoice::TRINARY_CHOICE_LEFT => TrinaryChoice::Left,
+ crate::ui::TrinaryChoice::TRINARY_CHOICE_MIDDLE => TrinaryChoice::Middle,
+ crate::ui::TrinaryChoice::TRINARY_CHOICE_RIGHT => TrinaryChoice::Right,
+ }
+}
+
+impl Ui for BitBox02Ui {
+ #[inline(always)]
+ async fn confirm(&mut self, params: &ConfirmParams<'_>) -> Result<(), UserAbort> {
+ let params = to_bitbox02_confirm_params(params);
+ match crate::ui::confirm(¶ms).await {
+ crate::ui::ConfirmResponse::Approved => Ok(()),
+ crate::ui::ConfirmResponse::Cancelled => Err(UserAbort),
+ }
+ }
+
+ #[inline(always)]
+ async fn verify_recipient(&mut self, recipient: &str, amount: &str) -> Result<(), UserAbort> {
+ match crate::ui::confirm_transaction_address(amount, recipient).await {
+ crate::ui::ConfirmResponse::Approved => Ok(()),
+ crate::ui::ConfirmResponse::Cancelled => Err(UserAbort),
+ }
+ }
+
+ #[inline(always)]
+ async fn verify_total_fee(
+ &mut self,
+ total: &str,
+ fee: &str,
+ longtouch: bool,
+ ) -> Result<(), UserAbort> {
+ match crate::ui::confirm_transaction_fee(total, fee, longtouch).await {
+ crate::ui::ConfirmResponse::Approved => Ok(()),
+ crate::ui::ConfirmResponse::Cancelled => Err(UserAbort),
+ }
+ }
+
+ #[inline(always)]
+ async fn status(&mut self, title: &str, status_success: bool) {
+ crate::ui::status(title, status_success).await
+ }
+
+ #[inline(always)]
+ async fn enter_string(
+ &mut self,
+ params: &EnterStringParams<'_>,
+ can_cancel: CanCancel,
+ preset: &str,
+ ) -> Result<zeroize::Zeroizing<String>, UserAbort> {
+ let params = to_bitbox02_trinary_input_string_params(params);
+ let can_cancel = match can_cancel {
+ CanCancel::Yes => true,
+ CanCancel::No => false,
+ };
+ crate::ui::trinary_input_string(¶ms, can_cancel, preset)
+ .await
+ .map_err(|_| UserAbort)
+ }
+
+ #[inline(always)]
+ async fn insert_sdcard(&mut self) -> Result<(), UserAbort> {
+ match crate::ui::sdcard().await {
+ crate::ui::SdcardResponse::Inserted => Ok(()),
+ crate::ui::SdcardResponse::Cancelled => Err(UserAbort),
+ }
+ }
+
+ #[inline(always)]
+ async fn menu(&mut self, words: &[&str], title: Option<&str>) -> Result<u8, UserAbort> {
+ match crate::ui::menu(crate::ui::MenuParams {
+ words,
+ title,
+ select_word: true,
+ continue_on_last: false,
+ cancel_confirm_title: None,
+ })
+ .await
+ {
+ crate::ui::MenuResponse::SelectWord(choice_idx) => Ok(choice_idx),
+ crate::ui::MenuResponse::ContinueOnLast => panic!("unexpected continue-on-last"),
+ crate::ui::MenuResponse::Cancel => Err(UserAbort),
+ }
+ }
+
+ #[inline(always)]
+ async fn trinary_choice(
+ &mut self,
+ message: &str,
+ label_left: Option<&str>,
+ label_middle: Option<&str>,
+ label_right: Option<&str>,
+ ) -> TrinaryChoice {
+ to_hal_trinary_choice(
+ crate::ui::trinary_choice(message, label_left, label_middle, label_right).await,
+ )
+ }
+
+ async fn show_mnemonic(&mut self, words: &[&str]) -> Result<(), UserAbort> {
+ match crate::ui::menu(crate::ui::MenuParams {
+ words,
+ title: None,
+ select_word: false,
+ continue_on_last: true,
+ cancel_confirm_title: Some("Recovery\nwords"),
+ })
+ .await
+ {
+ crate::ui::MenuResponse::ContinueOnLast => Ok(()),
+ crate::ui::MenuResponse::SelectWord(_) => panic!("unexpected select-word"),
+ crate::ui::MenuResponse::Cancel => Err(UserAbort),
+ }
+ }
+
+ async fn quiz_mnemonic_word(&mut self, choices: &[&str], title: &str) -> Result<u8, UserAbort> {
+ match crate::ui::menu(crate::ui::MenuParams {
+ words: choices,
+ title: Some(title),
+ select_word: true,
+ continue_on_last: false,
+ cancel_confirm_title: Some("Recovery\nwords"),
+ })
+ .await
+ {
+ crate::ui::MenuResponse::SelectWord(choice_idx) => Ok(choice_idx),
+ crate::ui::MenuResponse::ContinueOnLast => panic!("unexpected continue-on-last"),
+ crate::ui::MenuResponse::Cancel => Err(UserAbort),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_to_bitbox02_font() {
+ let cases = [
+ (Font::Default, crate::ui::Font::Default),
+ (Font::Password11X12, crate::ui::Font::Password11X12),
+ (Font::Monogram5X9, crate::ui::Font::Monogram5X9),
+ ];
+ for (input, expected) in cases {
+ assert_eq!(to_bitbox02_font(input) as i32, expected as i32);
+ }
+ }
+
+ #[test]
+ fn test_to_bitbox02_confirm_params() {
+ let fonts = [
+ (Font::Default, crate::ui::Font::Default),
+ (Font::Password11X12, crate::ui::Font::Password11X12),
+ (Font::Monogram5X9, crate::ui::Font::Monogram5X9),
+ ];
+ for (font, expected_font) in fonts {
+ let input = ConfirmParams {
+ title: "title",
+ title_autowrap: true,
+ body: "body",
+ font,
+ scrollable: true,
+ longtouch: true,
+ accept_only: true,
+ accept_is_nextarrow: true,
+ display_size: 42,
+ };
+ let output = to_bitbox02_confirm_params(&input);
+ assert_eq!(output.title, "title");
+ assert!(output.title_autowrap);
+ assert_eq!(output.body, "body");
+ assert_eq!(output.font as i32, expected_font as i32);
+ assert!(output.scrollable);
+ assert!(output.longtouch);
+ assert!(output.accept_only);
+ assert!(output.accept_is_nextarrow);
+ assert_eq!(output.display_size, 42);
+ }
+ }
+
+ #[test]
+ fn test_to_bitbox02_trinary_input_string_params() {
+ let input_without_wordlist = EnterStringParams {
+ title: "Enter",
+ wordlist: None,
+ number_input: true,
+ hide: true,
+ special_chars: true,
+ longtouch: true,
+ cancel_is_backbutton: true,
+ default_to_digits: true,
+ };
+ let output_without_wordlist =
+ to_bitbox02_trinary_input_string_params(&input_without_wordlist);
+ assert_eq!(output_without_wordlist.title, "Enter");
+ assert!(output_without_wordlist.wordlist.is_none());
+ assert!(output_without_wordlist.number_input);
+ assert!(output_without_wordlist.hide);
+ assert!(output_without_wordlist.special_chars);
+ assert!(output_without_wordlist.longtouch);
+ assert!(output_without_wordlist.cancel_is_backbutton);
+ assert!(output_without_wordlist.default_to_digits);
+
+ let wordlist = [1u16, 2, 3];
+ let input_with_wordlist = EnterStringParams {
+ title: "Seed",
+ wordlist: Some(&wordlist),
+ number_input: false,
+ hide: false,
+ special_chars: false,
+ longtouch: false,
+ cancel_is_backbutton: false,
+ default_to_digits: false,
+ };
+ let output_with_wordlist = to_bitbox02_trinary_input_string_params(&input_with_wordlist);
+ assert_eq!(output_with_wordlist.title, "Seed");
+ assert_eq!(output_with_wordlist.wordlist.unwrap(), wordlist.as_slice());
+ assert!(!output_with_wordlist.number_input);
+ assert!(!output_with_wordlist.hide);
+ assert!(!output_with_wordlist.special_chars);
+ assert!(!output_with_wordlist.longtouch);
+ assert!(!output_with_wordlist.cancel_is_backbutton);
+ assert!(!output_with_wordlist.default_to_digits);
+ }
+
+ #[test]
+ fn test_to_hal_trinary_choice() {
+ let cases = [
+ (
+ crate::ui::TrinaryChoice::TRINARY_CHOICE_LEFT,
+ TrinaryChoice::Left,
+ ),
+ (
+ crate::ui::TrinaryChoice::TRINARY_CHOICE_MIDDLE,
+ TrinaryChoice::Middle,
+ ),
+ (
+ crate::ui::TrinaryChoice::TRINARY_CHOICE_RIGHT,
+ TrinaryChoice::Right,
+ ),
+ ];
+ for (input, expected) in cases {
+ assert!(to_hal_trinary_choice(input) == expected);
+ }
+ }
+}
diff --git a/src/rust/bitbox02/src/lib.rs b/src/rust/bitbox02/src/lib.rs
index 7b67270..b37ec3a 100644
--- a/src/rust/bitbox02/src/lib.rs
+++ b/src/rust/bitbox02/src/lib.rs
@@ -28,6 +28,7 @@ pub mod da14531_protocol;
pub mod delay;
#[cfg(feature = "simulator-graphical")]
pub mod event;
+pub mod hal;
pub mod hid_hww;
#[cfg(feature = "app-u2f")]
pub mod hid_u2f;
diff --git a/test/simulator-graphical-bb03/Cargo.lock b/test/simulator-graphical-bb03/Cargo.lock
index 8c2c4a5..15e8d9f 100644
--- a/test/simulator-graphical-bb03/Cargo.lock
+++ b/test/simulator-graphical-bb03/Cargo.lock
@@ -365,7 +365,10 @@ name = "bitbox02"
version = "0.1.0"
dependencies = [
"bip39",
+ "bitbox-hal",
"bitbox02-sys",
+ "futures-lite",
+ "grounded",
"util",
"zeroize",
]
diff --git a/test/simulator-graphical/Cargo.lock b/test/simulator-graphical/Cargo.lock
index c51abc2..54902ae 100644
--- a/test/simulator-graphical/Cargo.lock
+++ b/test/simulator-graphical/Cargo.lock
@@ -327,7 +327,10 @@ name = "bitbox02"
version = "0.1.0"
dependencies = [
"bip39",
+ "bitbox-hal",
"bitbox02-sys",
+ "futures-lite",
+ "grounded",
"util",
"zeroize",
]
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.