36
36
37
37
// Change this ONLY via keystore_unlock() or keystore_lock()
38
38
static bool _is_unlocked_device = false;
39
- // Must be defined if is_unlocked is true. Length of the seed store in `_retained_seed`. See also:
40
- // `_validate_seed_length()`.
41
- static size_t _seed_length = 0 ;
39
+ // Stores a random key after unlock which, after stretching, is used to encrypt the retained seed.
40
+ static uint8_t _unstretched_retained_seed_encryption_key [32 ] = {0 };
42
41
// Must be defined if is_unlocked is true. ONLY ACCESS THIS WITH keystore_copy_seed().
43
- static uint8_t _retained_seed [KEYSTORE_MAX_SEED_LENGTH ] = {0 };
42
+ // Stores the encrypted seed after unlock.
43
+ static uint8_t _retained_seed_encrypted [KEYSTORE_MAX_SEED_LENGTH + 64 ] = {0 };
44
+ static size_t _retained_seed_encrypted_len = 0 ;
44
45
45
46
// Change this ONLY via keystore_unlock_bip39().
46
47
static bool _is_unlocked_bip39 = false;
48
+ // Stores a random keyy after bip39-unlock which, after stretching, is used to encrypt the retained
49
+ // bip39 seed.
50
+ static uint8_t _unstretched_retained_bip39_seed_encryption_key [32 ] = {0 };
47
51
// Must be defined if _is_unlocked is true. ONLY ACCESS THIS WITH _copy_bip39_seed().
48
- static uint8_t _retained_bip39_seed [64 ] = {0 };
49
-
50
- #ifdef TESTING
51
- void keystore_mock_unlocked (const uint8_t * seed , size_t seed_len , const uint8_t * bip39_seed )
52
- {
53
- _is_unlocked_device = seed != NULL ;
54
- if (seed != NULL ) {
55
- _seed_length = seed_len ;
56
- memcpy (_retained_seed , seed , seed_len );
57
- }
58
- _is_unlocked_bip39 = bip39_seed != NULL ;
59
- if (bip39_seed != NULL ) {
60
- memcpy (_retained_bip39_seed , bip39_seed , sizeof (_retained_bip39_seed ));
61
- }
62
- }
63
- #endif
52
+ // Stores the encrypted BIP-39 seed after bip39-unlock.
53
+ static uint8_t _retained_bip39_seed_encrypted [64 + 64 ] = {0 };
54
+ static size_t _retained_bip39_seed_encrypted_len = 0 ;
64
55
65
56
/**
66
57
* We allow seeds of 16, 24 or 32 bytes.
@@ -70,13 +61,56 @@ static bool _validate_seed_length(size_t seed_len)
70
61
return seed_len == 16 || seed_len == 24 || seed_len == 32 ;
71
62
}
72
63
64
+ USE_RESULT static keystore_error_t _stretch_retained_seed_encryption_key (
65
+ const uint8_t * encryption_key ,
66
+ const char * purpose_in ,
67
+ const char * purpose_out ,
68
+ uint8_t * out )
69
+ {
70
+ uint8_t salted_hashed [32 ] = {0 };
71
+ UTIL_CLEANUP_32 (salted_hashed );
72
+ if (!salt_hash_data (encryption_key , 32 , purpose_in , salted_hashed )) {
73
+ return KEYSTORE_ERR_SALT ;
74
+ }
75
+ if (securechip_kdf (SECURECHIP_SLOT_KDF , salted_hashed , 32 , out )) {
76
+ return KEYSTORE_ERR_SECURECHIP ;
77
+ }
78
+ if (!salt_hash_data (encryption_key , 32 , purpose_out , salted_hashed )) {
79
+ return KEYSTORE_ERR_SALT ;
80
+ }
81
+ if (wally_hmac_sha256 (salted_hashed , sizeof (salted_hashed ), out , 32 , out , 32 ) != WALLY_OK ) {
82
+ return KEYSTORE_ERR_HASH ;
83
+ }
84
+ return KEYSTORE_OK ;
85
+ }
86
+
73
87
bool keystore_copy_seed (uint8_t * seed_out , size_t * length_out )
74
88
{
75
89
if (!_is_unlocked_device ) {
76
90
return false;
77
91
}
78
- memcpy (seed_out , _retained_seed , _seed_length );
79
- * length_out = _seed_length ;
92
+
93
+ uint8_t retained_seed_encryption_key [32 ] = {0 };
94
+ UTIL_CLEANUP_32 (retained_seed_encryption_key );
95
+ if (_stretch_retained_seed_encryption_key (
96
+ _unstretched_retained_seed_encryption_key ,
97
+ "keystore_retained_seed_access_in" ,
98
+ "keystore_retained_seed_access_out" ,
99
+ retained_seed_encryption_key ) != KEYSTORE_OK ) {
100
+ return false;
101
+ }
102
+ size_t len = _retained_seed_encrypted_len - 48 ;
103
+ bool password_correct = cipher_aes_hmac_decrypt (
104
+ _retained_seed_encrypted ,
105
+ _retained_seed_encrypted_len ,
106
+ seed_out ,
107
+ & len ,
108
+ retained_seed_encryption_key );
109
+ if (!password_correct ) {
110
+ // Should never happen.
111
+ return false;
112
+ }
113
+ * length_out = len ;
80
114
return true;
81
115
}
82
116
@@ -92,13 +126,37 @@ static bool _copy_bip39_seed(uint8_t* bip39_seed_out)
92
126
if (!_is_unlocked_bip39 ) {
93
127
return false;
94
128
}
129
+
130
+ uint8_t retained_bip39_seed_encryption_key [32 ] = {0 };
131
+ UTIL_CLEANUP_32 (retained_bip39_seed_encryption_key );
132
+ if (_stretch_retained_seed_encryption_key (
133
+ _unstretched_retained_bip39_seed_encryption_key ,
134
+ "keystore_retained_bip39_seed_access_in" ,
135
+ "keystore_retained_bip39_seed_access_out" ,
136
+ retained_bip39_seed_encryption_key ) != KEYSTORE_OK ) {
137
+ return false;
138
+ }
139
+ size_t len = _retained_bip39_seed_encrypted_len - 48 ;
140
+ bool password_correct = cipher_aes_hmac_decrypt (
141
+ _retained_bip39_seed_encrypted ,
142
+ _retained_bip39_seed_encrypted_len ,
143
+ bip39_seed_out ,
144
+ & len ,
145
+ retained_bip39_seed_encryption_key );
146
+ if (!password_correct ) {
147
+ // Should never happen.
148
+ return false;
149
+ }
150
+ if (len != 64 ) {
151
+ // Should never happen.
152
+ return false;
153
+ }
95
154
// sanity check
96
155
uint8_t zero [64 ] = {0 };
97
156
util_zero (zero , 64 );
98
- if (MEMEQ (_retained_bip39_seed , zero , sizeof ( _retained_bip39_seed ) )) {
157
+ if (MEMEQ (bip39_seed_out , zero , 64 )) {
99
158
return false;
100
159
}
101
- memcpy (bip39_seed_out , _retained_bip39_seed , sizeof (_retained_bip39_seed ));
102
160
return true;
103
161
}
104
162
@@ -319,6 +377,67 @@ static void _free_string(char** str)
319
377
wally_free_string (* str );
320
378
}
321
379
380
+ USE_RESULT static keystore_error_t _retain_seed (const uint8_t * seed , size_t seed_len )
381
+ {
382
+ random_32_bytes (_unstretched_retained_seed_encryption_key );
383
+ uint8_t retained_seed_encryption_key [32 ] = {0 };
384
+ UTIL_CLEANUP_32 (retained_seed_encryption_key );
385
+ keystore_error_t result = _stretch_retained_seed_encryption_key (
386
+ _unstretched_retained_seed_encryption_key ,
387
+ "keystore_retained_seed_access_in" ,
388
+ "keystore_retained_seed_access_out" ,
389
+ retained_seed_encryption_key );
390
+ if (result != KEYSTORE_OK ) {
391
+ return result ;
392
+ }
393
+ size_t len = seed_len + 64 ;
394
+ if (!cipher_aes_hmac_encrypt (
395
+ seed , seed_len , _retained_seed_encrypted , & len , retained_seed_encryption_key )) {
396
+ return KEYSTORE_ERR_ENCRYPT ;
397
+ }
398
+ _retained_seed_encrypted_len = len ;
399
+ return KEYSTORE_OK ;
400
+ }
401
+
402
+ USE_RESULT static bool _retain_bip39_seed (const uint8_t * bip39_seed )
403
+ {
404
+ random_32_bytes (_unstretched_retained_bip39_seed_encryption_key );
405
+ uint8_t retained_bip39_seed_encryption_key [32 ] = {0 };
406
+ UTIL_CLEANUP_32 (retained_bip39_seed_encryption_key );
407
+ if (_stretch_retained_seed_encryption_key (
408
+ _unstretched_retained_bip39_seed_encryption_key ,
409
+ "keystore_retained_bip39_seed_access_in" ,
410
+ "keystore_retained_bip39_seed_access_out" ,
411
+ retained_bip39_seed_encryption_key ) != KEYSTORE_OK ) {
412
+ return false;
413
+ }
414
+ size_t len = sizeof (_retained_bip39_seed_encrypted );
415
+ if (!cipher_aes_hmac_encrypt (
416
+ bip39_seed ,
417
+ 64 ,
418
+ _retained_bip39_seed_encrypted ,
419
+ & len ,
420
+ retained_bip39_seed_encryption_key )) {
421
+ return false;
422
+ }
423
+ _retained_bip39_seed_encrypted_len = len ;
424
+ return true;
425
+ }
426
+
427
+ static void _delete_retained_seeds (void )
428
+ {
429
+ util_zero (
430
+ _unstretched_retained_seed_encryption_key ,
431
+ sizeof (_unstretched_retained_seed_encryption_key ));
432
+ util_zero (_retained_seed_encrypted , sizeof (_retained_seed_encrypted ));
433
+ _retained_seed_encrypted_len = 0 ;
434
+ util_zero (
435
+ _unstretched_retained_bip39_seed_encryption_key ,
436
+ sizeof (_unstretched_retained_seed_encryption_key ));
437
+ util_zero (_retained_bip39_seed_encrypted , sizeof (_retained_bip39_seed_encrypted ));
438
+ _retained_bip39_seed_encrypted_len = 0 ;
439
+ }
440
+
322
441
keystore_error_t keystore_unlock (
323
442
const char * password ,
324
443
uint8_t * remaining_attempts_out ,
@@ -350,12 +469,19 @@ keystore_error_t keystore_unlock(
350
469
if (result == KEYSTORE_OK ) {
351
470
if (_is_unlocked_device ) {
352
471
// Already unlocked. Fail if the seed changed under our feet (should never happen).
353
- if (seed_len != _seed_length || !MEMEQ (_retained_seed , seed , _seed_length )) {
472
+ uint8_t current_seed [KEYSTORE_MAX_SEED_LENGTH ] = {0 };
473
+ size_t current_seed_len = 0 ;
474
+ if (!keystore_copy_seed (current_seed , & current_seed_len )) {
475
+ return KEYSTORE_ERR_DECRYPT ;
476
+ }
477
+ if (seed_len != current_seed_len || !MEMEQ (current_seed , seed , current_seed_len )) {
354
478
Abort ("Seed has suddenly changed. This should never happen." );
355
479
}
356
480
} else {
357
- memcpy (_retained_seed , seed , seed_len );
358
- _seed_length = seed_len ;
481
+ keystore_error_t retain_seed_result = _retain_seed (seed , seed_len );
482
+ if (retain_seed_result != KEYSTORE_OK ) {
483
+ return retain_seed_result ;
484
+ }
359
485
_is_unlocked_device = true;
360
486
}
361
487
bitbox02_smarteeprom_reset_unlock_attempts ();
@@ -396,7 +522,9 @@ bool keystore_unlock_bip39(const char* mnemonic_passphrase)
396
522
mnemonic , mnemonic_passphrase , bip39_seed , sizeof (bip39_seed ), NULL ) != WALLY_OK ) {
397
523
return false;
398
524
}
399
- memcpy (_retained_bip39_seed , bip39_seed , sizeof (bip39_seed ));
525
+ if (!_retain_bip39_seed (bip39_seed )) {
526
+ return false;
527
+ }
400
528
_is_unlocked_bip39 = true;
401
529
return true;
402
530
}
@@ -405,9 +533,7 @@ void keystore_lock(void)
405
533
{
406
534
_is_unlocked_device = false;
407
535
_is_unlocked_bip39 = false;
408
- _seed_length = 0 ;
409
- util_zero (_retained_seed , sizeof (_retained_seed ));
410
- util_zero (_retained_bip39_seed , sizeof (_retained_bip39_seed ));
536
+ _delete_retained_seeds ();
411
537
}
412
538
413
539
bool keystore_is_locked (void )
@@ -789,3 +915,33 @@ bool keystore_secp256k1_schnorr_bip86_sign(
789
915
}
790
916
return secp256k1_schnorrsig_verify (ctx , sig64_out , msg32 , 32 , & pubkey ) == 1 ;
791
917
}
918
+
919
+ #ifdef TESTING
920
+ void keystore_mock_unlocked (const uint8_t * seed , size_t seed_len , const uint8_t * bip39_seed )
921
+ {
922
+ _is_unlocked_device = seed != NULL ;
923
+ if (seed != NULL ) {
924
+ if (_retain_seed (seed , seed_len ) != KEYSTORE_OK ) {
925
+ Abort ("couldn't retain seed" );
926
+ }
927
+ }
928
+ _is_unlocked_bip39 = bip39_seed != NULL ;
929
+ if (bip39_seed != NULL ) {
930
+ if (!_retain_bip39_seed (bip39_seed )) {
931
+ Abort ("couldn't retain bip39 seed" );
932
+ }
933
+ }
934
+ }
935
+
936
+ const uint8_t * keystore_test_get_retained_seed_encrypted (size_t * len_out )
937
+ {
938
+ * len_out = _retained_seed_encrypted_len ;
939
+ return _retained_seed_encrypted ;
940
+ }
941
+
942
+ const uint8_t * keystore_test_get_retained_bip39_seed_encrypted (size_t * len_out )
943
+ {
944
+ * len_out = _retained_bip39_seed_encrypted_len ;
945
+ return _retained_bip39_seed_encrypted ;
946
+ }
947
+ #endif
0 commit comments