diff --git a/core/embed/trezorhal/optiga.h b/core/embed/trezorhal/optiga.h index e196a65bb9..2335313672 100644 --- a/core/embed/trezorhal/optiga.h +++ b/core/embed/trezorhal/optiga.h @@ -69,8 +69,12 @@ int __wur optiga_pin_verify_v4(OPTIGA_UI_PROGRESS ui_progress, const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]); +int __wur optiga_pin_get_fails_v4(uint32_t *ctr); + int __wur optiga_pin_get_fails(uint32_t *ctr); +int __wur optiga_pin_fails_increase_v4(uint32_t count); + int __wur optiga_pin_fails_increase(uint32_t count); #endif diff --git a/core/embed/trezorhal/optiga/optiga.c b/core/embed/trezorhal/optiga/optiga.c index b3cde1b419..7fb5084aba 100644 --- a/core/embed/trezorhal/optiga/optiga.c +++ b/core/embed/trezorhal/optiga/optiga.c @@ -26,20 +26,29 @@ #include "rand.h" #include "storage.h" -// PIN counter reset key / Master secret (OID 0xF1D0). +// Counter-protected PIN secret and reset key for OID_STRETCHED_PIN_CTR (OID +// 0xF1D0). #define OID_PIN_SECRET (OPTIGA_OID_DATA + 0) -// PIN counter (OID 0xE120). -#define OID_PIN_COUNTER (OPTIGA_OID_COUNTER + 0) - -// PIN stretching counter (OID 0xE121). -#define OID_PIN_STRETCH_COUNTER (OPTIGA_OID_COUNTER + 1) - -// Stretched PIN (OID 0xF1D4). +// Digest of the stretched PIN (OID 0xF1D4). #define OID_STRETCHED_PIN (OPTIGA_OID_DATA + 4) -// Key for HMAC-SHA256 PIN stretching step (OID 0xF1D1). -#define OID_PIN_HMAC (OPTIGA_OID_DATA + 1) +// Counter-protected key for HMAC-SHA256 PIN stretching step (OID 0xF1D5). +#define OID_PIN_HMAC (OPTIGA_OID_DATA + 8) + +// Counter which limits the guesses at OID_STRETCHED_PIN (OID 0xE120). +#define OID_STRETCHED_PIN_CTR (OPTIGA_OID_COUNTER + 0) + +// Counter which limits the use of OID_PIN_HMAC (OID 0xE122). +#define OID_PIN_HMAC_CTR (OPTIGA_OID_COUNTER + 2) + +// Counter which limits the total number of PIN stretching operations over the +// lifetime of the device (OID 0xE121). +#define OID_PIN_TOTAL_CTR (OPTIGA_OID_COUNTER + 1) + +// Key for HMAC-SHA256 PIN stretching step used in storage version 3 and 4 (OID +// 0xF1D1). +#define OID_PIN_HMAC_V4 (OPTIGA_OID_DATA + 1) // Key for AES-CMAC PIN stretching step (OID 0xE200). #define OID_PIN_CMAC OPTIGA_OID_SYM_KEY @@ -47,20 +56,17 @@ // Key for ECDH PIN stretching step (OID 0xE0F3). #define OID_PIN_ECDH (OPTIGA_OID_ECC_KEY + 3) -// The number of times the stretching is repeated in each PIN processing phase -// for legacy storage version 3 and 4. -#define PIN_STRETCH_ITERATIONS_V4 1 - // The number of times that PIN stretching is repeated. #define PIN_STRETCH_ITERATIONS 2 // Value of the PIN counter when it is reset. static const uint8_t COUNTER_RESET[] = {0, 0, 0, 0, 0, 0, 0, PIN_MAX_TRIES}; -// Value of the PIN stretching counter when it is initialized. The limit is -// 600000 stretching operations, which equates to -// 100000 / PIN_STRETCH_ITERATIONS unlock operations. -static const uint8_t STRETCH_COUNTER_INIT[] = {0, 0, 0, 0, 0, 0x09, 0x27, 0xC0}; +// Initial value of the counter which limits the total number of PIN stretching +// operations. The limit is 600000 stretching operations, which equates to +// 300000 / PIN_STRETCH_ITERATIONS unlock operations over the lifetime of the +// device. +static const uint8_t PIN_TOTAL_CTR_INIT[] = {0, 0, 0, 0, 0, 0x09, 0x27, 0xC0}; static const optiga_metadata_item TYPE_AUTOREF = OPTIGA_META_VALUE(OPTIGA_DATA_TYPE_AUTOREF); @@ -70,10 +76,12 @@ static const optiga_metadata_item ACCESS_STRETCHED_PIN = OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_AUTO, OID_STRETCHED_PIN); static const optiga_metadata_item ACCESS_PIN_SECRET = OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_AUTO, OID_PIN_SECRET); -static const optiga_metadata_item ACCESS_PIN_COUNTER = - OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_LUC, OID_PIN_COUNTER); -static const optiga_metadata_item ACCESS_PIN_STRETCH_COUNTER = - OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_LUC, OID_PIN_STRETCH_COUNTER); +static const optiga_metadata_item ACCESS_STRETCHED_PIN_CTR = + OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_LUC, OID_STRETCHED_PIN_CTR); +static const optiga_metadata_item ACCESS_PIN_TOTAL_CTR = + OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_LUC, OID_PIN_TOTAL_CTR); +static const optiga_metadata_item ACCESS_PIN_HMAC_CTR = + OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_LUC, OID_PIN_HMAC_CTR); // Size of the DER BIT STRING header required for inputs to optiga_calc_ssec(). #define BIT_STRING_HEADER_SIZE 3 @@ -244,7 +252,7 @@ bool optiga_set_metadata(uint16_t oid, const optiga_metadata *metadata) { static bool optiga_pin_init_metadata(void) { optiga_metadata metadata = {0}; - // Set metadata for PIN counter reset key / Master secret. + // Set metadata for counter-protected PIN secret. memzero(&metadata, sizeof(metadata)); metadata.change = OPTIGA_META_ACCESS_ALWAYS; metadata.read = ACCESS_STRETCHED_PIN; @@ -254,43 +262,64 @@ static bool optiga_pin_init_metadata(void) { return false; } - // Set metadata for PIN counter. + // Set metadata for stretched PIN. + memzero(&metadata, sizeof(metadata)); + metadata.change = ACCESS_PIN_SECRET; + metadata.read = OPTIGA_META_ACCESS_NEVER; + metadata.execute = ACCESS_STRETCHED_PIN_CTR; + metadata.data_type = TYPE_AUTOREF; + if (!optiga_set_metadata(OID_STRETCHED_PIN, &metadata)) { + return false; + } + + // Set metadata for HMAC-SHA256 PIN stretching secret. + memzero(&metadata, sizeof(metadata)); + metadata.change = ACCESS_STRETCHED_PIN; + metadata.read = OPTIGA_META_ACCESS_NEVER; + metadata.execute = ACCESS_PIN_HMAC_CTR; + metadata.data_type = TYPE_PRESSEC; + if (!optiga_set_metadata(OID_PIN_HMAC, &metadata)) { + return false; + } + + // Set metadata for the counter of guesses at OID_STRETCHED_PIN. memzero(&metadata, sizeof(metadata)); metadata.change = ACCESS_PIN_SECRET; metadata.read = OPTIGA_META_ACCESS_ALWAYS; metadata.execute = OPTIGA_META_ACCESS_ALWAYS; - if (!optiga_set_metadata(OID_PIN_COUNTER, &metadata)) { + if (!optiga_set_metadata(OID_STRETCHED_PIN_CTR, &metadata)) { return false; } - // Initialize the PIN stretching counter if write access is possible. + // Set metadata for the counter of OID_PIN_HMAC uses. + memzero(&metadata, sizeof(metadata)); + metadata.change = ACCESS_STRETCHED_PIN; + metadata.read = OPTIGA_META_ACCESS_ALWAYS; + metadata.execute = OPTIGA_META_ACCESS_ALWAYS; + if (!optiga_set_metadata(OID_PIN_HMAC_CTR, &metadata)) { + return false; + } + + // Initialize the counter of the total number of PIN stretching operations, if + // write access is possible. memzero(&metadata, sizeof(metadata)); metadata.change = OPTIGA_META_ACCESS_ALWAYS; - if (write_metadata(OID_PIN_STRETCH_COUNTER, &metadata)) { - optiga_result res = optiga_set_data_object(OID_PIN_STRETCH_COUNTER, false, - STRETCH_COUNTER_INIT, - sizeof(STRETCH_COUNTER_INIT)); + if (write_metadata(OID_PIN_TOTAL_CTR, &metadata)) { + optiga_result res = + optiga_set_data_object(OID_PIN_TOTAL_CTR, false, PIN_TOTAL_CTR_INIT, + sizeof(PIN_TOTAL_CTR_INIT)); if (res != OPTIGA_SUCCESS) { return false; } } - // Set metadata for PIN stretching counter. + // Set metadata for the counter of the total number of PIN stretching + // operations. memzero(&metadata, sizeof(metadata)); metadata.change = OPTIGA_META_ACCESS_NEVER; metadata.read = OPTIGA_META_ACCESS_ALWAYS; metadata.execute = OPTIGA_META_ACCESS_ALWAYS; - if (!optiga_set_metadata(OID_PIN_STRETCH_COUNTER, &metadata)) { - return false; - } - - // Set metadata for stretched PIN. - memzero(&metadata, sizeof(metadata)); - metadata.change = ACCESS_PIN_SECRET; - metadata.read = OPTIGA_META_ACCESS_NEVER; - metadata.execute = ACCESS_PIN_COUNTER; - metadata.data_type = TYPE_AUTOREF; - if (!optiga_set_metadata(OID_STRETCHED_PIN, &metadata)) { + if (!optiga_set_metadata(OID_PIN_TOTAL_CTR, &metadata)) { return false; } @@ -298,7 +327,7 @@ static bool optiga_pin_init_metadata(void) { memzero(&metadata, sizeof(metadata)); metadata.change = OPTIGA_META_ACCESS_ALWAYS; metadata.read = OPTIGA_META_ACCESS_NEVER; - metadata.execute = ACCESS_PIN_STRETCH_COUNTER; + metadata.execute = ACCESS_PIN_TOTAL_CTR; metadata.key_usage = OPTIGA_META_KEY_USE_ENC; if (!optiga_set_metadata(OID_PIN_CMAC, &metadata)) { return false; @@ -308,41 +337,12 @@ static bool optiga_pin_init_metadata(void) { memzero(&metadata, sizeof(metadata)); metadata.change = OPTIGA_META_ACCESS_ALWAYS; metadata.read = OPTIGA_META_ACCESS_NEVER; - metadata.execute = ACCESS_PIN_STRETCH_COUNTER; + metadata.execute = ACCESS_PIN_TOTAL_CTR; metadata.key_usage = OPTIGA_META_KEY_USE_KEYAGREE; if (!optiga_set_metadata(OID_PIN_ECDH, &metadata)) { return false; } - // Generate and store the HMAC PIN stretching secret in OID_PIN_HMAC, if write - // access is possible. - memzero(&metadata, sizeof(metadata)); - metadata.change = OPTIGA_META_ACCESS_ALWAYS; - if (write_metadata(OID_PIN_HMAC, &metadata)) { - uint8_t secret[OPTIGA_PIN_SECRET_SIZE] = {0}; - optiga_result res = optiga_get_random(secret, sizeof(secret)); - if (res != OPTIGA_SUCCESS) { - return false; - } - random_xor(secret, sizeof(secret)); - - res = optiga_set_data_object(OID_PIN_HMAC, false, secret, sizeof(secret)); - memzero(secret, sizeof(secret)); - if (res != OPTIGA_SUCCESS) { - return false; - } - } - - // Set metadata for HMAC-SHA256 PIN stretching secret. - memzero(&metadata, sizeof(metadata)); - metadata.change = OPTIGA_META_ACCESS_NEVER; - metadata.read = OPTIGA_META_ACCESS_NEVER; - metadata.execute = ACCESS_PIN_STRETCH_COUNTER; - metadata.data_type = TYPE_PRESSEC; - if (!optiga_set_metadata(OID_PIN_HMAC, &metadata)) { - return false; - } - return true; } @@ -369,9 +369,10 @@ static int optiga_pin_init_stretch(void) { static int optiga_pin_stretch_common( OPTIGA_UI_PROGRESS ui_progress, HMAC_SHA256_CTX *ctx, - const uint8_t input[OPTIGA_PIN_SECRET_SIZE]) { - // Implements the functionality that is common to optiga_pin_stretch_secret() - // and the legacy function optiga_pin_stretch_secret_v4(). + const uint8_t input[OPTIGA_PIN_SECRET_SIZE], bool version4) { + // Implements the functionality that is common to + // optiga_pin_stretch_cmac_ecdh() and the legacy function + // optiga_pin_stretch_secret_v4(). uint8_t buffer[ENCRYPT_SYM_PREFIX_SIZE + OPTIGA_PIN_SECRET_SIZE] = {0}; size_t size = 0; @@ -387,16 +388,18 @@ static int optiga_pin_stretch_common( hmac_sha256_Update(ctx, buffer, size); - // Combine intermediate result with OID_PIN_HMAC - res = - optiga_encrypt_sym(OPTIGA_SYM_MODE_HMAC_SHA256, OID_PIN_HMAC, input, - OPTIGA_PIN_SECRET_SIZE, buffer, sizeof(buffer), &size); - if (res != OPTIGA_SUCCESS) { - memzero(buffer, sizeof(buffer)); - return res; - } + if (version4) { + // Combine intermediate result with OID_PIN_HMAC + res = optiga_encrypt_sym(OPTIGA_SYM_MODE_HMAC_SHA256, OID_PIN_HMAC_V4, + input, OPTIGA_PIN_SECRET_SIZE, buffer, + sizeof(buffer), &size); + if (res != OPTIGA_SUCCESS) { + memzero(buffer, sizeof(buffer)); + return res; + } - hmac_sha256_Update(ctx, buffer, size); + hmac_sha256_Update(ctx, buffer, size); + } ui_progress(200); @@ -435,39 +438,27 @@ static int optiga_pin_stretch_secret_v4( // in each attempt. // Pseudocode for the stretching process: - // result_0 = secret - // for i in range(PIN_STRETCH_ITERATIONS_V4): - // cmac_i = CMAC(optiga_cmac_key, result_i) - // hmac_i = HMAC(optiga_hmac_key, result_i) - // ecdh_i = ECDH(optiga_ecdh_key, result_i) - // result_{i+1} = HMAC-SHA256(secret, cmac_i || hmac_i || ecdh_i) - // secret = result_{PIN_STRETCH_ITERATIONS_V4} + // cmac_out = CMAC(OID_PIN_CMAC, secret) + // hmac_out = HMAC(OID_PIN_HMAC_V4, secret) + // ecdh_out = ECDH(OID_PIN_ECDH, secret) + // secret = HMAC-SHA256(secret, cmac_out || hmac_out || ecdh_out) HMAC_SHA256_CTX ctx = {0}; + hmac_sha256_Init(&ctx, secret, OPTIGA_PIN_SECRET_SIZE); - uint8_t result[OPTIGA_PIN_SECRET_SIZE] = {0}; - memcpy(result, secret, sizeof(result)); - - for (int i = 0; i < PIN_STRETCH_ITERATIONS_V4; ++i) { - hmac_sha256_Init(&ctx, secret, OPTIGA_PIN_SECRET_SIZE); - - optiga_result res = optiga_pin_stretch_common(ui_progress, &ctx, result); - if (res != OPTIGA_SUCCESS) { - memzero(result, sizeof(result)); - memzero(&ctx, sizeof(ctx)); - return res; - } - - hmac_sha256_Final(&ctx, result); + optiga_result res = + optiga_pin_stretch_common(ui_progress, &ctx, secret, true); + if (res != OPTIGA_SUCCESS) { + memzero(&ctx, sizeof(ctx)); + return res; } - memcpy(secret, result, sizeof(result)); - memzero(result, sizeof(result)); + hmac_sha256_Final(&ctx, secret); memzero(&ctx, sizeof(ctx)); return OPTIGA_SUCCESS; } -static int optiga_pin_stretch_secret( +static int optiga_pin_stretch_cmac_ecdh( OPTIGA_UI_PROGRESS ui_progress, uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) { // This step hardens the PIN verification process in case an attacker is able @@ -478,15 +469,14 @@ static int optiga_pin_stretch_secret( // search for the PIN. Thus it reduces the number of PIN values that the // attacker can test in a unit of time by forcing them to involve the Optiga // in each attempt, and restricts the overall number of attempts using - // OID_PIN_STRETCH_COUNTER. + // OID_PIN_TOTAL_CTR. // Pseudocode for the stretching process: - // for i in range(PIN_STRETCH_ITERATIONS): - // digest = HMAC(secret, "") - // cmac_i = CMAC(optiga_cmac_key, digest) - // hmac_i = HMAC(optiga_hmac_key, digest) - // ecdh_i = ECDH(optiga_ecdh_key, digest) - // secret = HMAC-SHA256(secret, cmac_i || hmac_i || ecdh_i) + // for _ in range(PIN_STRETCH_ITERATIONS): + // digest = HMAC-SHA256(stretched_pin, "") + // cmac_out = CMAC(OID_PIN_CMAC, digest) + // ecdh_out = ECDH(OID_PIN_ECDH, digest) + // stretched_pin = HMAC-SHA256(stretched_pin, cmac_out || ecdh_out) uint8_t digest[OPTIGA_PIN_SECRET_SIZE] = {0}; HMAC_SHA256_CTX ctx = {0}; @@ -498,7 +488,8 @@ static int optiga_pin_stretch_secret( hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, NULL, 0, digest); hmac_sha256_Init(&ctx, stretched_pin, OPTIGA_PIN_SECRET_SIZE); - optiga_result res = optiga_pin_stretch_common(ui_progress, &ctx, digest); + optiga_result res = + optiga_pin_stretch_common(ui_progress, &ctx, digest, false); if (res != OPTIGA_SUCCESS) { memzero(digest, sizeof(digest)); memzero(&ctx, sizeof(ctx)); @@ -515,13 +506,15 @@ static int optiga_pin_stretch_secret( int optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress, uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) { + int res = OPTIGA_SUCCESS; if (!optiga_pin_init_metadata()) { - return -1; + res = -1; + goto end; } - optiga_result res = optiga_pin_init_stretch(); + res = optiga_pin_init_stretch(); if (res != OPTIGA_SUCCESS) { - return res; + goto end; } ui_progress(200); @@ -530,43 +523,110 @@ int optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress, // ensures that if an attacker extracts the value of OID_STRETCHED_PIN or // OID_PIN_SECRET, then it cannot be used to conduct an offline brute-force // search for the PIN. - res = optiga_pin_stretch_secret(ui_progress, stretched_pin); + res = optiga_pin_stretch_cmac_ecdh(ui_progress, stretched_pin); if (res != OPTIGA_SUCCESS) { - return res; + goto end; } // Generate and store the counter-protected PIN secret. uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE] = {0}; res = optiga_get_random(pin_secret, sizeof(pin_secret)); if (res != OPTIGA_SUCCESS) { - return res; + goto end; } random_xor(pin_secret, sizeof(pin_secret)); res = optiga_set_data_object(OID_PIN_SECRET, false, pin_secret, sizeof(pin_secret)); if (res != OPTIGA_SUCCESS) { - return res; + goto end; } - // Authorise using OID_PIN_SECRET so that we can write to OID_PIN_COUNTER and - // OID_STRETCHED_PIN. + // Generate the key for the HMAC-SHA256 PIN stretching step. + uint8_t pin_hmac[OPTIGA_PIN_SECRET_SIZE] = {0}; + res = optiga_get_random(pin_hmac, sizeof(pin_hmac)); + if (res != OPTIGA_SUCCESS) { + goto end; + } + random_xor(pin_hmac, sizeof(pin_hmac)); + + // Authorise using OID_PIN_SECRET so that we can write to OID_STRETCHED_PIN + // and OID_STRETCHED_PIN_CTR. res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_PIN_SECRET, pin_secret, sizeof(pin_secret)); if (res != OPTIGA_SUCCESS) { - return res; + goto end; } + // Process the stretched PIN using a one-way function before using it in the + // operation that will be executed in Optiga during verification. This ensures + // that in the unlikely case of an attacker recording communication between + // the MCU and Optiga, they will not gain knowledge of the stretched PIN. + uint8_t digest[OPTIGA_PIN_SECRET_SIZE] = {0}; + hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, NULL, 0, digest); + + // Compute the operation that will be executed in Optiga during verification. + uint8_t hmac_buffer[ENCRYPT_SYM_PREFIX_SIZE + OPTIGA_PIN_SECRET_SIZE] = { + 0x61, 0x00, 0x20}; + hmac_sha256(pin_hmac, sizeof(pin_hmac), digest, sizeof(digest), + &hmac_buffer[ENCRYPT_SYM_PREFIX_SIZE]); + + // Stretch the PIN with the result. + hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, hmac_buffer, + sizeof(hmac_buffer), stretched_pin); + // Process the stretched PIN using a one-way function before sending it to the // Optiga. This ensures that in the unlikely case of an attacker recording // communication between the MCU and Optiga, they will not gain knowledge of // the stretched PIN. - uint8_t digest[OPTIGA_PIN_SECRET_SIZE] = {0}; hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, NULL, 0, digest); // Store the digest of the stretched PIN in OID_STRETCHED_PIN. res = optiga_set_data_object(OID_STRETCHED_PIN, false, digest, sizeof(digest)); + if (res != OPTIGA_SUCCESS) { + goto end; + } + + // Initialize the counter which limits the guesses at OID_STRETCHED_PIN so + // that we can authorise using OID_STRETCHED_PIN. + res = optiga_set_data_object(OID_STRETCHED_PIN_CTR, false, COUNTER_RESET, + sizeof(COUNTER_RESET)); + if (res != OPTIGA_SUCCESS) { + goto end; + } + + // Authorise using OID_STRETCHED_PIN so that we can write to OID_PIN_HMAC and + // OID_PIN_HMAC_CTR. + res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_STRETCHED_PIN, digest, + sizeof(digest)); + if (res != OPTIGA_SUCCESS) { + goto end; + } + + // Initialize the key for HMAC-SHA256 PIN stretching. + res = optiga_set_data_object(OID_PIN_HMAC, false, pin_hmac, sizeof(pin_hmac)); + if (res != OPTIGA_SUCCESS) { + goto end; + } + + // Initialize the counter which limits the guesses at OID_STRETCHED_PIN again, + // since we just depleted one attempt. + res = optiga_set_data_object(OID_STRETCHED_PIN_CTR, false, COUNTER_RESET, + sizeof(COUNTER_RESET)); + optiga_clear_auto_state(OID_PIN_SECRET); + if (res != OPTIGA_SUCCESS) { + goto end; + } + + // Initialize the PIN counter which limits the use of OID_PIN_HMAC. + res = optiga_set_data_object(OID_PIN_HMAC_CTR, false, COUNTER_RESET, + sizeof(COUNTER_RESET)); + if (res != OPTIGA_SUCCESS) { + goto end; + } + + ui_progress(200); // Stretch the PIN more with the counter-protected PIN secret. This method // ensures that if the user chooses a high-entropy PIN, then even if the @@ -575,24 +635,18 @@ int optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress, // integrated into the device in the first place. hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, pin_secret, sizeof(pin_secret), stretched_pin); + if (res != OPTIGA_SUCCESS) { + goto end; + } + +end: + memzero(hmac_buffer, sizeof(hmac_buffer)); + memzero(pin_hmac, sizeof(pin_hmac)); memzero(pin_secret, sizeof(pin_secret)); memzero(digest, sizeof(digest)); - if (res != OPTIGA_SUCCESS) { - optiga_clear_auto_state(OID_PIN_SECRET); - return res; - } - - // Initialize the PIN counter. - res = optiga_set_data_object(OID_PIN_COUNTER, false, COUNTER_RESET, - sizeof(COUNTER_RESET)); optiga_clear_auto_state(OID_PIN_SECRET); - if (res != OPTIGA_SUCCESS) { - return res; - } - - ui_progress(200); - - return OPTIGA_SUCCESS; + optiga_clear_auto_state(OID_STRETCHED_PIN); + return res; } int optiga_pin_verify_v4(OPTIGA_UI_PROGRESS ui_progress, @@ -648,14 +702,6 @@ int optiga_pin_verify_v4(OPTIGA_UI_PROGRESS ui_progress, return res; } - // Reset the PIN counter. - res = optiga_set_data_object(OID_PIN_COUNTER, false, COUNTER_RESET, - sizeof(COUNTER_RESET)); - optiga_clear_auto_state(OID_PIN_SECRET); - if (res != OPTIGA_SUCCESS) { - return res; - } - ui_progress(200); // Combine the value of OID_PIN_SECRET with the PIN-derived secret and @@ -674,10 +720,47 @@ int optiga_pin_verify_v4(OPTIGA_UI_PROGRESS ui_progress, return OPTIGA_SUCCESS; } +static int optiga_pin_stretch_hmac( + uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) { + // Process the stretched PIN using a one-way function before sending it to the + // Optiga. + uint8_t digest[OPTIGA_PIN_SECRET_SIZE] = {0}; + hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, NULL, 0, digest); + + // HMAC the digest with the key in OID_PIN_HMAC. + uint8_t hmac_buffer[ENCRYPT_SYM_PREFIX_SIZE + OPTIGA_PIN_SECRET_SIZE] = {0}; + size_t size = 0; + optiga_result res = optiga_encrypt_sym( + OPTIGA_SYM_MODE_HMAC_SHA256, OID_PIN_HMAC, digest, sizeof(digest), + hmac_buffer, sizeof(hmac_buffer), &size); + memzero(digest, sizeof(digest)); + if (res != OPTIGA_SUCCESS) { + uint8_t error_code = 0; + optiga_get_error_code(&error_code); + if (error_code + OPTIGA_COMMAND_ERROR_OFFSET == + OPTIGA_ERR_ACCESS_COND_NOT_SAT) { + return OPTIGA_ERR_COUNTER_EXCEEDED; + } else { + return error_code + OPTIGA_COMMAND_ERROR_OFFSET; + } + } + + // Stretch the PIN with the result. + hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, hmac_buffer, size, + stretched_pin); + memzero(hmac_buffer, sizeof(hmac_buffer)); + return OPTIGA_SUCCESS; +} + int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress, uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) { // Stretch the PIN more with stretching secrets from the Optiga. - optiga_result res = optiga_pin_stretch_secret(ui_progress, stretched_pin); + optiga_result res = optiga_pin_stretch_cmac_ecdh(ui_progress, stretched_pin); + if (res != OPTIGA_SUCCESS) { + return res; + } + + res = optiga_pin_stretch_hmac(stretched_pin); if (res != OPTIGA_SUCCESS) { return res; } @@ -687,7 +770,8 @@ int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress, uint8_t digest[OPTIGA_PIN_SECRET_SIZE] = {0}; hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, NULL, 0, digest); - // Authorise using OID_STRETCHED_PIN so that we can read from OID_PIN_SECRET. + // Authorise using OID_STRETCHED_PIN so that we can read from OID_PIN_SECRET + // and reset OID_PIN_HMAC_CTR. res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_STRETCHED_PIN, digest, sizeof(digest)); memzero(digest, sizeof(digest)); @@ -697,10 +781,16 @@ int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress, return error_code + OPTIGA_COMMAND_ERROR_OFFSET; } + // Reset the counter which limits the use of OID_PIN_HMAC. + res = optiga_set_data_object(OID_PIN_HMAC_CTR, false, COUNTER_RESET, + sizeof(COUNTER_RESET)); if (res != OPTIGA_SUCCESS) { + optiga_clear_auto_state(OID_STRETCHED_PIN); return res; } + ui_progress(200); + // Read the counter-protected PIN secret from OID_PIN_SECRET. uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE] = {0}; size_t size = 0; @@ -711,26 +801,20 @@ int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress, return res; } - if (size != OPTIGA_PIN_SECRET_SIZE) { - return OPTIGA_ERR_SIZE; - } - // Stretch the PIN more with the counter-protected PIN secret. - hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, pin_secret, - OPTIGA_PIN_SECRET_SIZE, stretched_pin); + hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, pin_secret, size, + stretched_pin); - ui_progress(200); - - // Authorise using OID_PIN_SECRET so that we can write to OID_PIN_COUNTER. + // Authorise using OID_PIN_SECRET so that we can reset OID_STRETCHED_PIN_CTR. res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_PIN_SECRET, - pin_secret, OPTIGA_PIN_SECRET_SIZE); + pin_secret, sizeof(pin_secret)); memzero(pin_secret, sizeof(pin_secret)); if (res != OPTIGA_SUCCESS) { return res; } - // Reset the PIN counter. - res = optiga_set_data_object(OID_PIN_COUNTER, false, COUNTER_RESET, + // Reset the counter which limits the guesses at OID_STRETCHED_PIN. + res = optiga_set_data_object(OID_STRETCHED_PIN_CTR, false, COUNTER_RESET, sizeof(COUNTER_RESET)); optiga_clear_auto_state(OID_PIN_SECRET); if (res != OPTIGA_SUCCESS) { @@ -763,8 +847,43 @@ static int optiga_get_counter(uint16_t oid, uint32_t *ctr) { return OPTIGA_SUCCESS; } +int optiga_pin_get_fails_v4(uint32_t *ctr) { + return optiga_get_counter(OID_STRETCHED_PIN_CTR, ctr); +} + int optiga_pin_get_fails(uint32_t *ctr) { - return optiga_get_counter(OID_PIN_COUNTER, ctr); + uint32_t ctr1 = 0; + uint32_t ctr2 = 0; + if (optiga_get_counter(OID_PIN_HMAC_CTR, &ctr1) != OPTIGA_SUCCESS || + optiga_get_counter(OID_STRETCHED_PIN_CTR, &ctr2) != OPTIGA_SUCCESS) { + return -1; + } + + // Ensure that the counters are in sync. + if (ctr1 > ctr2) { + if (optiga_count_data_object(OID_STRETCHED_PIN_CTR, ctr1 - ctr2) != + OPTIGA_SUCCESS) { + return -1; + } + *ctr = ctr1; + } else if (ctr2 > ctr1) { + if (optiga_count_data_object(OID_PIN_HMAC_CTR, ctr2 - ctr1) != + OPTIGA_SUCCESS) { + return -1; + } + *ctr = ctr2; + } else { + *ctr = ctr2; + } + return OPTIGA_SUCCESS; +} + +int optiga_pin_fails_increase_v4(uint32_t count) { + if (count > 0xff) { + return OPTIGA_ERR_PARAM; + } + + return optiga_count_data_object(OID_STRETCHED_PIN_CTR, count); } int optiga_pin_fails_increase(uint32_t count) { @@ -772,5 +891,10 @@ int optiga_pin_fails_increase(uint32_t count) { return OPTIGA_ERR_PARAM; } - return optiga_count_data_object(OID_PIN_COUNTER, count); + if (optiga_count_data_object(OID_PIN_HMAC_CTR, count) != OPTIGA_SUCCESS || + optiga_count_data_object(OID_STRETCHED_PIN_CTR, count) != + OPTIGA_SUCCESS) { + return -1; + } + return OPTIGA_SUCCESS; } diff --git a/core/embed/trezorhal/unix/optiga.c b/core/embed/trezorhal/unix/optiga.c index c6418aae8f..42ba824322 100644 --- a/core/embed/trezorhal/unix/optiga.c +++ b/core/embed/trezorhal/unix/optiga.c @@ -174,9 +174,16 @@ int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress, return OPTIGA_SUCCESS; } +int optiga_pin_get_fails_v4(uint32_t *ctr) { + *ctr = 0; + return OPTIGA_SUCCESS; +} + int optiga_pin_get_fails(uint32_t *ctr) { *ctr = 0; return OPTIGA_SUCCESS; } +int optiga_pin_fails_increase_v4(uint32_t count) { return OPTIGA_SUCCESS; } + int optiga_pin_fails_increase(uint32_t count) { return OPTIGA_SUCCESS; } diff --git a/storage/storage.c b/storage/storage.c index 006157352d..2e39218bef 100644 --- a/storage/storage.c +++ b/storage/storage.c @@ -1275,9 +1275,14 @@ uint32_t storage_get_pin_rem(void) { #if USE_OPTIGA // Synchronize counters in case they diverged. uint32_t ctr_optiga = 0; - ensure( - optiga_pin_get_fails(&ctr_optiga) == OPTIGA_SUCCESS ? sectrue : secfalse, - "optiga_pin_get_fails failed"); + optiga_result ret = OPTIGA_SUCCESS; + if (get_lock_version() >= 5) { + ret = optiga_pin_get_fails(&ctr_optiga); + } else { + ret = optiga_pin_get_fails_v4(&ctr_optiga); + } + ensure(ret == OPTIGA_SUCCESS ? sectrue : secfalse, + "optiga_pin_get_fails failed"); while (ctr_mcu < ctr_optiga) { storage_pin_fails_increase(); @@ -1285,9 +1290,12 @@ uint32_t storage_get_pin_rem(void) { } if (ctr_optiga < ctr_mcu) { - ensure(optiga_pin_fails_increase(ctr_mcu - ctr_optiga) == OPTIGA_SUCCESS - ? sectrue - : secfalse, + if (get_lock_version() >= 5) { + ret = optiga_pin_fails_increase(ctr_mcu - ctr_optiga); + } else { + ret = optiga_pin_fails_increase_v4(ctr_mcu - ctr_optiga); + } + ensure(ret == OPTIGA_SUCCESS ? sectrue : secfalse, "optiga_pin_fails_increase failed"); } #endif