@@ -95,30 +95,61 @@ pub fn lock() {
95
95
unsafe { ROOT_FINGERPRINT . write ( None ) }
96
96
}
97
97
98
- pub fn unlock_bip39 ( seed : & [ u8 ] , mnemonic_passphrase : & str ) -> Result < ( ) , Error > {
99
- let mut root_fingerprint = [ 0u8 ; 4 ] ;
100
- if unsafe {
101
- bitbox02_sys:: keystore_unlock_bip39 (
102
- seed. as_ptr ( ) ,
103
- seed. len ( ) ,
104
- crate :: util:: str_to_cstr_vec ( mnemonic_passphrase)
105
- . unwrap ( )
106
- . as_ptr ( )
107
- . cast ( ) ,
108
- root_fingerprint. as_mut_ptr ( ) ,
109
- )
110
- } {
111
- // Store root fingerprint.
112
- unsafe {
113
- ROOT_FINGERPRINT . write ( Some ( root_fingerprint) ) ;
114
- }
98
+ fn unlock_bip39_check ( seed : & [ u8 ] ) -> Result < ( ) , Error > {
99
+ if unsafe { bitbox02_sys:: keystore_unlock_bip39_check ( seed. as_ptr ( ) , seed. len ( ) ) } {
100
+ Ok ( ( ) )
101
+ } else {
102
+ Err ( Error :: CannotUnlockBIP39 )
103
+ }
104
+ }
115
105
106
+ fn unlock_bip39_finalize ( bip39_seed : & [ u8 ; 64 ] ) -> Result < ( ) , Error > {
107
+ if unsafe { bitbox02_sys:: keystore_unlock_bip39_finalize ( bip39_seed. as_ptr ( ) ) } {
116
108
Ok ( ( ) )
117
109
} else {
118
110
Err ( Error :: CannotUnlockBIP39 )
119
111
}
120
112
}
121
113
114
+ fn derive_bip39_seed (
115
+ secp : & Secp256k1 < All > ,
116
+ seed : & [ u8 ] ,
117
+ mnemonic_passphrase : & str ,
118
+ ) -> ( zeroize:: Zeroizing < [ u8 ; 64 ] > , [ u8 ; 4 ] ) {
119
+ let mnemonic = bip39:: Mnemonic :: from_entropy_in ( bip39:: Language :: English , seed) . unwrap ( ) ;
120
+ let bip39_seed: zeroize:: Zeroizing < [ u8 ; 64 ] > =
121
+ zeroize:: Zeroizing :: new ( mnemonic. to_seed_normalized ( mnemonic_passphrase) ) ;
122
+ let root_fingerprint: [ u8 ; 4 ] =
123
+ bitcoin:: bip32:: Xpriv :: new_master ( bitcoin:: NetworkKind :: Main , bip39_seed. as_ref ( ) )
124
+ . unwrap ( )
125
+ . fingerprint ( secp)
126
+ . to_bytes ( ) ;
127
+
128
+ ( bip39_seed, root_fingerprint)
129
+ }
130
+
131
+ /// Unlocks the bip39 seed. The input seed must be the keystore seed (i.e. must match the output
132
+ /// of `keystore_copy_seed()`).
133
+ /// `mnemonic_passphrase` is the bip39 passphrase used in the derivation. Use the empty string if no
134
+ /// passphrase is needed or provided.
135
+ pub fn unlock_bip39 (
136
+ secp : & Secp256k1 < All > ,
137
+ seed : & [ u8 ] ,
138
+ mnemonic_passphrase : & str ,
139
+ ) -> Result < ( ) , Error > {
140
+ unlock_bip39_check ( seed) ?;
141
+
142
+ let ( bip39_seed, root_fingerprint) = derive_bip39_seed ( secp, seed, mnemonic_passphrase) ;
143
+
144
+ unlock_bip39_finalize ( bip39_seed. as_slice ( ) . try_into ( ) . unwrap ( ) ) ?;
145
+
146
+ // Store root fingerprint.
147
+ unsafe {
148
+ ROOT_FINGERPRINT . write ( Some ( root_fingerprint) ) ;
149
+ }
150
+ Ok ( ( ) )
151
+ }
152
+
122
153
pub fn root_fingerprint ( ) -> Result < Vec < u8 > , ( ) > {
123
154
if is_locked ( ) {
124
155
return Err ( ( ) ) ;
@@ -411,7 +442,7 @@ mod tests {
411
442
. unwrap ( ) ;
412
443
assert ! ( encrypt_and_store_seed( & seed, "password" ) . is_ok( ) ) ;
413
444
assert ! ( is_locked( ) ) ; // still locked, it is only unlocked after unlock_bip39.
414
- assert ! ( unlock_bip39( & seed, "foo" ) . is_ok( ) ) ;
445
+ assert ! ( unlock_bip39( & secp256k1 :: Secp256k1 :: new ( ) , & seed, "foo" ) . is_ok( ) ) ;
415
446
assert ! ( !is_locked( ) ) ;
416
447
lock ( ) ;
417
448
assert ! ( is_locked( ) ) ;
@@ -484,6 +515,69 @@ mod tests {
484
515
assert ! ( matches!( unlock( "password" ) , Err ( Error :: Unseeded ) ) ) ;
485
516
}
486
517
518
+ #[ test]
519
+ fn test_derive_bip39_seed ( ) {
520
+ struct Test {
521
+ seed : & ' static str ,
522
+ passphrase : & ' static str ,
523
+ expected_bip39_seed : & ' static str ,
524
+ expected_root_fingerprint : & ' static str ,
525
+ }
526
+
527
+ let tests = & [
528
+ // 16 byte seed
529
+ Test {
530
+ seed : "fb5cf00d5ea61059fa066e25a6be9544" ,
531
+ passphrase : "" ,
532
+ expected_bip39_seed : "f4577e463be595868060e5a763328153155b4167cd284998c8c6096d044742372020f5b052d0c41c1c5e6a6a7da2cb8a367aaaa074fab7773e8d5b2f684257ed" ,
533
+ expected_root_fingerprint : "0b2fa4e5" ,
534
+ } ,
535
+ Test {
536
+ seed : "fb5cf00d5ea61059fa066e25a6be9544" ,
537
+ passphrase : "password" ,
538
+ expected_bip39_seed : "5922fb7630bc7cb871af102f733b6bdb8f05945147cd4646a89056fde0bdad5c3a4ff5be3f9e7af535f570e7053b5b22472555b331bc89cb797c306f7eb6a5a1" ,
539
+ expected_root_fingerprint : "c4062d44" ,
540
+ } ,
541
+ // 24 byte seed
542
+ Test {
543
+ seed : "23705a91b177b49822f28b3f1a60072d113fcaff4f250191" ,
544
+ passphrase : "" ,
545
+ expected_bip39_seed : "4a2a016a6d90eb3a79b7931ca0a172df5c5bfee3e5b47f0fd84bc0791ea3bbc9476c3d5de71cdb12c37e93c2aa3d5c303257f1992aed400fc5bbfc7da787bfa7" ,
546
+ expected_root_fingerprint : "62fd19e0" ,
547
+ } ,
548
+ Test {
549
+ seed : "23705a91b177b49822f28b3f1a60072d113fcaff4f250191" ,
550
+ passphrase : "password" ,
551
+ expected_bip39_seed : "bc317ee0f88870254be32274d63ec2b0e962bf09f3ca04287912bfc843f2fab7c556f8657cadc924f99a217b0daa91898303a8414102031a125c50023e45a80b" ,
552
+ expected_root_fingerprint : "c745266d" ,
553
+ } ,
554
+ // 32 byte seed
555
+ Test {
556
+ seed : "bd83a008b3b78c8cc56c678d1b7bfc651cc5be8242f44b5c0db96a34ee297833" ,
557
+ passphrase : "" ,
558
+ expected_bip39_seed : "63f844e2c61ecfb20f9100de381a7a9ec875b085f5ac7735a2ba4d615a0f4147b87be402f65651969130683deeef752760c09e291604fe4b89d61ffee2630be8" ,
559
+ expected_root_fingerprint : "93ba3a7b" ,
560
+ } ,
561
+ Test {
562
+ seed : "bd83a008b3b78c8cc56c678d1b7bfc651cc5be8242f44b5c0db96a34ee297833" ,
563
+ passphrase : "password" ,
564
+ expected_bip39_seed : "42e90dacd61f3373542d212f0fb9c291dcea84a6d85034272372dde7188638a98527280d65e41599f30d3434d8ee3d4747dbb84801ff1a851d2306c7d1648374" ,
565
+ expected_root_fingerprint : "b95c9318" ,
566
+ } ,
567
+ ] ;
568
+
569
+ let secp = secp256k1:: Secp256k1 :: new ( ) ;
570
+ for test in tests {
571
+ let seed = hex:: decode ( test. seed ) . unwrap ( ) ;
572
+ let ( bip39_seed, root_fingerprint) = derive_bip39_seed ( & secp, & seed, test. passphrase ) ;
573
+ assert_eq ! ( hex:: encode( bip39_seed) . as_str( ) , test. expected_bip39_seed) ;
574
+ assert_eq ! (
575
+ hex:: encode( root_fingerprint) . as_str( ) ,
576
+ test. expected_root_fingerprint
577
+ ) ;
578
+ }
579
+ }
580
+
487
581
#[ test]
488
582
fn test_unlock_bip39 ( ) {
489
583
mock_memory ( ) ;
@@ -497,13 +591,15 @@ mod tests {
497
591
. unwrap ( ) ;
498
592
crate :: memory:: set_salt_root ( mock_salt_root. as_slice ( ) . try_into ( ) . unwrap ( ) ) . unwrap ( ) ;
499
593
594
+ let secp = secp256k1:: Secp256k1 :: new ( ) ;
595
+
500
596
assert ! ( root_fingerprint( ) . is_err( ) ) ;
501
597
assert ! ( encrypt_and_store_seed( & seed, "password" ) . is_ok( ) ) ;
502
598
assert ! ( root_fingerprint( ) . is_err( ) ) ;
503
599
// Incorrect seed passed
504
- assert ! ( unlock_bip39( b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" , "foo" ) . is_err( ) ) ;
600
+ assert ! ( unlock_bip39( & secp , b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" , "foo" ) . is_err( ) ) ;
505
601
// Correct seed passed.
506
- assert ! ( unlock_bip39( & seed, "foo" ) . is_ok( ) ) ;
602
+ assert ! ( unlock_bip39( & secp , & seed, "foo" ) . is_ok( ) ) ;
507
603
assert_eq ! ( root_fingerprint( ) , Ok ( vec![ 0xf1 , 0xbc , 0x3c , 0x46 ] ) , ) ;
508
604
509
605
let expected_bip39_seed = hex:: decode ( "2b3c63de86f0f2b13cc6a36c1ba2314fbc1b40c77ab9cb64e96ba4d5c62fc204748ca6626a9f035e7d431bce8c9210ec0bdffc2e7db873dee56c8ac2153eee9a" ) . unwrap ( ) ;
0 commit comments