Skip to content

Commit c247526

Browse files
committed
Merge branch 'unlock'
2 parents e7c4c5e + 0cc8e36 commit c247526

File tree

14 files changed

+140
-81
lines changed

14 files changed

+140
-81
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
## [Unreleased]
4+
- Display granular error codes when unlock fails unexpectedly
45

56
## 9.6.0 [released 2021-05-20]
67
- Attempt to fix flaky SD behavior

CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ endif()
8989
#
9090
# Versions MUST contain three parts and start with lowercase 'v'.
9191
# Example 'v1.0.0'. They MUST not contain a pre-release label such as '-beta'.
92-
set(FIRMWARE_VERSION "v9.6.0")
93-
set(FIRMWARE_BTC_ONLY_VERSION "v9.6.0")
94-
set(FIRMWARE_BITBOXBASE_VERSION "v9.6.0")
92+
set(FIRMWARE_VERSION "v9.6.1")
93+
set(FIRMWARE_BTC_ONLY_VERSION "v9.6.1")
94+
set(FIRMWARE_BITBOXBASE_VERSION "v9.6.1")
9595
set(BOOTLOADER_VERSION "v1.0.3")
9696

9797
find_package(PythonInterp 3.6 REQUIRED)

src/keystore.c

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,27 @@ static uint8_t* _get_bip39_seed(void)
108108
return _retained_bip39_seed;
109109
}
110110

111-
static bool _stretch_password(const char* password, uint8_t* kdf_out)
111+
/**
112+
* Stretch the user password using the securechip, putting the result in `kdf_out`, which must be 32
113+
* bytes. `securechip_result_out`, if not NULL, will contain the error code from `securechip_kdf()`
114+
* if there was a secure chip error, and 0 otherwise.
115+
*/
116+
static keystore_error_t _stretch_password(
117+
const char* password,
118+
uint8_t* kdf_out,
119+
int* securechip_result_out)
112120
{
121+
if (securechip_result_out != NULL) {
122+
*securechip_result_out = 0;
123+
}
113124
uint8_t password_salted_hashed[32] = {0};
114125
UTIL_CLEANUP_32(password_salted_hashed);
115126
if (!salt_hash_data(
116127
(const uint8_t*)password,
117128
strlen(password),
118129
"keystore_seed_access_in",
119130
password_salted_hashed)) {
120-
return false;
131+
return KEYSTORE_ERR_SALT;
121132
}
122133

123134
uint8_t kdf_in[32] = {0};
@@ -126,14 +137,22 @@ static bool _stretch_password(const char* password, uint8_t* kdf_out)
126137

127138
// First KDF on SECURECHIP_SLOT_ROLLKEY increments the monotonic
128139
// counter. Call only once!
129-
if (!securechip_kdf(SECURECHIP_SLOT_ROLLKEY, kdf_in, 32, kdf_out)) {
130-
return false;
140+
int securechip_result = securechip_kdf(SECURECHIP_SLOT_ROLLKEY, kdf_in, 32, kdf_out);
141+
if (securechip_result) {
142+
if (securechip_result_out != NULL) {
143+
*securechip_result_out = securechip_result;
144+
}
145+
return KEYSTORE_ERR_SC_KDF;
131146
}
132147
// Second KDF does not use the counter and we call it multiple times.
133148
for (int i = 0; i < KDF_NUM_ITERATIONS; i++) {
134149
memcpy(kdf_in, kdf_out, 32);
135-
if (!securechip_kdf(SECURECHIP_SLOT_KDF, kdf_in, 32, kdf_out)) {
136-
return false;
150+
securechip_result = securechip_kdf(SECURECHIP_SLOT_KDF, kdf_in, 32, kdf_out);
151+
if (securechip_result) {
152+
if (securechip_result_out != NULL) {
153+
*securechip_result_out = securechip_result;
154+
}
155+
return KEYSTORE_ERR_SC_KDF;
137156
}
138157
}
139158

@@ -142,50 +161,59 @@ static bool _stretch_password(const char* password, uint8_t* kdf_out)
142161
strlen(password),
143162
"keystore_seed_access_out",
144163
password_salted_hashed)) {
145-
return false;
164+
return KEYSTORE_ERR_SALT;
146165
}
147166
if (wally_hmac_sha256(
148167
password_salted_hashed, sizeof(password_salted_hashed), kdf_out, 32, kdf_out, 32) !=
149168
WALLY_OK) {
150-
return false;
169+
return KEYSTORE_ERR_HASH;
151170
}
152171

153-
return true;
172+
return KEYSTORE_OK;
154173
}
155-
static bool _get_and_decrypt_seed(
174+
175+
/**
176+
* Retrieves the encrypted seed and attempts to decrypt it using the password.
177+
*
178+
* `securechip_result_out`, if not NULL, will contain the error code from `securechip_kdf()` if
179+
* there was a secure chip error, and 0 otherwise.
180+
*/
181+
static keystore_error_t _get_and_decrypt_seed(
156182
const char* password,
157183
uint8_t* decrypted_seed_out,
158184
size_t* decrypted_seed_len_out,
159-
bool* password_correct_out)
185+
int* securechip_result_out)
160186
{
161187
uint8_t encrypted_seed_and_hmac[96];
162188
UTIL_CLEANUP_32(encrypted_seed_and_hmac);
163189
uint8_t encrypted_len;
164190
if (!memory_get_encrypted_seed_and_hmac(encrypted_seed_and_hmac, &encrypted_len)) {
165-
return false;
191+
return KEYSTORE_ERR_MEMORY;
166192
}
167193
uint8_t secret[32];
168194
UTIL_CLEANUP_32(secret);
169-
if (!_stretch_password(password, secret)) {
170-
return false;
195+
keystore_error_t result = _stretch_password(password, secret, securechip_result_out);
196+
if (result != KEYSTORE_OK) {
197+
return result;
171198
}
172199
if (encrypted_len < 49) {
173200
Abort("_get_and_decrypt_seed: underflow / zero size");
174201
}
175202
size_t decrypted_len = encrypted_len - 48;
176203
uint8_t decrypted[decrypted_len];
177-
*password_correct_out = cipher_aes_hmac_decrypt(
204+
bool password_correct = cipher_aes_hmac_decrypt(
178205
encrypted_seed_and_hmac, encrypted_len, decrypted, &decrypted_len, secret);
179-
if (*password_correct_out) {
180-
if (!_validate_seed_length(decrypted_len)) {
181-
util_zero(decrypted, sizeof(decrypted));
182-
return false;
183-
}
184-
*decrypted_seed_len_out = decrypted_len;
185-
memcpy(decrypted_seed_out, decrypted, decrypted_len);
206+
if (!password_correct) {
207+
return KEYSTORE_ERR_INCORRECT_PASSWORD;
186208
}
187-
util_zero(decrypted, sizeof(decrypted));
188-
return true;
209+
if (!_validate_seed_length(decrypted_len)) {
210+
util_zero(decrypted, sizeof(decrypted));
211+
return KEYSTORE_ERR_SEED_SIZE;
212+
}
213+
*decrypted_seed_len_out = decrypted_len;
214+
memcpy(decrypted_seed_out, decrypted, decrypted_len);
215+
216+
return KEYSTORE_OK;
189217
}
190218

191219
static bool _verify_seed(
@@ -196,11 +224,7 @@ static bool _verify_seed(
196224
uint8_t decrypted_seed[KEYSTORE_MAX_SEED_LENGTH] = {0};
197225
size_t seed_len;
198226
UTIL_CLEANUP_32(decrypted_seed);
199-
bool password_correct = false;
200-
if (!_get_and_decrypt_seed(password, decrypted_seed, &seed_len, &password_correct)) {
201-
return false;
202-
}
203-
if (!password_correct) {
227+
if (_get_and_decrypt_seed(password, decrypted_seed, &seed_len, NULL) != KEYSTORE_OK) {
204228
return false;
205229
}
206230
if (expected_seed_len != seed_len) {
@@ -232,7 +256,7 @@ bool keystore_encrypt_and_store_seed(
232256
}
233257
uint8_t secret[32] = {0};
234258
UTIL_CLEANUP_32(secret);
235-
if (!_stretch_password(password, secret)) {
259+
if (_stretch_password(password, secret, NULL) != KEYSTORE_OK) {
236260
return false;
237261
}
238262

@@ -299,10 +323,13 @@ static void _free_string(char** str)
299323
wally_free_string(*str);
300324
}
301325

302-
keystore_error_t keystore_unlock(const char* password, uint8_t* remaining_attempts_out)
326+
keystore_error_t keystore_unlock(
327+
const char* password,
328+
uint8_t* remaining_attempts_out,
329+
int* securechip_result_out)
303330
{
304331
if (!memory_is_seeded()) {
305-
return KEYSTORE_ERR_GENERIC;
332+
return KEYSTORE_ERR_UNSEEDED;
306333
}
307334
uint8_t failed_attempts = bitbox02_smarteeprom_get_unlock_attempts();
308335
if (failed_attempts >= MAX_UNLOCK_ATTEMPTS) {
@@ -319,11 +346,12 @@ keystore_error_t keystore_unlock(const char* password, uint8_t* remaining_attemp
319346
uint8_t seed[KEYSTORE_MAX_SEED_LENGTH] = {0};
320347
UTIL_CLEANUP_32(seed);
321348
size_t seed_len;
322-
bool password_correct = false;
323-
if (!_get_and_decrypt_seed(password, seed, &seed_len, &password_correct)) {
324-
return KEYSTORE_ERR_GENERIC;
349+
keystore_error_t result =
350+
_get_and_decrypt_seed(password, seed, &seed_len, securechip_result_out);
351+
if (result != KEYSTORE_OK && result != KEYSTORE_ERR_INCORRECT_PASSWORD) {
352+
return result;
325353
}
326-
if (password_correct) {
354+
if (result == KEYSTORE_OK) {
327355
if (_is_unlocked_device) {
328356
// Already unlocked. Fail if the seed changed under our feet (should never happen).
329357
if (seed_len != _seed_length || !MEMEQ(_retained_seed, seed, _seed_length)) {
@@ -346,7 +374,7 @@ keystore_error_t keystore_unlock(const char* password, uint8_t* remaining_attemp
346374
}
347375

348376
*remaining_attempts_out = MAX_UNLOCK_ATTEMPTS - failed_attempts;
349-
return password_correct ? KEYSTORE_OK : KEYSTORE_ERR_INCORRECT_PASSWORD;
377+
return result;
350378
}
351379

352380
bool keystore_unlock_bip39(const char* mnemonic_passphrase)

src/keystore.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ typedef enum {
3636
KEYSTORE_OK,
3737
KEYSTORE_ERR_INCORRECT_PASSWORD,
3838
KEYSTORE_ERR_MAX_ATTEMPTS_EXCEEDED,
39-
KEYSTORE_ERR_GENERIC,
39+
KEYSTORE_ERR_UNSEEDED,
40+
KEYSTORE_ERR_MEMORY,
41+
KEYSTORE_ERR_SC_KDF,
42+
KEYSTORE_ERR_SEED_SIZE,
43+
KEYSTORE_ERR_SALT,
44+
KEYSTORE_ERR_HASH,
4045
} keystore_error_t;
4146

4247
#ifdef TESTING
@@ -89,15 +94,15 @@ USE_RESULT bool keystore_create_and_store_seed(
8994
* If it is false, the keystore is not unlocked.
9095
* @param[out] remaining_attempts_out will have the number of remaining attempts.
9196
* If zero, the keystore is locked until the device is reset.
97+
* @param[out] securechip_result_out, if not NULL, will contain the error code from
98+
* `securechip_kdf()` if there was a secure chip error, and 0 otherwise.
9299
* @return
93100
* - KEYSTORE_OK if they keystore was successfully unlocked
94-
* - KEYSTORE_ERR_INCORRECT_PASSWORD if the password was wrong
95-
* - KEYSTORE_ERR_MAX_ATTEMPTS_EXCEEDED if there were too many unsuccessful attempts. The device is
96-
* reset in this case.
97-
* - KEYSTORE_ERR_GENERIC if there was a fatal memory error.
101+
* - KEYSTORE_ERR_* if unsuccessful.
98102
* Only call this if memory_is_seeded() returns true.
99103
*/
100-
USE_RESULT keystore_error_t keystore_unlock(const char* password, uint8_t* remaining_attempts_out);
104+
USE_RESULT keystore_error_t
105+
keystore_unlock(const char* password, uint8_t* remaining_attempts_out, int* securechip_result_out);
101106

102107
/** Unlocks the bip39 seed.
103108
* @param[in] mnemonic_passphrase bip39 passphrase used in the derivation. Use the

src/rust/bitbox02-rust/src/hww/api/error.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
use crate::pb;
1616

17+
use crate::workflow::unlock::UnlockError;
18+
1719
#[allow(dead_code)]
1820
#[derive(Debug, PartialEq)]
1921
pub enum Error {
@@ -67,11 +69,11 @@ impl core::convert::From<crate::workflow::verify_message::Error> for Error {
6769
}
6870
}
6971

70-
impl core::convert::From<crate::workflow::unlock::UnlockError> for Error {
71-
fn from(error: crate::workflow::unlock::UnlockError) -> Self {
72+
impl core::convert::From<UnlockError> for Error {
73+
fn from(error: UnlockError) -> Self {
7274
match error {
73-
crate::workflow::unlock::UnlockError::UserAbort => Error::UserAbort,
74-
crate::workflow::unlock::UnlockError::IncorrectPassword => Error::Generic,
75+
UnlockError::UserAbort => Error::UserAbort,
76+
UnlockError::IncorrectPassword | UnlockError::Generic => Error::Generic,
7577
}
7678
}
7779
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ async fn confirm_mnemonic_passphrase(passphrase: &str) -> Result<(), confirm::Us
5353
pub enum UnlockError {
5454
UserAbort,
5555
IncorrectPassword,
56+
Generic,
5657
}
5758

5859
impl core::convert::From<super::cancel::Error> for UnlockError {
@@ -84,7 +85,11 @@ pub async fn unlock_keystore(
8485
status(&msg, false).await;
8586
Err(UnlockError::IncorrectPassword)
8687
}
87-
_ => panic!("keystore unlock failed"),
88+
Err(err) => {
89+
let msg = format!("keystore unlock failed\n{:?}", err);
90+
status(&msg, false).await;
91+
Err(UnlockError::Generic)
92+
}
8893
}
8994
}
9095

src/rust/bitbox02/src/keystore.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,38 @@ pub fn is_locked() -> bool {
3535
pub enum Error {
3636
CannotUnlockBIP39,
3737
IncorrectPassword { remaining_attempts: u8 },
38+
MaxAttemptsExceeded,
39+
Unseeded,
40+
Memory,
41+
// Securechip error with the error code from securechip.c
42+
ScKdf(i32),
43+
Salt,
44+
Hash,
45+
SeedSize,
3846
Unknown,
3947
}
4048

4149
pub fn unlock(password: &SafeInputString) -> Result<(), Error> {
4250
let mut remaining_attempts: u8 = 0;
43-
match unsafe { bitbox02_sys::keystore_unlock(password.as_cstr(), &mut remaining_attempts) } {
51+
let mut securechip_result: i32 = 0;
52+
match unsafe {
53+
bitbox02_sys::keystore_unlock(
54+
password.as_cstr(),
55+
&mut remaining_attempts,
56+
&mut securechip_result,
57+
)
58+
} {
4459
keystore_error_t::KEYSTORE_OK => Ok(()),
4560
keystore_error_t::KEYSTORE_ERR_INCORRECT_PASSWORD => {
4661
Err(Error::IncorrectPassword { remaining_attempts })
4762
}
48-
keystore_error_t::KEYSTORE_ERR_MAX_ATTEMPTS_EXCEEDED => Err(Error::Unknown),
49-
keystore_error_t::KEYSTORE_ERR_GENERIC => Err(Error::Unknown),
63+
keystore_error_t::KEYSTORE_ERR_MAX_ATTEMPTS_EXCEEDED => Err(Error::MaxAttemptsExceeded),
64+
keystore_error_t::KEYSTORE_ERR_UNSEEDED => Err(Error::Unseeded),
65+
keystore_error_t::KEYSTORE_ERR_MEMORY => Err(Error::Memory),
66+
keystore_error_t::KEYSTORE_ERR_SEED_SIZE => Err(Error::SeedSize),
67+
keystore_error_t::KEYSTORE_ERR_SC_KDF => Err(Error::ScKdf(securechip_result)),
68+
keystore_error_t::KEYSTORE_ERR_SALT => Err(Error::Salt),
69+
keystore_error_t::KEYSTORE_ERR_HASH => Err(Error::Hash),
5070
}
5171
}
5272

src/securechip/securechip.c

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -513,18 +513,18 @@ bool securechip_update_keys(void)
513513
return _update_kdf_key() == ATCA_SUCCESS;
514514
}
515515

516-
bool securechip_kdf(securechip_slot_t slot, const uint8_t* msg, size_t len, uint8_t* kdf_out)
516+
int securechip_kdf(securechip_slot_t slot, const uint8_t* msg, size_t len, uint8_t* kdf_out)
517517
{
518518
if (len > 127 || (slot != SECURECHIP_SLOT_ROLLKEY && slot != SECURECHIP_SLOT_KDF)) {
519-
return false;
519+
return SC_ERR_INVALID_ARGS;
520520
}
521521
if (msg == kdf_out) {
522-
return false;
522+
return SC_ERR_INVALID_ARGS;
523523
}
524524

525525
ATCA_STATUS result = _authorize_key();
526526
if (result != ATCA_SUCCESS) {
527-
return false;
527+
return result;
528528
}
529529

530530
uint8_t nonce_out[32] = {0};
@@ -555,7 +555,7 @@ bool securechip_kdf(securechip_slot_t slot, const uint8_t* msg, size_t len, uint
555555
/* kdf_out, */
556556
/* nonce_out); */
557557
if (result != ATCA_SUCCESS) {
558-
return false;
558+
return result;
559559
}
560560
// Output is encrypted with the io protection key.
561561
uint8_t io_protection_key[32] = {0};
@@ -567,11 +567,7 @@ bool securechip_kdf(securechip_slot_t slot, const uint8_t* msg, size_t len, uint
567567
.data = kdf_out,
568568
.data_size = 32,
569569
};
570-
result = atcah_io_decrypt(&io_dec_params);
571-
if (result != ATCA_SUCCESS) {
572-
return false;
573-
}
574-
return true;
570+
return atcah_io_decrypt(&io_dec_params);
575571
}
576572

577573
bool securechip_gen_attestation_key(uint8_t* pubkey_out)

0 commit comments

Comments
 (0)