Skip to content

Commit 8555ef8

Browse files
committed
Merge branch 'rust-keystore-functional-tests'
2 parents 87c62d1 + 704623e commit 8555ef8

File tree

4 files changed

+129
-243
lines changed

4 files changed

+129
-243
lines changed

src/rust/bitbox02-sys/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ const ALLOWLIST_FNS: &[&str] = &[
7373
"keystore_encrypt_and_store_seed",
7474
"keystore_get_bip39_word",
7575
"keystore_get_ed25519_seed",
76+
"keystore_get_u2f_seed",
7677
"keystore_is_locked",
7778
"keystore_lock",
7879
"keystore_mock_unlocked",

src/rust/bitbox02/src/keystore.rs

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,16 @@ pub fn get_ed25519_seed() -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
319319
}
320320
}
321321

322+
// Currently only used in the functional tests below.
323+
#[cfg(feature = "testing")]
324+
pub fn get_u2f_seed() -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
325+
let mut seed = zeroize::Zeroizing::new([0u8; 32].to_vec());
326+
match unsafe { bitbox02_sys::keystore_get_u2f_seed(seed.as_mut_ptr()) } {
327+
true => Ok(seed),
328+
false => Err(()),
329+
}
330+
}
331+
322332
pub fn secp256k1_schnorr_sign(
323333
keypath: &[u32],
324334
msg: &[u8; 32],
@@ -346,7 +356,8 @@ pub fn secp256k1_schnorr_sign(
346356
#[cfg(test)]
347357
mod tests {
348358
use super::*;
349-
use crate::testing::{mock_unlocked, mock_unlocked_using_mnemonic};
359+
use crate::testing::{mock_memory, mock_unlocked, mock_unlocked_using_mnemonic};
360+
use alloc::string::ToString;
350361
use util::bip32::HARDENED;
351362

352363
#[test]
@@ -479,4 +490,120 @@ mod tests {
479490
// Invalid seed side
480491
assert!(bip39_mnemonic_from_seed(b"foo").is_err());
481492
}
493+
494+
// Functional test to store seeds, unlock, retrieve seed.
495+
#[test]
496+
fn test_seeds() {
497+
let seed = hex::decode("cb33c20cea62a5c277527e2002da82e6e2b37450a755143a540a54cea8da9044")
498+
.unwrap();
499+
500+
for seed_size in [16, 24, 32] {
501+
mock_memory();
502+
lock();
503+
504+
// Can repeat until initialized - initialized means backup has been created.
505+
for _ in 0..2 {
506+
assert!(encrypt_and_store_seed(&seed[..seed_size], "foo").is_ok());
507+
}
508+
509+
// Wrong password.
510+
assert!(matches!(
511+
unlock("bar"),
512+
Err(Error::IncorrectPassword {
513+
remaining_attempts: 9
514+
})
515+
));
516+
517+
// Can't get seed before unlock.
518+
assert!(copy_seed().is_err());
519+
// Correct password. First time: unlock. After unlock, it becomes a password check.
520+
for _ in 0..3 {
521+
assert!(unlock("foo").is_ok());
522+
}
523+
assert_eq!(copy_seed().unwrap().as_slice(), &seed[..seed_size]);
524+
525+
// Can't store new seed once initialized.
526+
crate::memory::set_initialized().unwrap();
527+
assert!(matches!(
528+
encrypt_and_store_seed(&seed[..seed_size], "foo"),
529+
Err(Error::Memory)
530+
));
531+
}
532+
}
533+
534+
#[test]
535+
fn test_fixtures() {
536+
struct Test {
537+
seed_len: usize,
538+
mnemonic_passphrase: &'static str,
539+
expected_mnemonic: &'static str,
540+
expected_xpub: &'static str,
541+
expected_u2f_seed_hex: &'static str,
542+
}
543+
let seed = hex::decode("cb33c20cea62a5c277527e2002da82e6e2b37450a755143a540a54cea8da9044")
544+
.unwrap();
545+
546+
let tests = [
547+
Test {
548+
seed_len: 32,
549+
mnemonic_passphrase: "",
550+
expected_mnemonic: "sleep own lobster state clean thrive tail exist cactus bitter pass soccer clinic riot dream turkey before sport action praise tunnel hood donate man",
551+
expected_xpub: "xpub6Cj6NNCGj2CRPHvkuEG1rbW3nrNCAnLjaoTg1P67FCGoahSsbg9WQ7YaMEEP83QDxt2kZ3hTPAPpGdyEZcfAC1C75HfR66UbjpAb39f4PnG",
552+
expected_u2f_seed_hex: "4f464a6667ad88eebcd0f02982761e474ee0dd16253160320f49d1d6681745e9",
553+
},
554+
Test {
555+
seed_len: 32,
556+
mnemonic_passphrase: "abc",
557+
expected_mnemonic: "sleep own lobster state clean thrive tail exist cactus bitter pass soccer clinic riot dream turkey before sport action praise tunnel hood donate man",
558+
expected_xpub: "xpub6DXBP3HhFdhUTafatEULxfTXUUxDVuCxfa9RAiBU5r6aRgKiABbeBDyqwWWjmKPP1BZvpvVNMbVR5LeHzhQphtLcPZ8jk3MdLBgc2sACJwR",
559+
expected_u2f_seed_hex: "d599da991ad83baaf449c789e2dff1539dd66983b47a1dec1c00ff3f352cccbc",
560+
},
561+
Test {
562+
seed_len: 24,
563+
mnemonic_passphrase: "",
564+
expected_mnemonic: "sleep own lobster state clean thrive tail exist cactus bitter pass soccer clinic riot dream turkey before subject",
565+
expected_xpub: "xpub6C7fKxGtTzEVxCC22U2VHx4GpaVy77DzU6KdZ1CLuHgoUGviBMWDc62uoQVxqcRa5RQbMPnffjpwxve18BG81VJhJDXnSpRe5NGKwVpXiAb",
566+
expected_u2f_seed_hex: "fb9dc3fb0a17390776df5c3d8f9261bc5fd5df9f00414cee1393e37e0efda7ef",
567+
},
568+
Test {
569+
seed_len: 16,
570+
mnemonic_passphrase: "",
571+
expected_mnemonic: "sleep own lobster state clean thrive tail exist cactus bitter pass sniff",
572+
expected_xpub: "xpub6DLvpzjKpJ8k4xYrWYPmZQkUe9dkG1eRig2v6Jz4iYgo8hcpHWx87gGoCGDaB2cHFZ3ExUfe1jDiMu7Ch6gA4ULCBhvwZj29mHCPYSux3YV",
573+
expected_u2f_seed_hex: "20d68b206aff9667b623a460ce61fc94762de67561d6855ca9a6df7b409b2a54",
574+
},
575+
];
576+
577+
for test in tests {
578+
mock_memory();
579+
lock();
580+
let seed = &seed[..test.seed_len];
581+
assert!(unlock_bip39(test.mnemonic_passphrase).is_err());
582+
assert!(encrypt_and_store_seed(seed, "foo").is_ok());
583+
assert!(unlock_bip39(test.mnemonic_passphrase).is_err());
584+
assert!(is_locked());
585+
assert!(unlock("foo").is_ok());
586+
assert!(is_locked());
587+
assert!(unlock_bip39(test.mnemonic_passphrase).is_ok());
588+
assert!(!is_locked());
589+
assert_eq!(
590+
bip39_mnemonic_from_seed(&copy_seed().unwrap())
591+
.unwrap()
592+
.as_str(),
593+
test.expected_mnemonic,
594+
);
595+
let keypath = &[44 + HARDENED, 0 + HARDENED, 0 + HARDENED];
596+
let encoded_xpub = encode_xpub_at_keypath(keypath).unwrap();
597+
assert_eq!(
598+
bitcoin::bip32::Xpub::decode(&encoded_xpub)
599+
.unwrap()
600+
.to_string(),
601+
test.expected_xpub,
602+
);
603+
assert_eq!(
604+
hex::encode(get_u2f_seed().unwrap()),
605+
test.expected_u2f_seed_hex,
606+
);
607+
}
608+
}
482609
}

test/unit-test/CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,6 @@ set(TEST_LIST
232232
"-Wl,--wrap=secp256k1_anti_exfil_sign,--wrap=memory_is_initialized,--wrap=memory_is_seeded,--wrap=memory_get_failed_unlock_attempts,--wrap=memory_reset_failed_unlock_attempts,--wrap=memory_increment_failed_unlock_attempts,--wrap=memory_set_encrypted_seed_and_hmac,--wrap=memory_get_encrypted_seed_and_hmac,--wrap=memory_get_salt_root,--wrap=reset_reset,--wrap=random_32_bytes"
233233
keystore_antiklepto
234234
""
235-
keystore_functional
236-
"-Wl,--wrap=memory_is_initialized,--wrap=memory_is_seeded,--wrap=memory_set_encrypted_seed_and_hmac,--wrap=memory_get_encrypted_seed_and_hmac,--wrap=memory_get_salt_root,--wrap=memory_get_failed_unlock_attempts,--wrap=memory_reset_failed_unlock_attempts,--wrap=memory_increment_failed_unlock_attempts"
237235
gestures
238236
""
239237
random

0 commit comments

Comments
 (0)