Skip to content

Commit 4c3f4c2

Browse files
committed
Merge branch 'mock-sd'
2 parents 95a3b48 + eac6984 commit 4c3f4c2

File tree

8 files changed

+185
-77
lines changed

8 files changed

+185
-77
lines changed

src/rust/bitbox02-rust/src/backup.rs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use prost::Message;
1616

1717
use crate::pb_backup;
1818

19+
use crate::hal::Sd;
20+
1921
use alloc::boxed::Box;
2022
use alloc::string::String;
2123
use alloc::vec::Vec;
@@ -156,15 +158,18 @@ fn bitwise_recovery(buf1: &[u8], buf2: &[u8], buf3: &[u8]) -> Result<Zeroizing<V
156158
Ok(recovered_contents)
157159
}
158160

159-
pub fn load(dir: &str) -> Result<(Zeroizing<BackupData>, pb_backup::BackupMetaData), ()> {
160-
let files = bitbox02::sd::list_subdir(Some(dir))?;
161+
pub fn load(
162+
hal: &mut impl crate::hal::Hal,
163+
dir: &str,
164+
) -> Result<(Zeroizing<BackupData>, pb_backup::BackupMetaData), ()> {
165+
let files = hal.sd().list_subdir(Some(dir))?;
161166
if files.len() != 3 {
162167
return Err(());
163168
}
164169
let file_contents: [Zeroizing<Vec<u8>>; 3] = [
165-
bitbox02::sd::load_bin(&files[0], dir)?,
166-
bitbox02::sd::load_bin(&files[1], dir)?,
167-
bitbox02::sd::load_bin(&files[2], dir)?,
170+
hal.sd().load_bin(&files[0], dir)?,
171+
hal.sd().load_bin(&files[1], dir)?,
172+
hal.sd().load_bin(&files[2], dir)?,
168173
];
169174
for contents in file_contents.iter() {
170175
if let o @ Ok(_) = load_from_buffer(contents) {
@@ -182,6 +187,7 @@ pub fn load(dir: &str) -> Result<(Zeroizing<BackupData>, pb_backup::BackupMetaDa
182187
}
183188

184189
pub fn create(
190+
hal: &mut impl crate::hal::Hal,
185191
seed: &[u8],
186192
name: &str,
187193
backup_create_timestamp: u32,
@@ -226,7 +232,7 @@ pub fn create(
226232
};
227233
let backup_encoded = backup.encode_to_vec();
228234
let dir = id(seed);
229-
let files = bitbox02::sd::list_subdir(Some(&dir)).or(Err(Error::SdList))?;
235+
let files = hal.sd().list_subdir(Some(&dir)).or(Err(Error::SdList))?;
230236

231237
let filename_datetime = {
232238
let tm = bitbox02::get_datetime(backup_create_timestamp).map_err(|_| Error::Generic)?;
@@ -247,8 +253,12 @@ pub fn create(
247253
if files.contains(&filename) {
248254
return Err(Error::Generic);
249255
}
250-
bitbox02::sd::write_bin(&filename, &dir, &backup_encoded).or(Err(Error::SdWrite))?;
251-
if bitbox02::sd::load_bin(&filename, &dir)
256+
hal.sd()
257+
.write_bin(&filename, &dir, &backup_encoded)
258+
.or(Err(Error::SdWrite))?;
259+
if hal
260+
.sd()
261+
.load_bin(&filename, &dir)
252262
.or(Err(Error::SdRead))?
253263
.as_slice()
254264
!= backup_encoded.as_slice()
@@ -258,7 +268,7 @@ pub fn create(
258268
}
259269
let mut stale = false;
260270
for file in files {
261-
if bitbox02::sd::erase_file_in_subdir(&file, &dir).is_err() {
271+
if hal.sd().erase_file_in_subdir(&file, &dir).is_err() {
262272
stale = true
263273
}
264274
}
@@ -272,10 +282,9 @@ pub fn create(
272282
mod tests {
273283
use super::*;
274284

285+
use crate::hal::testing::TestingHal;
275286
use core::convert::TryInto;
276287

277-
use bitbox02::testing::mock_sd;
278-
279288
#[test]
280289
fn test_id() {
281290
// Seeds of different lengths (16, 24, 32 bytes)
@@ -295,14 +304,14 @@ mod tests {
295304
}
296305

297306
fn _test_create_load(seed: &[u8]) {
298-
mock_sd();
307+
let mut mock_hal = TestingHal::new();
299308
let timestamp = 1601281809;
300309
let birthdate = timestamp - 32400;
301-
assert!(create(seed, "test name", timestamp, birthdate).is_ok());
310+
assert!(create(&mut mock_hal, seed, "test name", timestamp, birthdate).is_ok());
302311
let dir = id(seed);
303-
assert_eq!(bitbox02::sd::list_subdir(None), Ok(vec![dir.clone()]));
312+
assert_eq!(mock_hal.sd.list_subdir(None), Ok(vec![dir.clone()]));
304313
assert_eq!(
305-
bitbox02::sd::list_subdir(Some(&dir)),
314+
mock_hal.sd.list_subdir(Some(&dir)),
306315
Ok(vec![
307316
"backup_Mon_2020-09-28T08-30-09Z_0.bin".into(),
308317
"backup_Mon_2020-09-28T08-30-09Z_1.bin".into(),
@@ -311,20 +320,22 @@ mod tests {
311320
);
312321

313322
// Recreating using same timestamp is not allowed and doesn't change the backups.
314-
assert!(create(seed, "new name", timestamp, birthdate).is_err());
323+
assert!(create(&mut mock_hal, seed, "new name", timestamp, birthdate).is_err());
315324
assert_eq!(
316-
bitbox02::sd::list_subdir(Some(&dir)),
325+
mock_hal.sd.list_subdir(Some(&dir)),
317326
Ok(vec![
318327
"backup_Mon_2020-09-28T08-30-09Z_0.bin".into(),
319328
"backup_Mon_2020-09-28T08-30-09Z_1.bin".into(),
320329
"backup_Mon_2020-09-28T08-30-09Z_2.bin".into()
321330
])
322331
);
323332

324-
let contents: [zeroize::Zeroizing<Vec<u8>>; 3] = bitbox02::sd::list_subdir(Some(&dir))
333+
let contents: [zeroize::Zeroizing<Vec<u8>>; 3] = mock_hal
334+
.sd
335+
.list_subdir(Some(&dir))
325336
.unwrap()
326337
.iter()
327-
.map(|file| bitbox02::sd::load_bin(file, &dir).unwrap())
338+
.map(|file| mock_hal.sd.load_bin(file, &dir).unwrap())
328339
.collect::<Vec<_>>()
329340
.try_into()
330341
.unwrap();
@@ -334,17 +345,17 @@ mod tests {
334345
);
335346

336347
// Recreating the backup removes the previous files.
337-
assert!(create(seed, "new name", timestamp + 1, birthdate).is_ok());
348+
assert!(create(&mut mock_hal, seed, "new name", timestamp + 1, birthdate).is_ok());
338349
assert_eq!(
339-
bitbox02::sd::list_subdir(Some(&dir)),
350+
mock_hal.sd.list_subdir(Some(&dir)),
340351
Ok(vec![
341352
"backup_Mon_2020-09-28T08-30-10Z_0.bin".into(),
342353
"backup_Mon_2020-09-28T08-30-10Z_1.bin".into(),
343354
"backup_Mon_2020-09-28T08-30-10Z_2.bin".into()
344355
])
345356
);
346357

347-
let (backup_data, metadata) = load(&dir).unwrap();
358+
let (backup_data, metadata) = load(&mut mock_hal, &dir).unwrap();
348359
assert_eq!(backup_data.get_seed(), seed);
349360
assert_eq!(backup_data.0.birthdate, birthdate);
350361
assert_eq!(metadata.name.as_str(), "new name");

src/rust/bitbox02-rust/src/hal.rs

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,15 @@
1515
use crate::workflow::RealWorkflows;
1616
pub use crate::workflow::Workflows as Ui;
1717

18+
use alloc::string::String;
19+
use alloc::vec::Vec;
20+
1821
pub trait Sd {
1922
fn sdcard_inserted(&mut self) -> bool;
23+
fn list_subdir(&mut self, subdir: Option<&str>) -> Result<Vec<String>, ()>;
24+
fn erase_file_in_subdir(&mut self, filename: &str, dir: &str) -> Result<(), ()>;
25+
fn load_bin(&mut self, filename: &str, dir: &str) -> Result<zeroize::Zeroizing<Vec<u8>>, ()>;
26+
fn write_bin(&mut self, filename: &str, dir: &str, data: &[u8]) -> Result<(), ()>;
2027
}
2128

2229
/// Hardware abstraction layer for BitBox devices.
@@ -28,9 +35,30 @@ pub trait Hal {
2835
pub struct BitBox02Sd;
2936

3037
impl Sd for BitBox02Sd {
38+
#[inline(always)]
3139
fn sdcard_inserted(&mut self) -> bool {
3240
bitbox02::sd::sdcard_inserted()
3341
}
42+
43+
#[inline(always)]
44+
fn list_subdir(&mut self, subdir: Option<&str>) -> Result<Vec<String>, ()> {
45+
bitbox02::sd::list_subdir(subdir)
46+
}
47+
48+
#[inline(always)]
49+
fn erase_file_in_subdir(&mut self, filename: &str, dir: &str) -> Result<(), ()> {
50+
bitbox02::sd::erase_file_in_subdir(filename, dir)
51+
}
52+
53+
#[inline(always)]
54+
fn load_bin(&mut self, filename: &str, dir: &str) -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
55+
bitbox02::sd::load_bin(filename, dir)
56+
}
57+
58+
#[inline(always)]
59+
fn write_bin(&mut self, filename: &str, dir: &str, data: &[u8]) -> Result<(), ()> {
60+
bitbox02::sd::write_bin(filename, dir, data)
61+
}
3462
}
3563

3664
pub struct BitBox02Hal {
@@ -58,24 +86,71 @@ impl Hal for BitBox02Hal {
5886

5987
#[cfg(feature = "testing")]
6088
pub mod testing {
89+
use alloc::collections::BTreeMap;
90+
use alloc::string::String;
91+
use alloc::vec::Vec;
92+
6193
pub struct TestingSd {
6294
pub inserted: Option<bool>,
95+
files: BTreeMap<String, BTreeMap<String, Vec<u8>>>,
6396
}
6497

6598
impl TestingSd {
6699
pub fn new() -> Self {
67-
Self { inserted: None }
100+
Self {
101+
inserted: None,
102+
files: BTreeMap::new(),
103+
}
68104
}
69105
}
70-
pub struct TestingHal<'a> {
71-
pub ui: crate::workflow::testing::TestingWorkflows<'a>,
72-
pub sd: TestingSd,
73-
}
74106

75107
impl super::Sd for TestingSd {
76108
fn sdcard_inserted(&mut self) -> bool {
77109
self.inserted.unwrap()
78110
}
111+
112+
fn list_subdir(&mut self, subdir: Option<&str>) -> Result<Vec<String>, ()> {
113+
match subdir {
114+
Some(key) => Ok(self
115+
.files
116+
.get(key)
117+
.map(|files| files.keys().cloned().collect())
118+
.unwrap_or_default()),
119+
None => Ok(self.files.keys().cloned().collect()),
120+
}
121+
}
122+
123+
fn erase_file_in_subdir(&mut self, filename: &str, dir: &str) -> Result<(), ()> {
124+
self.files
125+
.get_mut(dir)
126+
.and_then(|files| files.remove(filename).map(|_| ()))
127+
.ok_or(())
128+
}
129+
130+
fn load_bin(
131+
&mut self,
132+
filename: &str,
133+
dir: &str,
134+
) -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
135+
self.files
136+
.get(dir)
137+
.and_then(|files| files.get(filename))
138+
.map(|data| zeroize::Zeroizing::new(data.clone()))
139+
.ok_or(())
140+
}
141+
142+
fn write_bin(&mut self, filename: &str, dir: &str, data: &[u8]) -> Result<(), ()> {
143+
self.files
144+
.entry(dir.into())
145+
.or_default()
146+
.insert(filename.into(), data.to_vec());
147+
Ok(())
148+
}
149+
}
150+
151+
pub struct TestingHal<'a> {
152+
pub ui: crate::workflow::testing::TestingWorkflows<'a>,
153+
pub sd: TestingSd,
79154
}
80155

81156
impl TestingHal<'_> {
@@ -95,4 +170,35 @@ pub mod testing {
95170
&mut self.sd
96171
}
97172
}
173+
174+
#[cfg(test)]
175+
mod tests {
176+
use super::*;
177+
use crate::hal::Sd;
178+
179+
// Quick check if our mock TestingSd implementation makes sense.
180+
#[test]
181+
fn test_sd_list_write_read_erase() {
182+
let mut sd = TestingSd::new();
183+
assert_eq!(sd.list_subdir(None), Ok(vec![]));
184+
assert_eq!(sd.list_subdir(Some("dir1")), Ok(vec![]));
185+
186+
assert!(sd.load_bin("file1.txt", "dir1").is_err());
187+
assert!(sd.write_bin("file1.txt", "dir1", b"data").is_ok());
188+
assert_eq!(sd.list_subdir(None), Ok(vec!["dir1".into()]));
189+
assert_eq!(sd.list_subdir(Some("dir1")), Ok(vec!["file1.txt".into()]));
190+
assert_eq!(
191+
sd.load_bin("file1.txt", "dir1").unwrap().as_slice(),
192+
b"data"
193+
);
194+
assert!(sd.write_bin("file1.txt", "dir1", b"replaced data").is_ok());
195+
assert_eq!(
196+
sd.load_bin("file1.txt", "dir1").unwrap().as_slice(),
197+
b"replaced data"
198+
);
199+
assert!(sd.erase_file_in_subdir("doesnt-exist.txt", "dir1").is_err());
200+
assert!(sd.erase_file_in_subdir("file1.txt", "dir1").is_ok());
201+
assert_eq!(sd.list_subdir(Some("dir1")), Ok(vec![]));
202+
}
203+
}
98204
}

src/rust/bitbox02-rust/src/hww.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ mod tests {
139139
use crate::bb02_async::block_on;
140140
use crate::hal::testing::TestingHal;
141141
use crate::workflow::testing::Screen;
142-
use bitbox02::testing::{mock_memory, mock_sd};
142+
use bitbox02::testing::mock_memory;
143143

144144
use prost::Message;
145145

@@ -236,7 +236,6 @@ mod tests {
236236
#[test]
237237
fn test_noise() {
238238
mock_memory();
239-
mock_sd();
240239
let mut make_request = init_noise();
241240
let request = crate::pb::Request {
242241
request: Some(crate::pb::request::Request::ListBackups(
@@ -477,7 +476,6 @@ mod tests {
477476
] {
478477
bitbox02::keystore::lock();
479478
mock_memory();
480-
mock_sd();
481479

482480
bitbox02::memory::set_device_name("test device name").unwrap();
483481

@@ -508,8 +506,7 @@ mod tests {
508506
}]
509507
);
510508

511-
let mut mock_hal = TestingHal::new();
512-
mock_hal.sd.inserted = Some(true);
509+
mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
513510
make_request(
514511
&mut mock_hal,
515512
(crate::pb::Request {
@@ -541,7 +538,7 @@ mod tests {
541538

542539
let seed = bitbox02::keystore::copy_seed().unwrap();
543540
assert_eq!(seed.len(), host_entropy.len());
544-
let mut mock_hal = TestingHal::new();
541+
mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
545542
assert!(matches!(
546543
crate::pb::Response::decode(
547544
make_request(
@@ -566,7 +563,7 @@ mod tests {
566563
));
567564
assert_eq!(mock_hal.ui.screens, vec![]);
568565

569-
let mut mock_hal = TestingHal::new();
566+
mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
570567
make_request(
571568
&mut mock_hal,
572569
(crate::pb::Request {
@@ -587,7 +584,7 @@ mod tests {
587584
}]
588585
);
589586

590-
let mut mock_hal = TestingHal::new();
587+
mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
591588
assert!(matches!(
592589
crate::pb::Response::decode(
593590
make_request(
@@ -612,7 +609,7 @@ mod tests {
612609
));
613610
assert_eq!(mock_hal.ui.screens, vec![]);
614611

615-
let mut mock_hal = TestingHal::new();
612+
mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
616613
let backup_id = match crate::pb::Response::decode(
617614
make_request(
618615
&mut mock_hal,
@@ -647,7 +644,7 @@ mod tests {
647644
};
648645
assert_eq!(mock_hal.ui.screens, vec![]);
649646

650-
let mut mock_hal = TestingHal::new();
647+
mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
651648
mock_hal
652649
.ui
653650
.set_enter_string(Box::new(|_params| Ok("password".into())));

0 commit comments

Comments
 (0)