Skip to content

Commit 6d4781a

Browse files
committed
keystore: port _test_keystore_unlock_bip39 to Rust
1 parent 1308d60 commit 6d4781a

File tree

6 files changed

+53
-48
lines changed

6 files changed

+53
-48
lines changed

src/keystore.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,17 @@ USE_RESULT static keystore_error_t _retain_seed(const uint8_t* seed, size_t seed
354354

355355
USE_RESULT static bool _retain_bip39_seed(const uint8_t* bip39_seed)
356356
{
357+
#ifdef TESTING
358+
const uint8_t test_unstretched_retained_bip39_seed_encryption_key[32] =
359+
"\x9b\x44\xc7\x04\x88\x93\xfa\xaf\x6e\x2d\x76\x25\xd1\x3d\x8f\x1c\xab\x07\x65\xfd\x61\xf1"
360+
"\x59\xd9\x71\x3e\x08\x15\x5d\x06\x71\x7c";
361+
memcpy(
362+
_unstretched_retained_bip39_seed_encryption_key,
363+
test_unstretched_retained_bip39_seed_encryption_key,
364+
32);
365+
#else
357366
random_32_bytes(_unstretched_retained_bip39_seed_encryption_key);
367+
#endif
358368
uint8_t retained_bip39_seed_encryption_key[32] = {0};
359369
UTIL_CLEANUP_32(retained_bip39_seed_encryption_key);
360370
if (_stretch_retained_seed_encryption_key(

src/rust/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/rust/bitbox02-rust/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ version = "0.13.1"
6363
default-features = false
6464
features = ["derive"]
6565

66+
[dev-dependencies]
67+
bitbox-aes = { path = "../bitbox-aes", features = ["use-wally-sha512"] }
68+
6669
[features]
6770
ed25519 = [
6871
"dep:bip32-ed25519",

src/rust/bitbox02-sys/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ const ALLOWLIST_FNS: &[&str] = &[
8686
"keystore_unlock_bip39",
8787
"keystore_bip39_mnemonic_from_seed",
8888
"keystore_test_get_retained_seed_encrypted",
89+
"keystore_test_get_retained_bip39_seed_encrypted",
8990
"label_create",
9091
"localtime",
9192
"lock_animation_start",

src/rust/bitbox02/src/keystore.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,44 @@ mod tests {
568568
assert!(matches!(unlock("password"), Err(Error::Unseeded)));
569569
}
570570

571+
#[test]
572+
fn test_unlock_bip39() {
573+
mock_memory();
574+
lock();
575+
576+
let seed = hex::decode("1111111111111111222222222222222233333333333333334444444444444444")
577+
.unwrap();
578+
579+
let mock_salt_root =
580+
hex::decode("3333333333333333444444444444444411111111111111112222222222222222")
581+
.unwrap();
582+
crate::memory::set_salt_root(mock_salt_root.as_slice().try_into().unwrap()).unwrap();
583+
584+
assert!(encrypt_and_store_seed(&seed, "password").is_ok());
585+
assert!(unlock("password").is_ok());
586+
assert!(is_locked()); // still locked, it is only unlocked after unlock_bip39.
587+
assert!(unlock_bip39("foo").is_ok());
588+
589+
// Check that the retained bip39 seed was encrypted with the expected encryption key.
590+
let decrypted = {
591+
let retained_bip39_seed_encrypted: &[u8] = unsafe {
592+
let mut len = 0usize;
593+
let ptr = bitbox02_sys::keystore_test_get_retained_bip39_seed_encrypted(&mut len);
594+
core::slice::from_raw_parts(ptr, len)
595+
};
596+
let expected_retained_bip39_seed_secret =
597+
hex::decode("856d9a8c1ea42a69ae76324244ace674397ff1360a4ba4c85ffbd42cee8a7f29")
598+
.unwrap();
599+
bitbox_aes::decrypt_with_hmac(
600+
&expected_retained_bip39_seed_secret,
601+
retained_bip39_seed_encrypted,
602+
)
603+
.unwrap()
604+
};
605+
let expected_bip39_seed = hex::decode("2b3c63de86f0f2b13cc6a36c1ba2314fbc1b40c77ab9cb64e96ba4d5c62fc204748ca6626a9f035e7d431bce8c9210ec0bdffc2e7db873dee56c8ac2153eee9a").unwrap();
606+
assert_eq!(decrypted.as_slice(), expected_bip39_seed.as_slice());
607+
}
608+
571609
// This tests that you can create a keystore, unlock it, and then do this again. This is an
572610
// expected workflow for when the wallet setup process is restarted after seeding and unlocking,
573611
// but before creating a backup, in which case a new seed is created.

test/unit-test/test_keystore.c

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ static uint8_t _mock_bip39_seed[64] = {
5252
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
5353
};
5454

55-
static uint8_t _unstretched_retained_bip39_seed_encryption_key[32] =
56-
"\x9b\x44\xc7\x04\x88\x93\xfa\xaf\x6e\x2d\x76\x25\xd1\x3d\x8f\x1c\xab\x07\x65\xfd\x61\xf1\x59"
57-
"\xd9\x71\x3e\x08\x15\x5d\x06\x71\x7c";
58-
5955
static const uint32_t _keypath[] = {
6056
44 + BIP32_INITIAL_HARDENED_CHILD,
6157
0 + BIP32_INITIAL_HARDENED_CHILD,
@@ -69,10 +65,6 @@ static const uint8_t _expected_seckey[32] = {
6965
0x58, 0x92, 0x32, 0x9d, 0x67, 0xdf, 0xd4, 0xad, 0x05, 0xe9, 0xc3, 0xd0, 0x6e, 0xdf, 0x74, 0xfb,
7066
};
7167

72-
const uint8_t _expected_retained_bip39_seed_secret[32] =
73-
"\x85\x6d\x9a\x8c\x1e\xa4\x2a\x69\xae\x76\x32\x42\x44\xac\xe6\x74\x39\x7f\xf1\x36\x0a\x4b\xa4"
74-
"\xc8\x5f\xfb\xd4\x2c\xee\x8a\x7f\x29";
75-
7668
// Same as Python:
7769
// import hmac, hashlib; hmac.digest(b"unit-test", b"password", hashlib.sha256).hex()
7870
// See also: securechip_mock.c
@@ -120,16 +112,8 @@ void __wrap_random_32_bytes(uint8_t* buf)
120112
memcpy(buf, (const void*)mock(), 32);
121113
}
122114

123-
static void _expect_retain_bip39_seed(void)
124-
{
125-
will_return(__wrap_random_32_bytes, _unstretched_retained_bip39_seed_encryption_key);
126-
}
127-
128115
void _mock_unlocked(const uint8_t* seed, size_t seed_len, const uint8_t* bip39_seed)
129116
{
130-
if (bip39_seed != NULL) {
131-
_expect_retain_bip39_seed();
132-
}
133117
keystore_mock_unlocked(seed, seed_len, bip39_seed);
134118
}
135119

@@ -236,36 +220,6 @@ static void _expect_encrypt_and_store_seed(void)
236220
will_return(__wrap_memory_is_initialized, false);
237221
}
238222

239-
static void _test_keystore_unlock_bip39(void** state)
240-
{
241-
keystore_lock();
242-
assert_false(keystore_unlock_bip39(""));
243-
244-
_mock_unlocked(_mock_seed, sizeof(_mock_seed), NULL);
245-
assert_true(keystore_is_locked());
246-
247-
_expect_retain_bip39_seed();
248-
assert_true(keystore_unlock_bip39("foo"));
249-
// Check that the retained bip39 seed was encrypted with the expected encryption key.
250-
size_t encrypted_len = 0;
251-
const uint8_t* retained_bip39_seed_encrypted =
252-
keystore_test_get_retained_bip39_seed_encrypted(&encrypted_len);
253-
size_t decrypted_len = encrypted_len - 48;
254-
uint8_t out[decrypted_len];
255-
assert_true(cipher_aes_hmac_decrypt(
256-
retained_bip39_seed_encrypted,
257-
encrypted_len,
258-
out,
259-
&decrypted_len,
260-
_expected_retained_bip39_seed_secret));
261-
assert_int_equal(decrypted_len, 64);
262-
const uint8_t expected_bip39_seed[64] =
263-
"\x2b\x3c\x63\xde\x86\xf0\xf2\xb1\x3c\xc6\xa3\x6c\x1b\xa2\x31\x4f\xbc\x1b\x40\xc7\x7a\xb9"
264-
"\xcb\x64\xe9\x6b\xa4\xd5\xc6\x2f\xc2\x04\x74\x8c\xa6\x62\x6a\x9f\x03\x5e\x7d\x43\x1b\xce"
265-
"\x8c\x92\x10\xec\x0b\xdf\xfc\x2e\x7d\xb8\x73\xde\xe5\x6c\x8a\xc2\x15\x3e\xee\x9a";
266-
assert_memory_equal(out, expected_bip39_seed, decrypted_len);
267-
}
268-
269223
static void _test_keystore_lock(void** state)
270224
{
271225
_mock_unlocked(NULL, 0, NULL);
@@ -328,7 +282,6 @@ static void _mock_with_mnemonic(const char* mnemonic, const char* passphrase)
328282
assert_true(keystore_bip39_mnemonic_to_seed(mnemonic, seed, &seed_len));
329283

330284
_mock_unlocked(seed, seed_len, NULL);
331-
_expect_retain_bip39_seed();
332285
assert_true(keystore_unlock_bip39(passphrase));
333286
}
334287

@@ -442,7 +395,6 @@ int main(void)
442395
const struct CMUnitTest tests[] = {
443396
cmocka_unit_test(_test_keystore_secp256k1_nonce_commit),
444397
cmocka_unit_test(_test_keystore_secp256k1_sign),
445-
cmocka_unit_test(_test_keystore_unlock_bip39),
446398
cmocka_unit_test(_test_keystore_lock),
447399
cmocka_unit_test(_test_keystore_create_and_store_seed),
448400
cmocka_unit_test(_test_secp256k1_schnorr_sign),

0 commit comments

Comments
 (0)