Skip to content

Commit 36d8a62

Browse files
committed
keystore: move unlock_bip39() to Rust
With the goal of making it async.
1 parent 304b566 commit 36d8a62

File tree

8 files changed

+127
-173
lines changed

8 files changed

+127
-173
lines changed

src/keystore.c

Lines changed: 2 additions & 25 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().
@@ -512,29 +512,6 @@ bool keystore_unlock_bip39_finalize(const uint8_t* bip39_seed)
512512
return true;
513513
}
514514

515-
bool keystore_unlock_bip39(
516-
const uint8_t* seed,
517-
size_t seed_length,
518-
const char* mnemonic_passphrase,
519-
uint8_t* root_fingerprint_out)
520-
{
521-
if (!keystore_unlock_bip39_check(seed, seed_length)) {
522-
return false;
523-
}
524-
525-
usb_processing_timeout_reset(LONG_TIMEOUT);
526-
527-
uint8_t bip39_seed[64] = {0};
528-
UTIL_CLEANUP_64(bip39_seed);
529-
rust_derive_bip39_seed(
530-
rust_util_bytes(seed, seed_length),
531-
mnemonic_passphrase,
532-
rust_util_bytes_mut(bip39_seed, sizeof(bip39_seed)),
533-
rust_util_bytes_mut(root_fingerprint_out, 4));
534-
535-
return keystore_unlock_bip39_finalize(bip39_seed);
536-
}
537-
538515
void keystore_lock(void)
539516
{
540517
_is_unlocked_device = false;

src/keystore.h

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,22 +113,6 @@ keystore_unlock(const char* password, uint8_t* remaining_attempts_out, int* secu
113113
*/
114114
USE_RESULT bool keystore_unlock_bip39_check(const uint8_t* seed, size_t seed_length);
115115

116-
/** Unlocks the bip39 seed. The input seed must be the keystore seed (i.e. must match the output
117-
* of `keystore_copy_seed()`).
118-
* @param[in] seed the input seed to BIP39.
119-
* @param[in] seed_length the size of the seed
120-
* @param[in] mnemonic_passphrase bip39 passphrase used in the derivation. Use the
121-
* empty string if no passphrase is needed or provided.
122-
* @param[out] root_fingerprint_out must be 4 bytes long and will contain the root fingerprint of
123-
* the wallet.
124-
* @return returns false if there was a critital memory error, otherwise true.
125-
*/
126-
USE_RESULT bool keystore_unlock_bip39(
127-
const uint8_t* seed,
128-
size_t seed_length,
129-
const char* mnemonic_passphrase,
130-
uint8_t* root_fingerprint_out);
131-
132116
/**
133117
* Retains the given bip39 seed and marks the keystore as unlocked.
134118
* @param[in] bip39_seed 64 byte bip39 seed.

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: 3 additions & 3 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());
@@ -660,4 +660,4 @@ mod tests {
660660
.is_ok()
661661
);
662662
}
663-
}
663+
}

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)