Skip to content

Commit 49fa5ce

Browse files
committed
Merge branch 'rust-bip39-unlock'
2 parents 3d65f25 + 99755d2 commit 49fa5ce

File tree

8 files changed

+143
-157
lines changed

8 files changed

+143
-157
lines changed

src/keystore.c

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ static size_t _retained_seed_encrypted_len = 0;
4141
// plaintext.
4242
static uint8_t _retained_seed_hash[32] = {0};
4343

44-
// Change this ONLY via keystore_unlock_bip39().
44+
// Change this ONLY via keystore_unlock_bip39_finalize().
4545
static bool _is_unlocked_bip39 = false;
46-
// Stores a random keyy after bip39-unlock which, after stretching, is used to encrypt the retained
46+
// Stores a random key after bip39-unlock which, after stretching, is used to encrypt the retained
4747
// bip39 seed.
4848
static uint8_t _unstretched_retained_bip39_seed_encryption_key[32] = {0};
4949
// Must be defined if _is_unlocked is true. ONLY ACCESS THIS WITH keystore_copy_bip39_seed().
@@ -485,16 +485,11 @@ keystore_error_t keystore_unlock(
485485
return result;
486486
}
487487

488-
bool keystore_unlock_bip39(
489-
const uint8_t* seed,
490-
size_t seed_length,
491-
const char* mnemonic_passphrase,
492-
uint8_t* root_fingerprint_out)
488+
bool keystore_unlock_bip39_check(const uint8_t* seed, size_t seed_length)
493489
{
494490
if (!_is_unlocked_device) {
495491
return false;
496492
}
497-
usb_processing_timeout_reset(LONG_TIMEOUT);
498493

499494
uint8_t seed_hashed[32] = {0};
500495
UTIL_CLEANUP_32(seed_hashed);
@@ -505,14 +500,13 @@ bool keystore_unlock_bip39(
505500
return false;
506501
}
507502

508-
uint8_t bip39_seed[64] = {0};
509-
UTIL_CLEANUP_64(bip39_seed);
510-
rust_derive_bip39_seed(
511-
rust_util_bytes(seed, seed_length),
512-
mnemonic_passphrase,
513-
rust_util_bytes_mut(bip39_seed, sizeof(bip39_seed)),
514-
rust_util_bytes_mut(root_fingerprint_out, 4));
503+
usb_processing_timeout_reset(LONG_TIMEOUT);
504+
505+
return true;
506+
}
515507

508+
bool keystore_unlock_bip39_finalize(const uint8_t* bip39_seed)
509+
{
516510
if (!_retain_bip39_seed(bip39_seed)) {
517511
return false;
518512
}

src/keystore.h

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,21 +104,20 @@ USE_RESULT keystore_error_t keystore_create_and_store_seed(
104104
USE_RESULT keystore_error_t
105105
keystore_unlock(const char* password, uint8_t* remaining_attempts_out, int* securechip_result_out);
106106

107-
/** Unlocks the bip39 seed. The input seed must be the keystore seed (i.e. must match the output
107+
/**
108+
* Checks if bip39 unlocking can be performed. It can be performed if `keystore_unlock()`
109+
* successfully and the input seed matches the keystore seed (i.e. must match the output
108110
* of `keystore_copy_seed()`).
109111
* @param[in] seed the input seed to BIP39.
110112
* @param[in] seed_length the size of the seed
111-
* @param[in] mnemonic_passphrase bip39 passphrase used in the derivation. Use the
112-
* empty string if no passphrase is needed or provided.
113-
* @param[out] root_fingerprint_out must be 4 bytes long and will contain the root fingerprint of
114-
* the wallet.
115-
* @return returns false if there was a critital memory error, otherwise true.
116113
*/
117-
USE_RESULT bool keystore_unlock_bip39(
118-
const uint8_t* seed,
119-
size_t seed_length,
120-
const char* mnemonic_passphrase,
121-
uint8_t* root_fingerprint_out);
114+
USE_RESULT bool keystore_unlock_bip39_check(const uint8_t* seed, size_t seed_length);
115+
116+
/**
117+
* Retains the given bip39 seed and marks the keystore as unlocked.
118+
* @param[in] bip39_seed 64 byte bip39 seed.
119+
*/
120+
USE_RESULT bool keystore_unlock_bip39_finalize(const uint8_t* bip39_seed);
122121

123122
/**
124123
* Locks the keystore (resets to state before `keystore_unlock()`).

src/rust/bitbox02-rust/src/bip39.rs

Lines changed: 0 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -27,37 +27,6 @@ pub fn get_word(idx: u16) -> Result<zeroize::Zeroizing<String>, ()> {
2727

2828
// C API
2929

30-
/// # Safety
31-
///
32-
/// The passphrase must be not NULL and null-terminated.
33-
///
34-
/// `seed` must be 16, 24 or 32 bytes long.
35-
/// `bip39_seed_out` must be exactly 64 bytes long.
36-
/// `root_fingerprint_out` must be exactly 4 bytes long.
37-
#[unsafe(no_mangle)]
38-
pub unsafe extern "C" fn rust_derive_bip39_seed(
39-
seed: util::bytes::Bytes,
40-
passphrase: *const core::ffi::c_char,
41-
mut bip39_seed_out: util::bytes::BytesMut,
42-
mut root_fingerprint_out: util::bytes::BytesMut,
43-
) {
44-
let mnemonic =
45-
bip39::Mnemonic::from_entropy_in(bip39::Language::English, seed.as_ref()).unwrap();
46-
let passphrase = unsafe { core::ffi::CStr::from_ptr(passphrase) };
47-
let bip39_seed: zeroize::Zeroizing<[u8; 64]> =
48-
zeroize::Zeroizing::new(mnemonic.to_seed_normalized(passphrase.to_str().unwrap()));
49-
bip39_seed_out.as_mut().clone_from_slice(&bip39_seed[..]);
50-
51-
let root_fingerprint: [u8; 4] =
52-
bitcoin::bip32::Xpriv::new_master(bitcoin::NetworkKind::Main, bip39_seed.as_ref())
53-
.unwrap()
54-
.fingerprint(crate::secp256k1::SECP256K1)
55-
.to_bytes();
56-
root_fingerprint_out
57-
.as_mut()
58-
.clone_from_slice(&root_fingerprint);
59-
}
60-
6130
#[unsafe(no_mangle)]
6231
pub extern "C" fn rust_get_bip39_word(idx: u16, mut out: util::bytes::BytesMut) -> bool {
6332
let word = match get_word(idx) {
@@ -78,80 +47,6 @@ pub extern "C" fn rust_get_bip39_word(idx: u16, mut out: util::bytes::BytesMut)
7847
mod tests {
7948
use super::*;
8049

81-
#[test]
82-
fn test_rust_derive_bip39_seed() {
83-
struct Test {
84-
seed: &'static str,
85-
passphrase: &'static core::ffi::CStr,
86-
expected_bip39_seed: &'static str,
87-
expected_root_fingerprint: &'static str,
88-
}
89-
90-
let tests = &[
91-
// 16 byte seed
92-
Test {
93-
seed: "fb5cf00d5ea61059fa066e25a6be9544",
94-
passphrase: c"",
95-
expected_bip39_seed: "f4577e463be595868060e5a763328153155b4167cd284998c8c6096d044742372020f5b052d0c41c1c5e6a6a7da2cb8a367aaaa074fab7773e8d5b2f684257ed",
96-
expected_root_fingerprint: "0b2fa4e5",
97-
},
98-
Test {
99-
seed: "fb5cf00d5ea61059fa066e25a6be9544",
100-
passphrase: c"password",
101-
expected_bip39_seed: "5922fb7630bc7cb871af102f733b6bdb8f05945147cd4646a89056fde0bdad5c3a4ff5be3f9e7af535f570e7053b5b22472555b331bc89cb797c306f7eb6a5a1",
102-
expected_root_fingerprint: "c4062d44",
103-
},
104-
// 24 byte seed
105-
Test {
106-
seed: "23705a91b177b49822f28b3f1a60072d113fcaff4f250191",
107-
passphrase: c"",
108-
expected_bip39_seed: "4a2a016a6d90eb3a79b7931ca0a172df5c5bfee3e5b47f0fd84bc0791ea3bbc9476c3d5de71cdb12c37e93c2aa3d5c303257f1992aed400fc5bbfc7da787bfa7",
109-
expected_root_fingerprint: "62fd19e0",
110-
},
111-
Test {
112-
seed: "23705a91b177b49822f28b3f1a60072d113fcaff4f250191",
113-
passphrase: c"password",
114-
expected_bip39_seed: "bc317ee0f88870254be32274d63ec2b0e962bf09f3ca04287912bfc843f2fab7c556f8657cadc924f99a217b0daa91898303a8414102031a125c50023e45a80b",
115-
expected_root_fingerprint: "c745266d",
116-
},
117-
// 32 byte seed
118-
Test {
119-
seed: "bd83a008b3b78c8cc56c678d1b7bfc651cc5be8242f44b5c0db96a34ee297833",
120-
passphrase: c"",
121-
expected_bip39_seed: "63f844e2c61ecfb20f9100de381a7a9ec875b085f5ac7735a2ba4d615a0f4147b87be402f65651969130683deeef752760c09e291604fe4b89d61ffee2630be8",
122-
expected_root_fingerprint: "93ba3a7b",
123-
},
124-
Test {
125-
seed: "bd83a008b3b78c8cc56c678d1b7bfc651cc5be8242f44b5c0db96a34ee297833",
126-
passphrase: c"password",
127-
expected_bip39_seed: "42e90dacd61f3373542d212f0fb9c291dcea84a6d85034272372dde7188638a98527280d65e41599f30d3434d8ee3d4747dbb84801ff1a851d2306c7d1648374",
128-
expected_root_fingerprint: "b95c9318",
129-
},
130-
];
131-
132-
for test in tests {
133-
let seed = hex::decode(test.seed).unwrap();
134-
let mut bip39_seed = [0u8; 64];
135-
let mut root_fingerprint = [0u8; 4];
136-
unsafe {
137-
rust_derive_bip39_seed(
138-
util::bytes::rust_util_bytes(seed.as_ptr(), seed.len()),
139-
test.passphrase.as_ptr(),
140-
util::bytes::rust_util_bytes_mut(bip39_seed.as_mut_ptr(), bip39_seed.len()),
141-
util::bytes::rust_util_bytes_mut(
142-
root_fingerprint.as_mut_ptr(),
143-
root_fingerprint.len(),
144-
),
145-
);
146-
}
147-
assert_eq!(hex::encode(bip39_seed).as_str(), test.expected_bip39_seed);
148-
assert_eq!(
149-
hex::encode(root_fingerprint).as_str(),
150-
test.expected_root_fingerprint
151-
);
152-
}
153-
}
154-
15550
#[test]
15651
fn test_rust_get_bip39_word() {
15752
let mut word = [1u8; 10];

src/rust/bitbox02-rust/src/keystore.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ mod tests {
573573
keystore::lock();
574574
let seed = &seed[..test.seed_len];
575575

576-
assert!(keystore::unlock_bip39(seed, test.mnemonic_passphrase).is_err());
576+
assert!(keystore::unlock_bip39(SECP256K1, seed, test.mnemonic_passphrase).is_err());
577577

578578
bitbox02::securechip::fake_event_counter_reset();
579579
assert!(keystore::encrypt_and_store_seed(seed, "foo").is_ok());
@@ -582,7 +582,7 @@ mod tests {
582582
assert!(keystore::is_locked());
583583

584584
bitbox02::securechip::fake_event_counter_reset();
585-
assert!(keystore::unlock_bip39(seed, test.mnemonic_passphrase).is_ok());
585+
assert!(keystore::unlock_bip39(SECP256K1, seed, test.mnemonic_passphrase).is_ok());
586586
assert_eq!(bitbox02::securechip::fake_event_counter(), 1);
587587

588588
assert!(!keystore::is_locked());

src/rust/bitbox02-rust/src/workflow/unlock.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,9 @@ pub async fn unlock_bip39(hal: &mut impl crate::hal::Hal, seed: &[u8]) {
133133
}
134134
}
135135

136-
let result =
137-
bitbox02::ui::with_lock_animation(|| keystore::unlock_bip39(seed, &mnemonic_passphrase));
136+
let result = bitbox02::ui::with_lock_animation(|| {
137+
keystore::unlock_bip39(crate::secp256k1::SECP256K1, seed, &mnemonic_passphrase)
138+
});
138139
if result.is_err() {
139140
abort("bip39 unlock failed");
140141
}

src/rust/bitbox02-sys/build.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ const ALLOWLIST_FNS: &[&str] = &[
7979
"keystore_secp256k1_nonce_commit",
8080
"keystore_secp256k1_sign",
8181
"keystore_unlock",
82-
"keystore_unlock_bip39",
82+
"keystore_unlock_bip39_check",
83+
"keystore_unlock_bip39_finalize",
8384
"keystore_test_get_retained_seed_encrypted",
8485
"keystore_test_get_retained_bip39_seed_encrypted",
8586
"label_create",

0 commit comments

Comments
 (0)