What changed, and why it matters
This commit is a code refactoring that introduces a new 'Memory' hardware abstraction layer (HAL) in the BitBox02 firmware. It moves direct memory access calls behind a trait interface so the code can be more easily tested with mock memory. There is no direct evidence in the commit of a security vulnerability being fixed; it appears to be a structural improvement that also adds unit tests for password entry behavior.
No immediate security action is required. Reviewers may want to verify that the new `Memory` trait and its testing implementation faithfully preserve the semantics of the underlying `bitbox02::memory` operations, especially around initialization and seed storage, and that the added tests adequately cover the refactored code paths.
Security signals we found
Refactoring of sensitive memory operations (encrypted seed, initialization, reset) behind a HAL trait
Addition of unit tests for password entry default-to-digits behavior based on securechip type
No explicit security fix, CVE, or vulnerability description in commit message or diff
Evidence from the diff
The change adds a Memory trait to src/rust/bitbox02-rust/src/hal.rs with methods wrapping bitbox02::memory operations (device name, seed birthdate, initialization flags, encrypted seed/HMAC, reset, platform, securechip type, mnemonic passphrase). It provides real (BitBox02Memory) and testing (TestingMemory) implementations and updates call sites throughout hww, keystore, reset, and workflow modules to use hal.memory() instead of the global bitbox02::memory functions. The diff also adds unit tests for password::enter and adjusts existing tests to use the mock memory. No security bug is described or visibly patched.
Changed components
src/rust/bitbox02-rust/src/hal.rssrc/rust/bitbox02-rust/src/hww.rssrc/rust/bitbox02-rust/src/hww/api.rssrc/rust/bitbox02-rust/src/hww/api/backup.rssrc/rust/bitbox02-rust/src/hww/api/bluetooth.rssrc/rust/bitbox02-rust/src/hww/api/change_password.rssrc/rust/bitbox02-rust/src/hww/api/device_info.rssrc/rust/bitbox02-rust/src/hww/api/reset.rssrc/rust/bitbox02-rust/src/hww/api/restore.rssrc/rust/bitbox02-rust/src/hww/api/set_device_name.rssrc/rust/bitbox02-rust/src/hww/api/set_mnemonic_passphrase_enabled.rssrc/rust/bitbox02-rust/src/hww/api/show_mnemonic.rssrc/rust/bitbox02-rust/src/keystore.rssrc/rust/bitbox02-rust/src/reset.rssrc/rust/bitbox02-rust/src/workflow/password.rssrc/rust/bitbox02-rust/src/workflow/unlock.rssrc/rust/bitbox02/src/memory.rstest/simulator-graphical/src/main.rsInspect captured patch +420 / −138
diff --git a/src/rust/bitbox02-rust/src/hal.rs b/src/rust/bitbox02-rust/src/hal.rs
index 7d74c7d..cb547d3 100644
--- a/src/rust/bitbox02-rust/src/hal.rs
+++ b/src/rust/bitbox02-rust/src/hal.rs
@@ -60,12 +60,30 @@ pub trait SecureChip {
fn u2f_counter_set(&mut self, counter: u32) -> Result<(), ()>;
}
+pub trait Memory {
+ fn get_securechip_type(&mut self) -> Result<bitbox02::memory::SecurechipType, ()>;
+ fn get_platform(&mut self) -> Result<bitbox02::memory::Platform, ()>;
+ fn get_device_name(&mut self) -> String;
+ fn set_device_name(&mut self, name: &str) -> Result<(), bitbox02::memory::Error>;
+ fn is_mnemonic_passphrase_enabled(&mut self) -> bool;
+ fn set_mnemonic_passphrase_enabled(&mut self, enabled: bool) -> Result<(), ()>;
+ fn set_seed_birthdate(&mut self, timestamp: u32) -> Result<(), ()>;
+ fn get_seed_birthdate(&mut self) -> u32;
+ fn is_seeded(&mut self) -> bool;
+ fn is_initialized(&mut self) -> bool;
+ fn set_initialized(&mut self) -> Result<(), ()>;
+ fn get_encrypted_seed_and_hmac(&mut self) -> Result<alloc::vec::Vec<u8>, ()>;
+ fn set_encrypted_seed_and_hmac(&mut self, data: &[u8]) -> Result<(), ()>;
+ fn reset_hww(&mut self) -> Result<(), ()>;
+}
+
/// Hardware abstraction layer for BitBox devices.
pub trait Hal {
fn ui(&mut self) -> &mut impl Ui;
fn sd(&mut self) -> &mut impl Sd;
fn random(&mut self) -> &mut impl Random;
fn securechip(&mut self) -> &mut impl SecureChip;
+ fn memory(&mut self) -> &mut impl Memory;
}
pub struct BitBox02Sd;
@@ -167,11 +185,72 @@ impl SecureChip for BitBox02SecureChip {
}
}
+pub struct BitBox02Memory;
+
+impl Memory for BitBox02Memory {
+ fn get_securechip_type(&mut self) -> Result<bitbox02::memory::SecurechipType, ()> {
+ bitbox02::memory::get_securechip_type()
+ }
+
+ fn get_platform(&mut self) -> Result<bitbox02::memory::Platform, ()> {
+ bitbox02::memory::get_platform()
+ }
+
+ fn get_device_name(&mut self) -> String {
+ bitbox02::memory::get_device_name()
+ }
+
+ fn set_device_name(&mut self, name: &str) -> Result<(), bitbox02::memory::Error> {
+ bitbox02::memory::set_device_name(name)
+ }
+
+ 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<alloc::vec::Vec<u8>, ()> {
+ bitbox02::memory::get_encrypted_seed_and_hmac()
+ }
+
+ fn set_encrypted_seed_and_hmac(&mut self, data: &[u8]) -> Result<(), ()> {
+ bitbox02::memory::set_encrypted_seed_and_hmac(data)
+ }
+
+ fn reset_hww(&mut self) -> Result<(), ()> {
+ bitbox02::memory::reset_hww()
+ }
+}
+
pub struct BitBox02Hal {
ui: RealWorkflows,
sd: BitBox02Sd,
random: BitBox02Random,
securechip: BitBox02SecureChip,
+ memory: BitBox02Memory,
}
impl BitBox02Hal {
@@ -181,6 +260,7 @@ impl BitBox02Hal {
sd: BitBox02Sd,
random: BitBox02Random,
securechip: BitBox02SecureChip,
+ memory: BitBox02Memory,
}
}
}
@@ -198,6 +278,9 @@ impl Hal for BitBox02Hal {
fn securechip(&mut self) -> &mut impl SecureChip {
&mut self.securechip
}
+ fn memory(&mut self) -> &mut impl Memory {
+ &mut self.memory
+ }
}
#[cfg(feature = "testing")]
@@ -209,6 +292,7 @@ pub mod testing {
use bitcoin::hashes::{Hash, sha256};
+ use bitbox02::memory::SecurechipType;
use hex_lit::hex;
pub struct TestingRandom {
@@ -310,6 +394,17 @@ pub mod testing {
u2f_counter: u32,
}
+ pub struct TestingMemory {
+ securechip_type: SecurechipType,
+ platform: bitbox02::memory::Platform,
+ initialized: bool,
+ is_seeded: bool,
+ mnemonic_passphrase_enabled: bool,
+ seed_birthdate: u32,
+ encrypted_seed_and_hmac: Option<Vec<u8>>,
+ device_name: Option<String>,
+ }
+
impl TestingSecureChip {
pub fn new() -> Self {
TestingSecureChip {
@@ -419,11 +514,111 @@ pub mod testing {
}
}
+ impl TestingMemory {
+ pub fn new() -> Self {
+ Self {
+ securechip_type: SecurechipType::Atecc,
+ platform: bitbox02::memory::Platform::BitBox02,
+ initialized: false,
+ is_seeded: false,
+ mnemonic_passphrase_enabled: false,
+ seed_birthdate: 0,
+ encrypted_seed_and_hmac: None,
+ device_name: None,
+ }
+ }
+
+ pub fn set_securechip_type(&mut self, securechip_type: SecurechipType) {
+ self.securechip_type = securechip_type;
+ }
+
+ pub fn set_platform(&mut self, platform: bitbox02::memory::Platform) {
+ self.platform = platform;
+ }
+ }
+
+ impl super::Memory for TestingMemory {
+ fn get_securechip_type(&mut self) -> Result<SecurechipType, ()> {
+ Ok(self.securechip_type)
+ }
+
+ fn get_platform(&mut self) -> Result<bitbox02::memory::Platform, ()> {
+ Ok(self.platform)
+ }
+
+ fn get_device_name(&mut self) -> String {
+ self.device_name
+ .clone()
+ .unwrap_or_else(|| "My BitBox".into())
+ }
+
+ fn set_device_name(&mut self, name: &str) -> Result<(), bitbox02::memory::Error> {
+ self.device_name = Some(name.into());
+ Ok(())
+ }
+
+ fn is_mnemonic_passphrase_enabled(&mut self) -> bool {
+ self.mnemonic_passphrase_enabled
+ }
+
+ fn set_mnemonic_passphrase_enabled(&mut self, enabled: bool) -> Result<(), ()> {
+ self.mnemonic_passphrase_enabled = enabled;
+ Ok(())
+ }
+
+ fn set_seed_birthdate(&mut self, timestamp: u32) -> Result<(), ()> {
+ self.seed_birthdate = timestamp;
+ Ok(())
+ }
+
+ fn get_seed_birthdate(&mut self) -> u32 {
+ self.seed_birthdate
+ }
+
+ fn is_seeded(&mut self) -> bool {
+ self.is_seeded
+ }
+
+ fn is_initialized(&mut self) -> bool {
+ self.initialized
+ }
+
+ fn set_initialized(&mut self) -> Result<(), ()> {
+ self.initialized = true;
+ Ok(())
+ }
+
+ fn get_encrypted_seed_and_hmac(&mut self) -> Result<alloc::vec::Vec<u8>, ()> {
+ self.encrypted_seed_and_hmac.clone().ok_or(())
+ }
+
+ fn set_encrypted_seed_and_hmac(&mut self, data: &[u8]) -> Result<(), ()> {
+ // 96 is the max space allocated in BitBox02's memory for this.
+ if data.len() > 96 {
+ return Err(());
+ }
+ self.encrypted_seed_and_hmac = Some(data.to_vec());
+ self.is_seeded = true;
+ Ok(())
+ }
+
+ fn reset_hww(&mut self) -> Result<(), ()> {
+ self.initialized = false;
+ self.is_seeded = false;
+ self.mnemonic_passphrase_enabled = false;
+ self.seed_birthdate = 0;
+ self.encrypted_seed_and_hmac = None;
+ self.device_name = None;
+ Ok(())
+ }
+ }
+
pub struct TestingHal<'a> {
pub ui: crate::workflow::testing::TestingWorkflows<'a>,
pub sd: TestingSd,
pub random: TestingRandom,
pub securechip: TestingSecureChip,
+ pub memory: TestingMemory,
}
impl TestingHal<'_> {
@@ -433,6 +628,7 @@ pub mod testing {
sd: TestingSd::new(),
random: TestingRandom::new(),
securechip: TestingSecureChip::new(),
+ memory: TestingMemory::new(),
}
}
}
@@ -450,6 +646,9 @@ pub mod testing {
fn securechip(&mut self) -> &mut impl super::SecureChip {
&mut self.securechip
}
+ fn memory(&mut self) -> &mut impl super::Memory {
+ &mut self.memory
+ }
}
#[cfg(test)]
diff --git a/src/rust/bitbox02-rust/src/hww.rs b/src/rust/bitbox02-rust/src/hww.rs
index b8fd8ab..1b404c5 100644
--- a/src/rust/bitbox02-rust/src/hww.rs
+++ b/src/rust/bitbox02-rust/src/hww.rs
@@ -15,6 +15,7 @@
pub mod api;
pub mod noise;
+use crate::hal::Memory;
use alloc::vec::Vec;
const OP_UNLOCK: u8 = b'u';
@@ -113,7 +114,7 @@ async fn _process_packet(hal: &mut impl crate::hal::Hal, usb_in: Vec<u8>) -> Vec
// Update the waiting screen from "See the BitBoxApp" to the logo, now that the host is
// connected. When the device is initialized, we delay this until the unlock call, otherwise
// there would be a flicker where the logo would be shown before the host invokes unlock.
- if !bitbox02::memory::is_initialized() || usb_in.as_slice() == [OP_UNLOCK] {
+ if !hal.memory().is_initialized() || usb_in.as_slice() == [OP_UNLOCK] {
bitbox02::ui::screen_process_waiting_switch_to_logo();
}
@@ -330,8 +331,8 @@ mod tests {
);
assert!(!crate::keystore::is_locked());
- assert!(bitbox02::memory::is_seeded());
- assert!(!bitbox02::memory::is_initialized());
+ assert!(mock_hal.memory.is_seeded());
+ assert!(!mock_hal.memory.is_initialized());
let reboot_request = crate::pb::Request {
request: Some(crate::pb::request::Request::Reboot(
@@ -392,9 +393,8 @@ mod tests {
)
.unwrap();
assert!(!crate::keystore::is_locked());
- assert!(!bitbox02::memory::is_initialized());
- let mut mock_hal = TestingHal::new();
- mock_hal.sd.inserted = Some(true);
+ assert!(!mock_hal.memory.is_initialized());
+ mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
make_request(
&mut mock_hal,
(crate::pb::Request {
@@ -423,7 +423,7 @@ mod tests {
}
]
);
- assert!(bitbox02::memory::is_initialized());
+ assert!(mock_hal.memory.is_initialized());
let reboot_request = crate::pb::Request {
request: Some(crate::pb::request::Request::Reboot(
@@ -435,7 +435,7 @@ mod tests {
// Can't reboot when initialized but locked.
crate::keystore::lock();
- let mut mock_hal = TestingHal::new();
+ mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
let response_encoded =
make_request(&mut mock_hal, &reboot_request.encode_to_vec()).unwrap();
let response = crate::pb::Response::decode(&response_encoded[..]).unwrap();
@@ -448,7 +448,7 @@ mod tests {
);
// Unlock.
- let mut mock_hal = TestingHal::new();
+ mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
mock_hal
.ui
.set_enter_string(Box::new(|_params| Ok("password".into())));
@@ -460,8 +460,8 @@ mod tests {
// Since in the previous request the msg was encrypted but not decrypted (query was
// rejected), the noise states are out of sync and we need to make a new channel.
+ mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
let mut make_request = init_noise();
- let mut mock_hal = TestingHal::new();
let reboot_called = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
make_request(&mut mock_hal, reboot_request.encode_to_vec().as_ref()).unwrap();
}));
@@ -493,10 +493,9 @@ mod tests {
crate::keystore::lock();
mock_memory();
- bitbox02::memory::set_device_name("test device name").unwrap();
-
let mut make_request = init_noise();
let mut mock_hal = TestingHal::new();
+ mock_hal.memory.set_device_name("test device name").unwrap();
mock_hal.sd.inserted = Some(true);
mock_hal
.ui
diff --git a/src/rust/bitbox02-rust/src/hww/api.rs b/src/rust/bitbox02-rust/src/hww/api.rs
index 4ae4ca3..95cc0fe 100644
--- a/src/rust/bitbox02-rust/src/hww/api.rs
+++ b/src/rust/bitbox02-rust/src/hww/api.rs
@@ -48,7 +48,7 @@ use pb::request::Request;
use pb::response::Response;
use prost::Message;
-use crate::hal::Sd;
+use crate::hal::{Memory, Sd};
/// Encodes a protobuf Response message.
pub fn encode(response: Response) -> Vec<u8> {
@@ -94,7 +94,7 @@ async fn process_api_btc(
}
/// Checks if the device is ready to accept/handle an api endpoint.
-fn can_call(request: &Request) -> bool {
+fn can_call(hal: &mut impl crate::hal::Hal, request: &Request) -> bool {
// We have four main states:
// Creating a wallet on an uninitialized device goes from Uninitialized to Seeded, and when the
// backup is created to `Initialized*`.
@@ -110,13 +110,13 @@ fn can_call(request: &Request) -> bool {
// InitializedAndUnlocked (seed backuped up on SD card, keystore unlocked).
InitializedAndUnlocked,
}
- let state: State = if bitbox02::memory::is_initialized() {
+ let state: State = if hal.memory().is_initialized() {
if crate::keystore::is_locked() {
State::InitializedAndLocked
} else {
State::InitializedAndUnlocked
}
- } else if bitbox02::memory::is_seeded() {
+ } else if hal.memory().is_seeded() {
State::Seeded
} else {
State::Uninitialized
@@ -227,7 +227,7 @@ pub async fn process(hal: &mut impl crate::hal::Hal, input: Vec<u8>) -> Vec<u8>
Ok(request) => request,
Err(err) => return encode(make_error(err)),
};
- if !can_call(&request) {
+ if !can_call(hal, &request) {
return encode(make_error(Error::InvalidState));
}
diff --git a/src/rust/bitbox02-rust/src/hww/api/backup.rs b/src/rust/bitbox02-rust/src/hww/api/backup.rs
index 1b1dda8..ad62f00 100644
--- a/src/rust/bitbox02-rust/src/hww/api/backup.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/backup.rs
@@ -19,7 +19,7 @@ use alloc::vec::Vec;
use pb::response::Response;
use crate::backup;
-use crate::hal::{Sd, Ui};
+use crate::hal::{Memory, Sd, Ui};
use crate::workflow::{confirm, unlock};
pub async fn check(
@@ -97,7 +97,7 @@ pub async fn create(
)
.await?;
- let is_initialized = bitbox02::memory::is_initialized();
+ let is_initialized = hal.memory().is_initialized();
let seed = if is_initialized {
unlock::unlock_keystore(hal, "Unlock device", unlock::CanCancel::Yes).await?
@@ -110,7 +110,7 @@ pub async fn create(
};
let seed_birthdate = if !is_initialized {
- if bitbox02::memory::set_seed_birthdate(timestamp).is_err() {
+ if hal.memory().set_seed_birthdate(timestamp).is_err() {
return Err(Error::Memory);
}
timestamp
@@ -121,22 +121,15 @@ pub async fn create(
} else {
0
};
- match backup::create(
- hal,
- &seed,
- &bitbox02::memory::get_device_name(),
- timestamp,
- seed_birthdate,
- )
- .await
- {
+ let device_name = hal.memory().get_device_name();
+ match backup::create(hal, &seed, &device_name, timestamp, seed_birthdate).await {
Ok(()) => {
// The backup was created, so reporting an error here
// could have bad consequences like replacing the sd card,
// not safely disposing of the old one. The issue fixes
// itself after replugging and going through the backup
// process again.
- let _ = bitbox02::memory::set_initialized();
+ let _ = hal.memory().set_initialized();
hal.ui().status("Backup created", true).await;
Ok(Response::Success(pb::Success {}))
@@ -199,7 +192,7 @@ mod tests {
Ok(Response::Success(pb::Success {}))
);
assert_eq!(mock_hal.securechip.get_event_counter(), 1);
- assert_eq!(EXPECTED_TIMESTMAP, bitbox02::memory::get_seed_birthdate());
+ assert_eq!(EXPECTED_TIMESTMAP, mock_hal.memory.get_seed_birthdate());
assert_eq!(
mock_hal.ui.screens,
vec![
@@ -233,14 +226,13 @@ mod tests {
mock_memory();
+ let mut mock_hal = TestingHal::new();
let seed = hex::decode("cb33c20cea62a5c277527e2002da82e6e2b37450a755143a540a54cea8da9044")
.unwrap();
- crate::keystore::encrypt_and_store_seed(&mut TestingHal::new(), &seed, "password").unwrap();
- bitbox02::memory::set_initialized().unwrap();
+ crate::keystore::encrypt_and_store_seed(&mut mock_hal, &seed, "password").unwrap();
+ mock_hal.memory.set_initialized().unwrap();
let mut password_entered: bool = false;
-
- let mut mock_hal = TestingHal::new();
mock_hal.sd.inserted = Some(true);
mock_hal.ui.set_enter_string(Box::new(|_params| {
password_entered = true;
@@ -370,7 +362,7 @@ mod tests {
"",
);
- bitbox02::memory::set_device_name(DEVICE_NAME_1).unwrap();
+ mock_hal.memory.set_device_name(DEVICE_NAME_1).unwrap();
assert!(
block_on(create(
&mut mock_hal,
@@ -394,12 +386,18 @@ mod tests {
);
// Create another backup.
+
+ // Mock a reset, otherwise we can't make another backup, as it would ask for a password
+ // because the above backup creation set the initialized flag.
+ mock_hal.memory.reset_hww().unwrap();
+
mock_memory();
mock_unlocked_using_mnemonic(
"goddess item rack improve shaft occur actress rib emerge salad rich blame model glare lounge stable electric height scrub scrub oyster now dinner oven",
"",
);
- bitbox02::memory::set_device_name(DEVICE_NAME_2).unwrap();
+
+ mock_hal.memory.set_device_name(DEVICE_NAME_2).unwrap();
assert!(
block_on(create(
&mut mock_hal,
diff --git a/src/rust/bitbox02-rust/src/hww/api/bluetooth.rs b/src/rust/bitbox02-rust/src/hww/api/bluetooth.rs
index cdaf058..1b93fa5 100644
--- a/src/rust/bitbox02-rust/src/hww/api/bluetooth.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/bluetooth.rs
@@ -22,7 +22,7 @@ use pb::bluetooth_response::Response;
use sha2::{Digest, Sha256};
-use crate::hal::Ui;
+use crate::hal::{Memory, Ui};
use crate::workflow::confirm;
use alloc::vec::Vec;
@@ -216,7 +216,7 @@ pub async fn process_api(
request: &Request,
) -> Result<Response, Error> {
if !matches!(
- memory::get_platform().map_err(|_| Error::Memory)?,
+ hal.memory().get_platform().map_err(|_| Error::Memory)?,
memory::Platform::BitBox02Plus
) {
return Err(Error::Disabled);
diff --git a/src/rust/bitbox02-rust/src/hww/api/change_password.rs b/src/rust/bitbox02-rust/src/hww/api/change_password.rs
index 8328425..30d18d2 100644
--- a/src/rust/bitbox02-rust/src/hww/api/change_password.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/change_password.rs
@@ -35,7 +35,7 @@ pub async fn process(hal: &mut impl crate::hal::Hal) -> Result<Response, Error>
mod tests {
use super::*;
- use crate::hal::testing::TestingHal;
+ use crate::hal::{Memory, testing::TestingHal};
use crate::workflow::{testing::Screen, unlock};
use alloc::boxed::Box;
use bitbox02::testing::mock_memory;
@@ -54,7 +54,7 @@ mod tests {
let mut hal = TestingHal::new();
keystore::encrypt_and_store_seed(&mut hal, &seed, old_password).unwrap();
block_on(unlock::unlock_bip39(&mut hal, &seed));
- bitbox02::memory::set_initialized().unwrap();
+ hal.memory.set_initialized().unwrap();
// Allow exactly 3 prompts
let mut prompt_counter = 0u32;
@@ -99,29 +99,27 @@ mod tests {
]
);
- let securechip_events = hal.securechip.get_event_counter();
- drop(hal);
// We expect 14 secure chip events. This is intentionally brittle to catch
// unintended changes in the number of securechip operations during password change.
// If this fails after a legitimate change, update the expected count.
- assert_eq!(securechip_events, 14);
- assert_eq!(prompt_counter, 3);
+ assert_eq!(hal.securechip.get_event_counter(), 14);
// check that the old password is no longer valid
keystore::lock();
- // create new hal instance to call unlock
- let mut hal_verify = TestingHal::new();
assert!(matches!(
- block_on(keystore::unlock(&mut hal_verify, old_password)),
+ block_on(keystore::unlock(&mut hal, old_password)),
Err(keystore::Error::IncorrectPassword)
));
// check that the new password is valid
assert_eq!(
- block_on(keystore::unlock(&mut hal_verify, new_password))
+ block_on(keystore::unlock(&mut hal, new_password))
.unwrap()
.as_slice(),
seed.as_slice()
);
+
+ drop(hal);
+ assert_eq!(prompt_counter, 3);
}
// Test that we fail if the unlock fails
@@ -135,7 +133,7 @@ mod tests {
let mut hal = TestingHal::new();
keystore::encrypt_and_store_seed(&mut hal, &seed, correct_password).unwrap();
block_on(unlock::unlock_bip39(&mut hal, &seed));
- bitbox02::memory::set_initialized().unwrap();
+ hal.memory.set_initialized().unwrap();
keystore::lock();
let mut prompt_counter = 0u32;
@@ -166,16 +164,16 @@ mod tests {
// We expect 5 secure chip events (sensitive to code changes)
assert_eq!(hal.securechip.get_event_counter(), 5);
- drop(hal);
- assert_eq!(prompt_counter, 1);
// check that the old password is still valid
- let mut hal_verify = TestingHal::new();
assert_eq!(
- block_on(keystore::unlock(&mut hal_verify, correct_password))
+ block_on(keystore::unlock(&mut hal, correct_password))
.unwrap()
.as_slice(),
seed.as_slice()
);
+
+ drop(hal);
+ assert_eq!(prompt_counter, 1);
}
// Test that we fail if the confirm password mismatch
@@ -191,7 +189,7 @@ mod tests {
let mut hal = TestingHal::new();
keystore::encrypt_and_store_seed(&mut hal, &seed, old_password).unwrap();
block_on(unlock::unlock_bip39(&mut hal, &seed));
- bitbox02::memory::set_initialized().unwrap();
+ hal.memory.set_initialized().unwrap();
keystore::lock();
let mut prompt_counter = 0u32;
@@ -214,16 +212,15 @@ mod tests {
}
}));
let result = block_on(process(&mut hal));
- drop(hal);
assert_eq!(result, Err(Error::Generic));
// check that the old password is still valid
- let mut hal_verify = TestingHal::new();
assert_eq!(
- block_on(keystore::unlock(&mut hal_verify, old_password))
+ block_on(keystore::unlock(&mut hal, old_password))
.unwrap()
.as_slice(),
seed.as_slice()
);
+ drop(hal);
}
}
diff --git a/src/rust/bitbox02-rust/src/hww/api/device_info.rs b/src/rust/bitbox02-rust/src/hww/api/device_info.rs
index 7cd03fa..ab68ffd 100644
--- a/src/rust/bitbox02-rust/src/hww/api/device_info.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/device_info.rs
@@ -13,15 +13,14 @@
// limitations under the License.
use super::Error;
-use crate::hal::SecureChip;
+use crate::hal::{Memory, SecureChip};
use crate::pb;
-use pb::response::Response;
-
use bitbox02::{memory, securechip, spi_mem};
+use pb::response::Response;
pub fn process(hal: &mut impl crate::hal::Hal) -> Result<Response, Error> {
- let bluetooth = match memory::get_platform().map_err(|_| Error::Memory)? {
+ let bluetooth = match hal.memory().get_platform().map_err(|_| Error::Memory)? {
memory::Platform::BitBox02Plus => {
let ble_metadata = memory::get_ble_metadata();
Some(pb::device_info_response::Bluetooth {
@@ -34,10 +33,10 @@ pub fn process(hal: &mut impl crate::hal::Hal) -> Result<Response, Error> {
memory::Platform::BitBox02 => None,
};
Ok(Response::DeviceInfo(pb::DeviceInfoResponse {
- name: memory::get_device_name(),
- initialized: memory::is_initialized(),
+ name: hal.memory().get_device_name(),
+ initialized: hal.memory().is_initialized(),
version: crate::version::FIRMWARE_VERSION_SHORT.into(),
- mnemonic_passphrase_enabled: memory::is_mnemonic_passphrase_enabled(),
+ mnemonic_passphrase_enabled: hal.memory().is_mnemonic_passphrase_enabled(),
monotonic_increments_remaining: hal.securechip().monotonic_increments_remaining()?,
securechip_model: match hal.securechip().model()? {
securechip::Model::ATECC_ATECC608A => "ATECC608A".into(),
diff --git a/src/rust/bitbox02-rust/src/hww/api/reset.rs b/src/rust/bitbox02-rust/src/hww/api/reset.rs
index a2db511..a7ffcce 100644
--- a/src/rust/bitbox02-rust/src/hww/api/reset.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/reset.rs
@@ -38,7 +38,7 @@ pub async fn process(hal: &mut impl crate::hal::Hal) -> Result<Response, Error>
mod tests {
use super::*;
- use crate::hal::testing::TestingHal;
+ use crate::hal::{Memory, testing::TestingHal};
use crate::workflow::testing::Screen;
use alloc::boxed::Box;
use bitbox02::testing::mock_memory;
@@ -47,10 +47,10 @@ mod tests {
#[test]
pub fn test_reset() {
mock_memory();
- bitbox02::memory::set_device_name("test device name").unwrap();
// User aborted confirmation.
let mut mock_hal = TestingHal::new();
+ mock_hal.memory.set_device_name("test device name").unwrap();
mock_hal.ui.abort_nth(0);
assert_eq!(block_on(process(&mut mock_hal)), Err(Error::Generic));
assert_eq!(
@@ -62,12 +62,13 @@ mod tests {
}],
);
assert_eq!(
- bitbox02::memory::get_device_name().as_str(),
+ mock_hal.memory.get_device_name().as_str(),
"test device name",
);
// All good.
let mut mock_hal = TestingHal::new();
+ mock_hal.memory.set_device_name("test device name").unwrap();
assert_eq!(
block_on(process(&mut mock_hal)),
Ok(Response::Success(pb::Success {}))
@@ -86,6 +87,6 @@ mod tests {
}
],
);
- assert_eq!(bitbox02::memory::get_device_name().as_str(), "My BitBox");
+ assert_eq!(mock_hal.memory.get_device_name().as_str(), "My BitBox");
}
}
diff --git a/src/rust/bitbox02-rust/src/hww/api/restore.rs b/src/rust/bitbox02-rust/src/hww/api/restore.rs
index f723bf8..959e0e8 100644
--- a/src/rust/bitbox02-rust/src/hww/api/restore.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/restore.rs
@@ -19,8 +19,7 @@ use pb::response::Response;
#[cfg(feature = "app-u2f")]
use crate::hal::SecureChip;
-use crate::hal::Ui;
-
+use crate::hal::{Memory, Ui};
use crate::workflow::{confirm, mnemonic, password, unlock};
pub async fn from_file(
@@ -77,7 +76,7 @@ pub async fn from_file(
}
// Ignore error here. Missing birthdate should not abort an otherwise successful restore.
- let _ = bitbox02::memory::set_seed_birthdate(data.0.birthdate);
+ let _ = hal.memory().set_seed_birthdate(data.0.birthdate);
#[cfg(feature = "app-u2f")]
{
@@ -86,10 +85,10 @@ pub async fn from_file(
let _ = hal.securechip().u2f_counter_set(request.timestamp);
}
- bitbox02::memory::set_initialized().or(Err(Error::Memory))?;
+ hal.memory().set_initialized().or(Err(Error::Memory))?;
// Ignore non-critical error.
- let _ = bitbox02::memory::set_device_name(&metadata.name);
+ let _ = hal.memory().set_device_name(&metadata.name);
unlock::unlock_bip39(hal, seed).await;
Ok(Response::Success(pb::Success {}))
@@ -159,7 +158,7 @@ pub async fn from_mnemonic(
let _ = hal.securechip().u2f_counter_set(timestamp);
}
- bitbox02::memory::set_initialized().or(Err(Error::Memory))?;
+ hal.memory().set_initialized().or(Err(Error::Memory))?;
unlock::unlock_bip39(hal, &seed).await;
Ok(Response::Success(pb::Success {}))
@@ -206,7 +205,7 @@ mod tests {
assert_eq!(mock_hal.securechip.get_event_counter(), 8);
assert!(!crate::keystore::is_locked());
- assert!(memory::is_initialized());
+ assert!(mock_hal.memory.is_initialized());
// Seed of hardcoded phrase used in unit tests:
// boring mistake dish oyster truth pigeon viable emerge sort crash wire portion cannon couple enact box walk height pull today solid off enable tide
assert_eq!(
diff --git a/src/rust/bitbox02-rust/src/hww/api/set_device_name.rs b/src/rust/bitbox02-rust/src/hww/api/set_device_name.rs
index e4965c5..5ce2d36 100644
--- a/src/rust/bitbox02-rust/src/hww/api/set_device_name.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/set_device_name.rs
@@ -17,7 +17,7 @@ use crate::pb;
use pb::response::Response;
-use crate::hal::Ui;
+use crate::hal::{Memory, Ui};
use crate::workflow::confirm;
pub async fn process(
@@ -37,7 +37,7 @@ pub async fn process(
hal.ui().confirm(¶ms).await?;
- bitbox02::memory::set_device_name(name)?;
+ hal.memory().set_device_name(name)?;
Ok(Response::Success(pb::Success {}))
}
@@ -76,7 +76,7 @@ mod tests {
longtouch: false,
}]
);
- assert_eq!(SOME_NAME, &bitbox02::memory::get_device_name());
+ assert_eq!(SOME_NAME, mock_hal.memory.get_device_name());
// User aborted confirmation.
let mut mock_hal = TestingHal::new();
diff --git a/src/rust/bitbox02-rust/src/hww/api/set_mnemonic_passphrase_enabled.rs b/src/rust/bitbox02-rust/src/hww/api/set_mnemonic_passphrase_enabled.rs
index 97b2392..541b10e 100644
--- a/src/rust/bitbox02-rust/src/hww/api/set_mnemonic_passphrase_enabled.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/set_mnemonic_passphrase_enabled.rs
@@ -17,7 +17,7 @@ use crate::pb;
use pb::response::Response;
-use crate::hal::Ui;
+use crate::hal::{Memory, Ui};
use crate::workflow::confirm;
pub async fn process(
@@ -33,7 +33,11 @@ pub async fn process(
hal.ui().confirm(¶ms).await?;
- if bitbox02::memory::set_mnemonic_passphrase_enabled(enabled).is_err() {
+ if hal
+ .memory()
+ .set_mnemonic_passphrase_enabled(enabled)
+ .is_err()
+ {
return Err(Error::Memory);
}
@@ -72,9 +76,9 @@ mod tests {
}],
);
- assert!(bitbox02::memory::is_mnemonic_passphrase_enabled());
+ assert!(mock_hal.memory.is_mnemonic_passphrase_enabled());
// Disable:
- let mut mock_hal = TestingHal::new();
+ mock_hal.ui.screens.clear();
assert_eq!(
block_on(process(
&mut mock_hal,
@@ -90,7 +94,7 @@ mod tests {
longtouch: true,
}],
);
- assert!(!bitbox02::memory::is_mnemonic_passphrase_enabled());
+ assert!(!mock_hal.memory.is_mnemonic_passphrase_enabled());
// User aborted confirmation.
let mut mock_hal = TestingHal::new();
diff --git a/src/rust/bitbox02-rust/src/hww/api/show_mnemonic.rs b/src/rust/bitbox02-rust/src/hww/api/show_mnemonic.rs
index 58f1ac5..19e6d24 100644
--- a/src/rust/bitbox02-rust/src/hww/api/show_mnemonic.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/show_mnemonic.rs
@@ -19,7 +19,7 @@ use crate::pb;
use pb::response::Response;
-use crate::hal::Ui;
+use crate::hal::{Memory, Ui};
use crate::workflow::{confirm, unlock};
/// Handle the ShowMnemonic API call. This shows the seed encoded as
@@ -28,7 +28,7 @@ use crate::workflow::{confirm, unlock};
/// wrote it down correctly.
pub async fn process(hal: &mut impl crate::hal::Hal) -> Result<Response, Error> {
let mnemonic_sentence = {
- let seed = if bitbox02::memory::is_initialized() {
+ let seed = if hal.memory().is_initialized() {
unlock::unlock_keystore(hal, "Unlock device", unlock::CanCancel::Yes).await?
} else {
crate::keystore::copy_seed(hal)?
@@ -59,7 +59,7 @@ pub async fn process(hal: &mut impl crate::hal::Hal) -> Result<Response, Error>
hal.ui().show_and_confirm_mnemonic(&words).await?;
- bitbox02::memory::set_initialized().or(Err(Error::Memory))?;
+ hal.memory().set_initialized().or(Err(Error::Memory))?;
hal.ui().status("Backup created", true).await;
Ok(Response::Success(pb::Success {}))
@@ -81,8 +81,9 @@ mod tests {
#[test]
fn test_process_uninitialized() {
mock_memory();
+ let mut mock_hal = TestingHal::new();
crate::keystore::encrypt_and_store_seed(
- &mut TestingHal::new(),
+ &mut mock_hal,
hex::decode("c7940c13479b8d9a6498f4e50d5a42e0d617bc8e8ac9f2b8cecf97e94c2b035c")
.unwrap()
.as_slice(),
@@ -90,9 +91,7 @@ mod tests {
)
.unwrap();
- assert!(!bitbox02::memory::is_initialized());
-
- let mut mock_hal = TestingHal::new();
+ assert!(!mock_hal.memory.is_initialized());
mock_hal.ui.set_enter_string(Box::new(|_params| {
panic!("unexpected call to enter password")
}));
@@ -132,8 +131,9 @@ mod tests {
#[test]
fn test_process_initialized() {
mock_memory();
+ let mut mock_hal = TestingHal::new();
crate::keystore::encrypt_and_store_seed(
- &mut TestingHal::new(),
+ &mut mock_hal,
hex::decode("c7940c13479b8d9a6498f4e50d5a42e0d617bc8e8ac9f2b8cecf97e94c2b035c")
.unwrap()
.as_slice(),
@@ -141,11 +141,10 @@ mod tests {
)
.unwrap();
- bitbox02::memory::set_initialized().unwrap();
+ mock_hal.memory.set_initialized().unwrap();
let mut password_entered: bool = false;
- let mut mock_hal = TestingHal::new();
mock_hal.ui.set_enter_string(Box::new(|_params| {
password_entered = true;
Ok("password".into())
@@ -190,8 +189,11 @@ mod tests {
#[test]
fn test_process_initialized_wrong_password() {
mock_memory();
+
+ let mut mock_hal = TestingHal::new();
+
crate::keystore::encrypt_and_store_seed(
- &mut TestingHal::new(),
+ &mut mock_hal,
hex::decode("c7940c13479b8d9a6498f4e50d5a42e0d617bc8e8ac9f2b8cecf97e94c2b035c")
.unwrap()
.as_slice(),
@@ -199,9 +201,7 @@ mod tests {
)
.unwrap();
- bitbox02::memory::set_initialized().unwrap();
-
- let mut mock_hal = TestingHal::new();
+ mock_hal.memory.set_initialized().unwrap();
mock_hal
.ui
.set_enter_string(Box::new(|_params| Ok("wrong password".into())));
diff --git a/src/rust/bitbox02-rust/src/keystore.rs b/src/rust/bitbox02-rust/src/keystore.rs
index 6b59680..a845962 100644
--- a/src/rust/bitbox02-rust/src/keystore.rs
+++ b/src/rust/bitbox02-rust/src/keystore.rs
@@ -19,7 +19,7 @@ use alloc::string::String;
use alloc::vec::Vec;
use crate::bip32;
-use crate::hal::{Random, SecureChip};
+use crate::hal::{Memory, Random, SecureChip};
use bitbox02::keystore;
pub use bitbox02::keystore::SignResult;
@@ -167,12 +167,16 @@ pub fn is_locked() -> bool {
!unlocked
}
-fn verify_seed(encryption_key: &[u8], expected_seed: &[u8]) -> bool {
+fn verify_seed(
+ hal: &mut impl crate::hal::Hal,
+ encryption_key: &[u8],
+ expected_seed: &[u8],
+) -> bool {
if encryption_key.len() != 32 {
return false;
}
- let cipher = match bitbox02::memory::get_encrypted_seed_and_hmac() {
+ let cipher = match hal.memory().get_encrypted_seed_and_hmac() {
Ok(cipher) => cipher,
Err(_) => return false,
};
@@ -240,10 +244,12 @@ fn encrypt_and_store_seed_internal(
panic!("encrypted seed length overflow");
}
- bitbox02::memory::set_encrypted_seed_and_hmac(&encrypted).map_err(|_| Error::Memory)?;
+ hal.memory()
+ .set_encrypted_seed_and_hmac(&encrypted)
+ .map_err(|_| Error::Memory)?;
- if !verify_seed(&secret, seed) {
- bitbox02::memory::reset_hww().map_err(|_| Error::Memory)?;
+ if !verify_seed(hal, &secret, seed) {
+ hal.memory().reset_hww().map_err(|_| Error::Memory)?;
return Err(Error::Memory);
}
@@ -257,7 +263,7 @@ pub fn encrypt_and_store_seed(
seed: &[u8],
password: &str,
) -> Result<(), Error> {
- if bitbox02::memory::is_initialized() {
+ if hal.memory().is_initialized() {
return Err(Error::Memory);
}
encrypt_and_store_seed_internal(hal, seed, password)
@@ -269,7 +275,7 @@ pub fn re_encrypt_seed(
seed: &[u8],
new_password: &str,
) -> Result<(), Error> {
- if !bitbox02::memory::is_seeded() {
+ if !hal.memory().is_seeded() {
return Err(Error::Unseeded);
}
@@ -304,7 +310,10 @@ fn get_and_decrypt_seed(
hal: &mut impl crate::hal::Hal,
password: &str,
) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
- let encrypted = bitbox02::memory::get_encrypted_seed_and_hmac().map_err(|_| Error::Memory)?;
+ let encrypted = hal
+ .memory()
+ .get_encrypted_seed_and_hmac()
+ .map_err(|_| Error::Memory)?;
// Our Optiga securechip implementation fails password stretching if the password is
// wrong, so it already returns an error here. The ATECC stretches the password without checking
// if the password is correct, and we determine if it is correct in the seed decryption
@@ -325,7 +334,7 @@ pub async fn unlock(
hal: &mut impl crate::hal::Hal,
password: &str,
) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
- if !bitbox02::memory::is_seeded() {
+ if !hal.memory().is_seeded() {
return Err(Error::Unseeded);
}
if get_remaining_unlock_attempts() == 0 {
@@ -903,7 +912,7 @@ mod tests {
);
// Check the seed has been stored encrypted with the expected encryption key.
// Decrypt and check seed.
- let cipher = bitbox02::memory::get_encrypted_seed_and_hmac().unwrap();
+ let cipher = hal.memory.get_encrypted_seed_and_hmac().unwrap();
// Same as Python:
// import hmac, hashlib; hmac.digest(b"unit-test", b"password", hashlib.sha256).hex()
@@ -1174,7 +1183,7 @@ mod tests {
bitbox02::memory::MAX_UNLOCK_ATTEMPTS - i
);
// Still seeded.
- assert!(bitbox02::memory::is_seeded());
+ assert!(mock_hal.memory.is_seeded());
// Wrong password does not lock the keystore again if already unlocked.
assert!(copy_seed(&mut mock_hal).is_ok());
}
@@ -1184,7 +1193,7 @@ mod tests {
Err(Error::MaxAttemptsExceeded),
));
// Last wrong attempt locks & resets. There is no more seed.
- assert!(!bitbox02::memory::is_seeded());
+ assert!(!mock_hal.memory.is_seeded());
assert!(copy_seed(&mut mock_hal).is_err());
assert!(matches!(
block_on(unlock(&mut mock_hal, "password")),
@@ -1221,7 +1230,7 @@ mod tests {
);
assert!(is_locked());
assert!(copy_seed(&mut mock_hal).is_err());
- assert!(bitbox02::memory::is_seeded());
+ assert!(mock_hal.memory.is_seeded());
}
assert!(matches!(
@@ -1230,7 +1239,7 @@ mod tests {
));
assert!(is_locked());
assert!(copy_seed(&mut mock_hal).is_err());
- assert!(!bitbox02::memory::is_seeded());
+ assert!(!mock_hal.memory.is_seeded());
assert!(matches!(
block_on(unlock(&mut mock_hal, "password")),
Err(Error::Unseeded)
@@ -1269,7 +1278,7 @@ mod tests {
));
assert!(is_locked());
assert!(copy_seed(&mut mock_hal).is_err());
- assert!(!bitbox02::memory::is_seeded());
+ assert!(!mock_hal.memory.is_seeded());
}
/// Ensures the failed-attempt counter resets once a correct password is entered while the
@@ -1316,7 +1325,7 @@ mod tests {
wrong_attempt(&mut mock_hal);
assert!(copy_seed(&mut mock_hal).is_err());
- assert!(bitbox02::memory::is_seeded());
+ assert!(mock_hal.memory.is_seeded());
}
/// Ensures the failed-attempt counter resets when the keystore stays unlocked throughout, so
@@ -1368,7 +1377,7 @@ mod tests {
wrong_attempt(&mut mock_hal);
assert!(copy_seed(&mut mock_hal).is_ok());
- assert!(bitbox02::memory::is_seeded());
+ assert!(mock_hal.memory.is_seeded());
}
#[test]
@@ -2058,7 +2067,7 @@ mod tests {
);
// Can't store new seed once initialized.
- bitbox02::memory::set_initialized().unwrap();
+ mock_hal.memory.set_initialized().unwrap();
assert!(matches!(
encrypt_and_store_seed(&mut mock_hal, &seed[..seed_size], "foo"),
Err(Error::Memory)
diff --git a/src/rust/bitbox02-rust/src/reset.rs b/src/rust/bitbox02-rust/src/reset.rs
index 03ca91e..8a1e04f 100644
--- a/src/rust/bitbox02-rust/src/reset.rs
+++ b/src/rust/bitbox02-rust/src/reset.rs
@@ -13,7 +13,7 @@
// limitations under the License.
use crate::general::abort;
-use crate::hal::{SecureChip, Ui};
+use crate::hal::{Memory, SecureChip, Ui};
/// Resets the device:
/// - Updates secure chip KDF keys.
@@ -58,7 +58,7 @@ pub(crate) async fn reset(hal: &mut impl crate::hal::Hal, status: bool) {
}
}
- if bitbox02::memory::reset_hww().is_err() {
+ if hal.memory().reset_hww().is_err() {
abort("Could not reset memory.");
}
@@ -70,7 +70,7 @@ pub(crate) async fn reset(hal: &mut impl crate::hal::Hal, status: bool) {
// The ble chip needs to be restarted to load the new secrets.
if matches!(
- bitbox02::memory::get_platform(),
+ hal.memory().get_platform(),
Ok(bitbox02::memory::Platform::BitBox02Plus)
) {
bitbox02::reset_ble();
@@ -95,13 +95,14 @@ mod tests {
fn test_reset_success() {
mock_memory();
+ let mut hal = TestingHal::new();
+
keystore::lock();
mock_unlocked();
- bitbox02::memory::set_device_name("Custom name").unwrap();
+ hal.memory.set_device_name("Custom name").unwrap();
assert!(!keystore::is_locked());
assert!(bitbox02::smarteeprom::is_enabled());
- let mut hal = TestingHal::new();
// Make the reset keys call fail once, to test that it is retried.
hal.securechip.mock_reset_keys_fails();
@@ -118,7 +119,7 @@ mod tests {
assert!(keystore::is_locked());
// Memory has been reset to factory defaults.
- assert_eq!(bitbox02::memory::get_device_name().as_str(), "My BitBox");
+ assert_eq!(hal.memory.get_device_name().as_str(), "My BitBox");
// SmartEEPROM was disabled as part of the reset.
assert!(!bitbox02::smarteeprom::is_enabled());
diff --git a/src/rust/bitbox02-rust/src/workflow/password.rs b/src/rust/bitbox02-rust/src/workflow/password.rs
index e84abab..96b349e 100644
--- a/src/rust/bitbox02-rust/src/workflow/password.rs
+++ b/src/rust/bitbox02-rust/src/workflow/password.rs
@@ -14,6 +14,9 @@
use super::{Workflows, confirm, trinary_input_string};
+use crate::hal::Memory;
+use bitbox02::memory::SecurechipType;
+
pub use trinary_input_string::{CanCancel, Error};
use alloc::string::String;
@@ -64,9 +67,13 @@ pub async fn enter(
longtouch: true,
default_to_digits: match password_type {
PasswordType::DevicePassword => {
- match bitbox02::memory::get_securechip_type().map_err(|_| EnterError::Memory)? {
- bitbox02::memory::SecurechipType::Atecc => false,
- bitbox02::memory::SecurechipType::Optiga => true,
+ match hal
+ .memory()
+ .get_securechip_type()
+ .map_err(|_| EnterError::Memory)?
+ {
+ SecurechipType::Atecc => false,
+ SecurechipType::Optiga => true,
}
}
PasswordType::Bip39Passphrase => false,
@@ -148,3 +155,73 @@ pub async fn enter_twice(
hal.ui().status("Success", true).await;
Ok(password)
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::hal::testing::TestingHal;
+ use alloc::boxed::Box;
+ use util::bb02_async::block_on;
+
+ #[test]
+ fn test_enter_default_to_digits_atecc() {
+ let mut hal = TestingHal::new();
+ hal.memory.set_securechip_type(SecurechipType::Atecc);
+ hal.ui.set_enter_string(Box::new(|params| {
+ assert!(!params.default_to_digits);
+ Ok("pw".into())
+ }));
+
+ let password = block_on(enter(
+ &mut hal,
+ "Enter password",
+ PasswordType::DevicePassword,
+ CanCancel::No,
+ ))
+ .unwrap();
+
+ assert_eq!(password.as_str(), "pw");
+ }
+
+ #[test]
+ fn test_enter_default_to_digits_optiga() {
+ let mut hal = TestingHal::new();
+ hal.memory.set_securechip_type(SecurechipType::Optiga);
+ hal.ui.set_enter_string(Box::new(|params| {
+ assert!(params.default_to_digits);
+ Ok("pw".into())
+ }));
+
+ let password = block_on(enter(
+ &mut hal,
+ "Enter password",
+ PasswordType::DevicePassword,
+ CanCancel::No,
+ ))
+ .unwrap();
+
+ assert_eq!(password.as_str(), "pw");
+ }
+
+ #[test]
+ fn test_enter_cancelled() {
+ let mut hal = TestingHal::new();
+ hal.memory.set_securechip_type(SecurechipType::Atecc);
+ hal.ui.set_enter_string(Box::new(|_params| {
+ Err(trinary_input_string::Error::Cancelled)
+ }));
+
+ let result = block_on(enter(
+ &mut hal,
+ "Enter password",
+ PasswordType::DevicePassword,
+ CanCancel::Yes,
+ ));
+
+ assert!(matches!(result, Err(EnterError::Cancelled)));
+ assert!(
+ hal.ui
+ .contains_confirm("", "Do you really\nwant to cancel?")
+ );
+ }
+}
diff --git a/src/rust/bitbox02-rust/src/workflow/unlock.rs b/src/rust/bitbox02-rust/src/workflow/unlock.rs
index 8b3aeeb..98c698c 100644
--- a/src/rust/bitbox02-rust/src/workflow/unlock.rs
+++ b/src/rust/bitbox02-rust/src/workflow/unlock.rs
@@ -13,7 +13,7 @@
// limitations under the License.
use crate::general::abort;
-use crate::hal::Ui;
+use crate::hal::{Memory, Ui};
use crate::workflow::{confirm, password};
pub use password::CanCancel;
@@ -114,7 +114,7 @@ pub async fn unlock_bip39(hal: &mut impl crate::hal::Hal, seed: &[u8]) {
let mut mnemonic_passphrase = zeroize::Zeroizing::new("".into());
// If setting activated, get the passphrase from the user.
- if bitbox02::memory::is_mnemonic_passphrase_enabled() {
+ if hal.memory().is_mnemonic_passphrase_enabled() {
// Loop until the user confirms.
loop {
mnemonic_passphrase = password::enter(
@@ -164,7 +164,7 @@ pub async fn unlock_bip39(hal: &mut impl crate::hal::Hal, seed: &[u8]) {
///
/// Returns Ok on success, Err if the device cannot be unlocked because it was not initialized.
pub async fn unlock(hal: &mut impl crate::hal::Hal) -> Result<(), ()> {
- if !bitbox02::memory::is_initialized() {
+ if !hal.memory().is_initialized() {
return Err(());
}
if !crate::keystore::is_locked() {
@@ -207,7 +207,7 @@ mod tests {
)
.unwrap();
- bitbox02::memory::set_initialized().unwrap();
+ mock_hal.memory.set_initialized().unwrap();
// Lock the keystore to simulate the normal locked state
crate::keystore::lock();
@@ -251,7 +251,7 @@ mod tests {
)
.unwrap();
- bitbox02::memory::set_initialized().unwrap();
+ mock_hal.memory.set_initialized().unwrap();
// Lock the keystore to simulate the normal locked state
crate::keystore::lock();
diff --git a/src/rust/bitbox02/src/memory.rs b/src/rust/bitbox02/src/memory.rs
index 1090de1..e29f0e5 100644
--- a/src/rust/bitbox02/src/memory.rs
+++ b/src/rust/bitbox02/src/memory.rs
@@ -229,6 +229,7 @@ pub fn multisig_get_by_hash(hash: &[u8]) -> Option<String> {
}
}
+#[derive(Clone, Copy)]
pub enum Platform {
BitBox02,
BitBox02Plus,
@@ -242,6 +243,7 @@ pub fn get_platform() -> Result<Platform, ()> {
}
}
+#[derive(Clone, Copy)]
pub enum SecurechipType {
Atecc,
Optiga,
diff --git a/test/simulator-graphical/src/main.rs b/test/simulator-graphical/src/main.rs
index 6f0bd5e..777c5d5 100644
--- a/test/simulator-graphical/src/main.rs
+++ b/test/simulator-graphical/src/main.rs
@@ -50,6 +50,7 @@ use tracing::{debug, error, info};
use tracing_subscriber::{EnvFilter, filter::LevelFilter, fmt, prelude::*};
use bitbox02::ui::ugui::UG_COLOR;
+use bitbox02_rust::hal::{Hal, Memory};
static BG: &[u8; 325362] = include_bytes!("../bg.png");
@@ -168,13 +169,9 @@ fn init_hww(preseed: bool) -> bool {
if preseed {
let mnemonic = "boring mistake dish oyster truth pigeon viable emerge sort crash wire portion cannon couple enact box walk height pull today solid off enable tide";
let seed = bitbox02_rust::bip39::mnemonic_to_seed(&mnemonic).unwrap();
- bitbox02_rust::keystore::encrypt_and_store_seed(
- &mut bitbox02_rust::hal::BitBox02Hal::new(),
- &seed,
- "",
- )
- .unwrap();
- bitbox02::memory::set_initialized().unwrap();
+ let mut hal = bitbox02_rust::hal::BitBox02Hal::new();
+ bitbox02_rust::keystore::encrypt_and_store_seed(&mut hal, &seed, "").unwrap();
+ hal.memory().set_initialized().unwrap();
}
bitbox02::smarteeprom::bb02_config();
Why this scored 18/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.