From 486f58d1c5df6115daf776893720d2c4709dfdc4 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 5 Oct 2023 17:04:45 +0200 Subject: [PATCH] feat(storage): Add PIN health check. --- core/embed/trezorhal/optiga.h | 4 ++ storage/storage.c | 97 ++++++++++++++++++++++++----------- 2 files changed, 71 insertions(+), 30 deletions(-) diff --git a/core/embed/trezorhal/optiga.h b/core/embed/trezorhal/optiga.h index 59828a9cb3..59f7d577fa 100644 --- a/core/embed/trezorhal/optiga.h +++ b/core/embed/trezorhal/optiga.h @@ -46,6 +46,10 @@ // optiga_pin_verify(). #define OPTIGA_PIN_DERIVE_MS 1200 +// The number of milliseconds it takes to execute optiga_pin_verify() for an +// invalid PIN. +#define OPTIGA_PIN_DERIVE_INVALID_MS 400 + typedef secbool (*OPTIGA_UI_PROGRESS)(uint32_t elapsed_ms); int __wur optiga_sign(uint8_t index, const uint8_t *digest, size_t digest_size, diff --git a/storage/storage.c b/storage/storage.c index 130d399600..cf2380a9c0 100644 --- a/storage/storage.c +++ b/storage/storage.c @@ -18,6 +18,7 @@ */ #include +#include #include #include "chacha20poly1305/rfc7539.h" @@ -182,7 +183,7 @@ static secbool storage_set_encrypted(const uint16_t key, const void *val, static secbool storage_get_encrypted(const uint16_t key, void *val_dest, const uint16_t max_len, uint16_t *len); static secbool decrypt_dek(const uint8_t *pin, size_t pin_len, - const uint8_t *ext_salt); + const uint8_t *ext_salt, int pbkdf2_iterations); // The number of milliseconds required to compute PBKDF2. static int pbkdf2_ms(int pbkdf2_iterations) { @@ -190,10 +191,15 @@ static int pbkdf2_ms(int pbkdf2_iterations) { } // The number of milliseconds required to derive the KEK and KEIV. -static int pin_derive_ms(int pbkdf2_iterations) { +static int pin_derive_ms(bool pin_valid, int pbkdf2_iterations) { + (void)pin_valid; int time_ms = pbkdf2_ms(pbkdf2_iterations); #if USE_OPTIGA - time_ms += OPTIGA_PIN_DERIVE_MS; + if (pin_valid) { + time_ms += OPTIGA_PIN_DERIVE_MS; + } else { + time_ms += OPTIGA_PIN_DERIVE_INVALID_MS; + } #endif return time_ms; } @@ -531,7 +537,7 @@ static void pbkdf2_update_progressive(PBKDF2_HMAC_SHA256_CTX *ctx, #if !USE_OPTIGA static void derive_kek(const uint8_t *pin, size_t pin_len, const uint8_t *storage_salt, const uint8_t *ext_salt, - uint8_t kek[SHA256_DIGEST_LENGTH], + int pbkdf2_iterations, uint8_t kek[SHA256_DIGEST_LENGTH], uint8_t keiv[SHA256_DIGEST_LENGTH]) { uint8_t salt[HARDWARE_SALT_SIZE + STORAGE_SALT_SIZE + EXTERNAL_SALT_SIZE] = { 0}; @@ -550,11 +556,11 @@ static void derive_kek(const uint8_t *pin, size_t pin_len, PBKDF2_HMAC_SHA256_CTX ctx = {0}; pbkdf2_hmac_sha256_Init(&ctx, pin, pin_len, salt, salt_len, 1); - pbkdf2_update_progressive(&ctx, PBKDF2_ITER_COUNT / 2); + pbkdf2_update_progressive(&ctx, pbkdf2_iterations / 2); pbkdf2_hmac_sha256_Final(&ctx, kek); pbkdf2_hmac_sha256_Init(&ctx, pin, pin_len, salt, salt_len, 2); - pbkdf2_update_progressive(&ctx, PBKDF2_ITER_COUNT / 2); + pbkdf2_update_progressive(&ctx, pbkdf2_iterations / 2); pbkdf2_hmac_sha256_Final(&ctx, keiv); memzero(&ctx, sizeof(PBKDF2_HMAC_SHA256_CTX)); @@ -565,7 +571,7 @@ static void derive_kek(const uint8_t *pin, size_t pin_len, #if USE_OPTIGA static void stretch_pin_optiga(const uint8_t *pin, size_t pin_len, const uint8_t storage_salt[STORAGE_SALT_SIZE], - const uint8_t *ext_salt, + const uint8_t *ext_salt, int pbkdf2_iterations, uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) { // Combining the PIN with the storage salt aims to ensure that if the // MCU-Optiga communication is compromised, then a user with a low-entropy PIN @@ -593,7 +599,7 @@ static void stretch_pin_optiga(const uint8_t *pin, size_t pin_len, PBKDF2_HMAC_SHA256_CTX ctx = {0}; pbkdf2_hmac_sha256_Init(&ctx, pin, pin_len, salt, salt_len, 1); memzero(&salt, sizeof(salt)); - pbkdf2_update_progressive(&ctx, PBKDF2_ITER_COUNT); + pbkdf2_update_progressive(&ctx, pbkdf2_iterations); pbkdf2_hmac_sha256_Final(&ctx, stretched_pin); memzero(&ctx, sizeof(ctx)); @@ -622,12 +628,14 @@ static void derive_kek_optiga( static secbool __wur derive_kek_set(const uint8_t *pin, size_t pin_len, const uint8_t *storage_salt, const uint8_t *ext_salt, + int pbkdf2_iterations, uint8_t kek[SHA256_DIGEST_LENGTH], uint8_t keiv[SHA256_DIGEST_LENGTH]) { #if USE_OPTIGA uint8_t optiga_secret[OPTIGA_PIN_SECRET_SIZE] = {0}; uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE] = {0}; - stretch_pin_optiga(pin, pin_len, storage_salt, ext_salt, stretched_pin); + stretch_pin_optiga(pin, pin_len, storage_salt, ext_salt, pbkdf2_iterations, + stretched_pin); int ret = optiga_pin_set(ui_progress, stretched_pin, optiga_secret); memzero(stretched_pin, sizeof(stretched_pin)); if (ret != OPTIGA_SUCCESS) { @@ -637,7 +645,8 @@ static secbool __wur derive_kek_set(const uint8_t *pin, size_t pin_len, derive_kek_optiga(optiga_secret, kek, keiv); memzero(optiga_secret, sizeof(optiga_secret)); #else - derive_kek(pin, pin_len, storage_salt, ext_salt, kek, keiv); + derive_kek(pin, pin_len, storage_salt, ext_salt, pbkdf2_iterations, kek, + keiv); #endif return sectrue; } @@ -645,12 +654,14 @@ static secbool __wur derive_kek_set(const uint8_t *pin, size_t pin_len, static secbool __wur derive_kek_unlock(const uint8_t *pin, size_t pin_len, const uint8_t *storage_salt, const uint8_t *ext_salt, + int pbkdf2_iterations, uint8_t kek[SHA256_DIGEST_LENGTH], uint8_t keiv[SHA256_DIGEST_LENGTH]) { #if USE_OPTIGA uint8_t optiga_secret[OPTIGA_PIN_SECRET_SIZE] = {0}; uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE] = {0}; - stretch_pin_optiga(pin, pin_len, storage_salt, ext_salt, stretched_pin); + stretch_pin_optiga(pin, pin_len, storage_salt, ext_salt, pbkdf2_iterations, + stretched_pin); int ret = optiga_pin_verify(ui_progress, stretched_pin, optiga_secret); memzero(stretched_pin, sizeof(stretched_pin)); if (ret != OPTIGA_SUCCESS) { @@ -667,13 +678,14 @@ static secbool __wur derive_kek_unlock(const uint8_t *pin, size_t pin_len, derive_kek_optiga(optiga_secret, kek, keiv); memzero(optiga_secret, sizeof(optiga_secret)); #else - derive_kek(pin, pin_len, storage_salt, ext_salt, kek, keiv); + derive_kek(pin, pin_len, storage_salt, ext_salt, pbkdf2_iterations, kek, + keiv); #endif return sectrue; } static secbool set_pin(const uint8_t *pin, size_t pin_len, - const uint8_t *ext_salt) { + const uint8_t *ext_salt, int pbkdf2_iterations) { // Encrypt the cached keys using the new PIN and set the new PVC. uint8_t buffer[STORAGE_SALT_SIZE + KEYS_SIZE + POLY1305_TAG_SIZE] = {0}; uint8_t *rand_salt = buffer; @@ -685,7 +697,8 @@ static secbool set_pin(const uint8_t *pin, size_t pin_len, chacha20poly1305_ctx ctx = {0}; random_buffer(rand_salt, STORAGE_SALT_SIZE); ui_progress(0); - ensure(derive_kek_set(pin, pin_len, rand_salt, ext_salt, kek, keiv), + ensure(derive_kek_set(pin, pin_len, rand_salt, ext_salt, pbkdf2_iterations, + kek, keiv), "derive_kek_set failed"); rfc7539_init(&ctx, kek, keiv); memzero(kek, sizeof(kek)); @@ -826,10 +839,32 @@ static void init_wiped_storage(void) { ensure(set_wipe_code(WIPE_CODE_EMPTY, WIPE_CODE_EMPTY_LEN), "set_wipe_code failed"); - ui_total = PIN_DERIVE_MS; + ui_total = pin_derive_ms(true, PBKDF2_ITER_COUNT); ui_rem = ui_total; ui_message = PROCESSING_MSG; - ensure(set_pin(PIN_EMPTY, PIN_EMPTY_LEN, NULL), "init_pin failed"); + +#if PYOPT + // Health check to make sure that an invalid PIN does not unlock and the valid + // PIN does. Skip in debug builds to avoid excessive wear of Optiga counters. + ui_total += 2 * pin_derive_ms(true, 2) + pin_derive_ms(false, 2); + ui_rem = ui_total; + const uint8_t PIN_TEST_GOOD[] = {0x01}; + const uint8_t PIN_TEST_BAD[] = {0x02}; + if (set_pin(PIN_TEST_GOOD, sizeof(PIN_TEST_GOOD), NULL, 2) != sectrue) { + norcow_wipe(); + ensure(secfalse, "pin_health_check_0 failed"); + } + if (decrypt_dek(PIN_TEST_BAD, sizeof(PIN_TEST_BAD), NULL, 2) != secfalse) { + norcow_wipe(); + ensure(secfalse, "pin_health_check_1 failed"); + } + if (decrypt_dek(PIN_TEST_GOOD, sizeof(PIN_TEST_GOOD), NULL, 2) != sectrue) { + norcow_wipe(); + ensure(secfalse, "pin_health_check_2 failed"); + } +#endif + ensure(set_pin(PIN_EMPTY, PIN_EMPTY_LEN, NULL, PBKDF2_ITER_COUNT), + "init_pin failed"); } void storage_init(PIN_UI_WAIT_CALLBACK callback, const uint8_t *salt, @@ -1140,7 +1175,7 @@ secbool check_storage_version(void) { } static secbool decrypt_dek(const uint8_t *pin, size_t pin_len, - const uint8_t *ext_salt) { + const uint8_t *ext_salt, int pbkdf2_iterations) { // Read the random salt from EDEK_PVC_KEY and use it to derive the KEK and // KEIV from the PIN. const void *buffer = NULL; @@ -1161,8 +1196,8 @@ static secbool decrypt_dek(const uint8_t *pin, size_t pin_len, uint8_t kek[SHA256_DIGEST_LENGTH] = {0}; uint8_t keiv[SHA256_DIGEST_LENGTH] = {0}; - if (sectrue != - derive_kek_unlock(pin, pin_len, rand_salt, ext_salt, kek, keiv)) { + if (sectrue != derive_kek_unlock(pin, pin_len, rand_salt, ext_salt, + pbkdf2_iterations, kek, keiv)) { memzero(kek, sizeof(kek)); memzero(keiv, sizeof(keiv)); return secfalse; @@ -1209,8 +1244,8 @@ static secbool unlock(const uint8_t *pin, size_t pin_len, // storage_upgrade_unlocked(). uint32_t legacy_pin = 0; if (get_lock_version() <= 2) { - ui_total += pin_derive_ms(PBKDF2_ITER_COUNT); - ui_rem += pin_derive_ms(PBKDF2_ITER_COUNT); + ui_total += pin_derive_ms(true, PBKDF2_ITER_COUNT); + ui_rem += pin_derive_ms(true, PBKDF2_ITER_COUNT); legacy_pin = pin_to_int(pin, pin_len); unlock_pin = (const uint8_t *)&legacy_pin; unlock_pin_len = sizeof(legacy_pin); @@ -1264,7 +1299,8 @@ static secbool unlock(const uint8_t *pin, size_t pin_len, } // Check whether the entered PIN is correct. - if (sectrue != decrypt_dek(unlock_pin, unlock_pin_len, ext_salt)) { + if (sectrue != + decrypt_dek(unlock_pin, unlock_pin_len, ext_salt, PBKDF2_ITER_COUNT)) { memzero(&legacy_pin, sizeof(legacy_pin)); // Wipe storage if too many failures wait_random(); @@ -1308,7 +1344,7 @@ secbool storage_unlock(const uint8_t *pin, size_t pin_len, return secfalse; } - ui_total = pin_derive_ms(PBKDF2_ITER_COUNT); + ui_total = pin_derive_ms(true, PBKDF2_ITER_COUNT); ui_rem = ui_total; if (pin_len == 0) { if (ui_message == NULL) { @@ -1603,7 +1639,7 @@ secbool storage_change_pin(const uint8_t *oldpin, size_t oldpin_len, return secfalse; } - ui_total = 2 * pin_derive_ms(PBKDF2_ITER_COUNT); + ui_total = 2 * pin_derive_ms(true, PBKDF2_ITER_COUNT); ui_rem = ui_total; ui_message = (oldpin_len != 0 && newpin_len == 0) ? VERIFYING_PIN_MSG : PROCESSING_MSG; @@ -1617,7 +1653,7 @@ secbool storage_change_pin(const uint8_t *oldpin, size_t oldpin_len, return secfalse; } - return set_pin(newpin, newpin_len, new_ext_salt); + return set_pin(newpin, newpin_len, new_ext_salt, PBKDF2_ITER_COUNT); } void storage_ensure_not_wipe_code(const uint8_t *pin, size_t pin_len) { @@ -1652,7 +1688,7 @@ secbool storage_change_wipe_code(const uint8_t *pin, size_t pin_len, return secfalse; } - ui_total = pin_derive_ms(PBKDF2_ITER_COUNT); + ui_total = pin_derive_ms(true, PBKDF2_ITER_COUNT); ui_rem = ui_total; ui_message = (pin_len != 0 && wipe_code_len == 0) ? VERIFYING_PIN_MSG : PROCESSING_MSG; @@ -1810,14 +1846,15 @@ static secbool storage_upgrade(void) { } // Set EDEK_PVC_KEY and PIN_NOT_SET_KEY. - ui_total = pin_derive_ms(PBKDF2_ITER_COUNT); + ui_total = pin_derive_ms(true, PBKDF2_ITER_COUNT); ui_rem = ui_total; ui_message = PROCESSING_MSG; secbool found = norcow_get(V0_PIN_KEY, &val, &len); if (sectrue == found && *(const uint32_t *)val != V0_PIN_EMPTY) { - set_pin((const uint8_t *)val, len, NULL); + set_pin((const uint8_t *)val, len, NULL, PBKDF2_ITER_COUNT); } else { - set_pin((const uint8_t *)&V0_PIN_EMPTY, sizeof(V0_PIN_EMPTY), NULL); + set_pin((const uint8_t *)&V0_PIN_EMPTY, sizeof(V0_PIN_EMPTY), NULL, + PBKDF2_ITER_COUNT); ret = norcow_set(PIN_NOT_SET_KEY, &TRUE_BYTE, sizeof(TRUE_BYTE)); } @@ -1900,7 +1937,7 @@ static secbool storage_upgrade_unlocked(const uint8_t *pin, size_t pin_len, if (version <= 2) { // Upgrade EDEK_PVC_KEY from the old uint32 PIN scheme to the new // variable-length PIN scheme. - if (sectrue != set_pin(pin, pin_len, ext_salt)) { + if (sectrue != set_pin(pin, pin_len, ext_salt, PBKDF2_ITER_COUNT)) { return secfalse; } }