|
14 | 14 |
|
15 | 15 | #include <string.h> |
16 | 16 |
|
17 | | -#include "cipher/cipher.h" |
18 | 17 | #include "hardfault.h" |
19 | 18 | #include "keystore.h" |
20 | 19 | #include "memory/bitbox02_smarteeprom.h" |
|
28 | 27 | #include <rust/rust.h> |
29 | 28 | #include <secp256k1_ecdsa_s2c.h> |
30 | 29 |
|
31 | | -// Unlocking the keystore take longer than the 500ms watchdog we have setup. Reset the watchdog |
32 | | -// counter to (~7s) to avoid incorrectly assuming we lost communication with the app. |
33 | | -#define LONG_TIMEOUT (-70) |
34 | | - |
35 | | -/** |
36 | | - * We allow seeds of 16, 24 or 32 bytes. |
37 | | - */ |
38 | | -static bool _validate_seed_length(size_t seed_len) |
39 | | -{ |
40 | | - return seed_len == 16 || seed_len == 24 || seed_len == 32; |
41 | | -} |
42 | | - |
43 | | -/** |
44 | | - * Retrieves the encrypted seed and attempts to decrypt it using the password. |
45 | | - * |
46 | | - * `securechip_result_out`, if not NULL, will contain the error code from `securechip_kdf()` if |
47 | | - * there was a secure chip error, and 0 otherwise. |
48 | | - */ |
49 | | -static keystore_error_t _get_and_decrypt_seed( |
50 | | - const char* password, |
51 | | - uint8_t* decrypted_seed_out, |
52 | | - size_t* decrypted_seed_len_out, |
53 | | - int* securechip_result_out) |
54 | | -{ |
55 | | - uint8_t encrypted_seed_and_hmac[96]; |
56 | | - UTIL_CLEANUP_32(encrypted_seed_and_hmac); |
57 | | - uint8_t encrypted_len; |
58 | | - if (!memory_get_encrypted_seed_and_hmac(encrypted_seed_and_hmac, &encrypted_len)) { |
59 | | - return KEYSTORE_ERR_MEMORY; |
60 | | - } |
61 | | - uint8_t secret[32]; |
62 | | - UTIL_CLEANUP_32(secret); |
63 | | - int stretch_result = securechip_stretch_password(password, secret); |
64 | | - if (stretch_result) { |
65 | | - if (stretch_result == SC_ERR_INCORRECT_PASSWORD) { |
66 | | - // Our Optiga securechip implementation fails password stretching if the password is |
67 | | - // wrong, so we can early-abort here. The ATECC stretches the password without checking |
68 | | - // if the password is correct, and we determine if it is correct in the seed decryption |
69 | | - // step below. |
70 | | - return KEYSTORE_ERR_INCORRECT_PASSWORD; |
71 | | - } |
72 | | - if (securechip_result_out != NULL) { |
73 | | - *securechip_result_out = stretch_result; |
74 | | - } |
75 | | - return KEYSTORE_ERR_SECURECHIP; |
76 | | - } |
77 | | - if (encrypted_len < 49) { |
78 | | - Abort("_get_and_decrypt_seed: underflow / zero size"); |
79 | | - } |
80 | | - size_t decrypted_len = encrypted_len - 48; |
81 | | - uint8_t decrypted[decrypted_len]; |
82 | | - bool password_correct = cipher_aes_hmac_decrypt( |
83 | | - encrypted_seed_and_hmac, encrypted_len, decrypted, &decrypted_len, secret); |
84 | | - if (!password_correct) { |
85 | | - return KEYSTORE_ERR_INCORRECT_PASSWORD; |
86 | | - } |
87 | | - if (!_validate_seed_length(decrypted_len)) { |
88 | | - util_zero(decrypted, sizeof(decrypted)); |
89 | | - return KEYSTORE_ERR_SEED_SIZE; |
90 | | - } |
91 | | - *decrypted_seed_len_out = decrypted_len; |
92 | | - memcpy(decrypted_seed_out, decrypted, decrypted_len); |
93 | | - |
94 | | - return KEYSTORE_OK; |
95 | | -} |
96 | | - |
97 | | -USE_RESULT static keystore_error_t _retain_seed(const uint8_t* seed, size_t seed_len) |
98 | | -{ |
99 | | - if (!rust_keystore_retain_seed(rust_util_bytes(seed, seed_len))) { |
100 | | - return KEYSTORE_ERR_STRETCH_RETAINED_SEED_KEY; |
101 | | - } |
102 | | - return KEYSTORE_OK; |
103 | | -} |
104 | | - |
105 | | -keystore_error_t keystore_unlock( |
106 | | - const char* password, |
107 | | - int* securechip_result_out, |
108 | | - uint8_t* seed_out, |
109 | | - size_t* seed_len_out) |
110 | | -{ |
111 | | - if (!memory_is_seeded()) { |
112 | | - return KEYSTORE_ERR_UNSEEDED; |
113 | | - } |
114 | | - uint8_t failed_attempts = bitbox02_smarteeprom_get_unlock_attempts(); |
115 | | - if (failed_attempts >= MAX_UNLOCK_ATTEMPTS) { |
116 | | - /* |
117 | | - * We reset the device as soon as the MAX_UNLOCK_ATTEMPTSth attempt |
118 | | - * is made. So we should never enter this branch... |
119 | | - * This is just an extraordinary measure for added resilience. |
120 | | - */ |
121 | | - reset_reset(false); |
122 | | - return KEYSTORE_ERR_MAX_ATTEMPTS_EXCEEDED; |
123 | | - } |
124 | | - usb_processing_timeout_reset(LONG_TIMEOUT); |
125 | | - |
126 | | - bitbox02_smarteeprom_increment_unlock_attempts(); |
127 | | - uint8_t seed[KEYSTORE_MAX_SEED_LENGTH] = {0}; |
128 | | - UTIL_CLEANUP_32(seed); |
129 | | - size_t seed_len; |
130 | | - keystore_error_t result = |
131 | | - _get_and_decrypt_seed(password, seed, &seed_len, securechip_result_out); |
132 | | - if (result != KEYSTORE_OK) { |
133 | | - failed_attempts = bitbox02_smarteeprom_get_unlock_attempts(); |
134 | | - |
135 | | - if (failed_attempts >= MAX_UNLOCK_ATTEMPTS) { |
136 | | - reset_reset(false); |
137 | | - return KEYSTORE_ERR_MAX_ATTEMPTS_EXCEEDED; |
138 | | - } |
139 | | - |
140 | | - return result; |
141 | | - } |
142 | | - |
143 | | - if (rust_keystore_is_unlocked_device()) { |
144 | | - // Already unlocked. Fail if the seed changed under our feet (should never happen). |
145 | | - if (!rust_keystore_check_retained_seed(rust_util_bytes(seed, seed_len))) { |
146 | | - Abort("Seed has suddenly changed. This should never happen."); |
147 | | - } |
148 | | - } else { |
149 | | - keystore_error_t retain_seed_result = _retain_seed(seed, seed_len); |
150 | | - if (retain_seed_result != KEYSTORE_OK) { |
151 | | - return retain_seed_result; |
152 | | - } |
153 | | - } |
154 | | - bitbox02_smarteeprom_reset_unlock_attempts(); |
155 | | - |
156 | | - if (seed_out != NULL && seed_len_out != NULL) { |
157 | | - memcpy(seed_out, seed, seed_len); |
158 | | - *seed_len_out = seed_len; |
159 | | - } |
160 | | - return KEYSTORE_OK; |
161 | | -} |
162 | | - |
163 | 30 | bool keystore_get_bip39_word_stack(uint16_t idx, char* word_out, size_t word_out_size) |
164 | 31 | { |
165 | 32 | return rust_get_bip39_word(idx, rust_util_bytes_mut((uint8_t*)word_out, word_out_size)); |
|
0 commit comments