1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-19 12:58:13 +00:00

feat(core): Rework Optiga PIN slot configuration.

This commit is contained in:
Andrew Kozlik 2024-06-13 18:24:09 +02:00 committed by Andrew Kozlik
parent 9c50e15cf7
commit 509e291118
4 changed files with 316 additions and 173 deletions

View File

@ -69,8 +69,12 @@ int __wur optiga_pin_verify_v4(OPTIGA_UI_PROGRESS ui_progress,
const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE],
uint8_t out_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_get_fails(uint32_t *ctr);
int __wur optiga_pin_fails_increase_v4(uint32_t count);
int __wur optiga_pin_fails_increase(uint32_t count); int __wur optiga_pin_fails_increase(uint32_t count);
#endif #endif

View File

@ -26,20 +26,29 @@
#include "rand.h" #include "rand.h"
#include "storage.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) #define OID_PIN_SECRET (OPTIGA_OID_DATA + 0)
// PIN counter (OID 0xE120). // Digest of the stretched PIN (OID 0xF1D4).
#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).
#define OID_STRETCHED_PIN (OPTIGA_OID_DATA + 4) #define OID_STRETCHED_PIN (OPTIGA_OID_DATA + 4)
// Key for HMAC-SHA256 PIN stretching step (OID 0xF1D1). // Counter-protected key for HMAC-SHA256 PIN stretching step (OID 0xF1D5).
#define OID_PIN_HMAC (OPTIGA_OID_DATA + 1) #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). // Key for AES-CMAC PIN stretching step (OID 0xE200).
#define OID_PIN_CMAC OPTIGA_OID_SYM_KEY #define OID_PIN_CMAC OPTIGA_OID_SYM_KEY
@ -47,20 +56,17 @@
// Key for ECDH PIN stretching step (OID 0xE0F3). // Key for ECDH PIN stretching step (OID 0xE0F3).
#define OID_PIN_ECDH (OPTIGA_OID_ECC_KEY + 3) #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. // The number of times that PIN stretching is repeated.
#define PIN_STRETCH_ITERATIONS 2 #define PIN_STRETCH_ITERATIONS 2
// Value of the PIN counter when it is reset. // 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}; 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 // Initial value of the counter which limits the total number of PIN stretching
// 600000 stretching operations, which equates to // operations. The limit is 600000 stretching operations, which equates to
// 100000 / PIN_STRETCH_ITERATIONS unlock operations. // 300000 / PIN_STRETCH_ITERATIONS unlock operations over the lifetime of the
static const uint8_t STRETCH_COUNTER_INIT[] = {0, 0, 0, 0, 0, 0x09, 0x27, 0xC0}; // device.
static const uint8_t PIN_TOTAL_CTR_INIT[] = {0, 0, 0, 0, 0, 0x09, 0x27, 0xC0};
static const optiga_metadata_item TYPE_AUTOREF = static const optiga_metadata_item TYPE_AUTOREF =
OPTIGA_META_VALUE(OPTIGA_DATA_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); OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_AUTO, OID_STRETCHED_PIN);
static const optiga_metadata_item ACCESS_PIN_SECRET = static const optiga_metadata_item ACCESS_PIN_SECRET =
OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_AUTO, OID_PIN_SECRET); OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_AUTO, OID_PIN_SECRET);
static const optiga_metadata_item ACCESS_PIN_COUNTER = static const optiga_metadata_item ACCESS_STRETCHED_PIN_CTR =
OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_LUC, OID_PIN_COUNTER); OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_LUC, OID_STRETCHED_PIN_CTR);
static const optiga_metadata_item ACCESS_PIN_STRETCH_COUNTER = static const optiga_metadata_item ACCESS_PIN_TOTAL_CTR =
OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_LUC, OID_PIN_STRETCH_COUNTER); 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(). // Size of the DER BIT STRING header required for inputs to optiga_calc_ssec().
#define BIT_STRING_HEADER_SIZE 3 #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) { static bool optiga_pin_init_metadata(void) {
optiga_metadata metadata = {0}; optiga_metadata metadata = {0};
// Set metadata for PIN counter reset key / Master secret. // Set metadata for counter-protected PIN secret.
memzero(&metadata, sizeof(metadata)); memzero(&metadata, sizeof(metadata));
metadata.change = OPTIGA_META_ACCESS_ALWAYS; metadata.change = OPTIGA_META_ACCESS_ALWAYS;
metadata.read = ACCESS_STRETCHED_PIN; metadata.read = ACCESS_STRETCHED_PIN;
@ -254,43 +262,64 @@ static bool optiga_pin_init_metadata(void) {
return false; 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)); memzero(&metadata, sizeof(metadata));
metadata.change = ACCESS_PIN_SECRET; metadata.change = ACCESS_PIN_SECRET;
metadata.read = OPTIGA_META_ACCESS_ALWAYS; metadata.read = OPTIGA_META_ACCESS_ALWAYS;
metadata.execute = 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; 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)); memzero(&metadata, sizeof(metadata));
metadata.change = OPTIGA_META_ACCESS_ALWAYS; metadata.change = OPTIGA_META_ACCESS_ALWAYS;
if (write_metadata(OID_PIN_STRETCH_COUNTER, &metadata)) { if (write_metadata(OID_PIN_TOTAL_CTR, &metadata)) {
optiga_result res = optiga_set_data_object(OID_PIN_STRETCH_COUNTER, false, optiga_result res =
STRETCH_COUNTER_INIT, optiga_set_data_object(OID_PIN_TOTAL_CTR, false, PIN_TOTAL_CTR_INIT,
sizeof(STRETCH_COUNTER_INIT)); sizeof(PIN_TOTAL_CTR_INIT));
if (res != OPTIGA_SUCCESS) { if (res != OPTIGA_SUCCESS) {
return false; 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)); memzero(&metadata, sizeof(metadata));
metadata.change = OPTIGA_META_ACCESS_NEVER; metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_ALWAYS; metadata.read = OPTIGA_META_ACCESS_ALWAYS;
metadata.execute = OPTIGA_META_ACCESS_ALWAYS; metadata.execute = OPTIGA_META_ACCESS_ALWAYS;
if (!optiga_set_metadata(OID_PIN_STRETCH_COUNTER, &metadata)) { if (!optiga_set_metadata(OID_PIN_TOTAL_CTR, &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)) {
return false; return false;
} }
@ -298,7 +327,7 @@ static bool optiga_pin_init_metadata(void) {
memzero(&metadata, sizeof(metadata)); memzero(&metadata, sizeof(metadata));
metadata.change = OPTIGA_META_ACCESS_ALWAYS; metadata.change = OPTIGA_META_ACCESS_ALWAYS;
metadata.read = OPTIGA_META_ACCESS_NEVER; 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; metadata.key_usage = OPTIGA_META_KEY_USE_ENC;
if (!optiga_set_metadata(OID_PIN_CMAC, &metadata)) { if (!optiga_set_metadata(OID_PIN_CMAC, &metadata)) {
return false; return false;
@ -308,41 +337,12 @@ static bool optiga_pin_init_metadata(void) {
memzero(&metadata, sizeof(metadata)); memzero(&metadata, sizeof(metadata));
metadata.change = OPTIGA_META_ACCESS_ALWAYS; metadata.change = OPTIGA_META_ACCESS_ALWAYS;
metadata.read = OPTIGA_META_ACCESS_NEVER; 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; metadata.key_usage = OPTIGA_META_KEY_USE_KEYAGREE;
if (!optiga_set_metadata(OID_PIN_ECDH, &metadata)) { if (!optiga_set_metadata(OID_PIN_ECDH, &metadata)) {
return false; 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; return true;
} }
@ -369,9 +369,10 @@ static int optiga_pin_init_stretch(void) {
static int optiga_pin_stretch_common( static int optiga_pin_stretch_common(
OPTIGA_UI_PROGRESS ui_progress, HMAC_SHA256_CTX *ctx, OPTIGA_UI_PROGRESS ui_progress, HMAC_SHA256_CTX *ctx,
const uint8_t input[OPTIGA_PIN_SECRET_SIZE]) { const uint8_t input[OPTIGA_PIN_SECRET_SIZE], bool version4) {
// Implements the functionality that is common to optiga_pin_stretch_secret() // Implements the functionality that is common to
// and the legacy function optiga_pin_stretch_secret_v4(). // 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}; uint8_t buffer[ENCRYPT_SYM_PREFIX_SIZE + OPTIGA_PIN_SECRET_SIZE] = {0};
size_t size = 0; size_t size = 0;
@ -387,16 +388,18 @@ static int optiga_pin_stretch_common(
hmac_sha256_Update(ctx, buffer, size); hmac_sha256_Update(ctx, buffer, size);
if (version4) {
// Combine intermediate result with OID_PIN_HMAC // Combine intermediate result with OID_PIN_HMAC
res = res = optiga_encrypt_sym(OPTIGA_SYM_MODE_HMAC_SHA256, OID_PIN_HMAC_V4,
optiga_encrypt_sym(OPTIGA_SYM_MODE_HMAC_SHA256, OID_PIN_HMAC, input, input, OPTIGA_PIN_SECRET_SIZE, buffer,
OPTIGA_PIN_SECRET_SIZE, buffer, sizeof(buffer), &size); sizeof(buffer), &size);
if (res != OPTIGA_SUCCESS) { if (res != OPTIGA_SUCCESS) {
memzero(buffer, sizeof(buffer)); memzero(buffer, sizeof(buffer));
return res; return res;
} }
hmac_sha256_Update(ctx, buffer, size); hmac_sha256_Update(ctx, buffer, size);
}
ui_progress(200); ui_progress(200);
@ -435,39 +438,27 @@ static int optiga_pin_stretch_secret_v4(
// in each attempt. // in each attempt.
// Pseudocode for the stretching process: // Pseudocode for the stretching process:
// result_0 = secret // cmac_out = CMAC(OID_PIN_CMAC, secret)
// for i in range(PIN_STRETCH_ITERATIONS_V4): // hmac_out = HMAC(OID_PIN_HMAC_V4, secret)
// cmac_i = CMAC(optiga_cmac_key, result_i) // ecdh_out = ECDH(OID_PIN_ECDH, secret)
// hmac_i = HMAC(optiga_hmac_key, result_i) // secret = HMAC-SHA256(secret, cmac_out || hmac_out || ecdh_out)
// 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}
HMAC_SHA256_CTX ctx = {0}; HMAC_SHA256_CTX ctx = {0};
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); hmac_sha256_Init(&ctx, secret, OPTIGA_PIN_SECRET_SIZE);
optiga_result res = optiga_pin_stretch_common(ui_progress, &ctx, result); optiga_result res =
optiga_pin_stretch_common(ui_progress, &ctx, secret, true);
if (res != OPTIGA_SUCCESS) { if (res != OPTIGA_SUCCESS) {
memzero(result, sizeof(result));
memzero(&ctx, sizeof(ctx)); memzero(&ctx, sizeof(ctx));
return res; return res;
} }
hmac_sha256_Final(&ctx, result); hmac_sha256_Final(&ctx, secret);
}
memcpy(secret, result, sizeof(result));
memzero(result, sizeof(result));
memzero(&ctx, sizeof(ctx)); memzero(&ctx, sizeof(ctx));
return OPTIGA_SUCCESS; return OPTIGA_SUCCESS;
} }
static int optiga_pin_stretch_secret( static int optiga_pin_stretch_cmac_ecdh(
OPTIGA_UI_PROGRESS ui_progress, OPTIGA_UI_PROGRESS ui_progress,
uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) { uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) {
// This step hardens the PIN verification process in case an attacker is able // 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 // 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 // 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 // in each attempt, and restricts the overall number of attempts using
// OID_PIN_STRETCH_COUNTER. // OID_PIN_TOTAL_CTR.
// Pseudocode for the stretching process: // Pseudocode for the stretching process:
// for i in range(PIN_STRETCH_ITERATIONS): // for _ in range(PIN_STRETCH_ITERATIONS):
// digest = HMAC(secret, "") // digest = HMAC-SHA256(stretched_pin, "")
// cmac_i = CMAC(optiga_cmac_key, digest) // cmac_out = CMAC(OID_PIN_CMAC, digest)
// hmac_i = HMAC(optiga_hmac_key, digest) // ecdh_out = ECDH(OID_PIN_ECDH, digest)
// ecdh_i = ECDH(optiga_ecdh_key, digest) // stretched_pin = HMAC-SHA256(stretched_pin, cmac_out || ecdh_out)
// secret = HMAC-SHA256(secret, cmac_i || hmac_i || ecdh_i)
uint8_t digest[OPTIGA_PIN_SECRET_SIZE] = {0}; uint8_t digest[OPTIGA_PIN_SECRET_SIZE] = {0};
HMAC_SHA256_CTX ctx = {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(stretched_pin, OPTIGA_PIN_SECRET_SIZE, NULL, 0, digest);
hmac_sha256_Init(&ctx, stretched_pin, OPTIGA_PIN_SECRET_SIZE); 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) { if (res != OPTIGA_SUCCESS) {
memzero(digest, sizeof(digest)); memzero(digest, sizeof(digest));
memzero(&ctx, sizeof(ctx)); memzero(&ctx, sizeof(ctx));
@ -515,13 +506,15 @@ static int optiga_pin_stretch_secret(
int optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress, int optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress,
uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) { uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) {
int res = OPTIGA_SUCCESS;
if (!optiga_pin_init_metadata()) { 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) { if (res != OPTIGA_SUCCESS) {
return res; goto end;
} }
ui_progress(200); 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 // 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 // OID_PIN_SECRET, then it cannot be used to conduct an offline brute-force
// search for the PIN. // 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) { if (res != OPTIGA_SUCCESS) {
return res; goto end;
} }
// Generate and store the counter-protected PIN secret. // Generate and store the counter-protected PIN secret.
uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE] = {0}; uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE] = {0};
res = optiga_get_random(pin_secret, sizeof(pin_secret)); res = optiga_get_random(pin_secret, sizeof(pin_secret));
if (res != OPTIGA_SUCCESS) { if (res != OPTIGA_SUCCESS) {
return res; goto end;
} }
random_xor(pin_secret, sizeof(pin_secret)); random_xor(pin_secret, sizeof(pin_secret));
res = optiga_set_data_object(OID_PIN_SECRET, false, pin_secret, res = optiga_set_data_object(OID_PIN_SECRET, false, pin_secret,
sizeof(pin_secret)); sizeof(pin_secret));
if (res != OPTIGA_SUCCESS) { if (res != OPTIGA_SUCCESS) {
return res; goto end;
} }
// Authorise using OID_PIN_SECRET so that we can write to OID_PIN_COUNTER and // Generate the key for the HMAC-SHA256 PIN stretching step.
// OID_STRETCHED_PIN. 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, res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_PIN_SECRET,
pin_secret, sizeof(pin_secret)); pin_secret, sizeof(pin_secret));
if (res != OPTIGA_SUCCESS) { 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 // 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 // Optiga. This ensures that in the unlikely case of an attacker recording
// communication between the MCU and Optiga, they will not gain knowledge of // communication between the MCU and Optiga, they will not gain knowledge of
// the stretched PIN. // the stretched PIN.
uint8_t digest[OPTIGA_PIN_SECRET_SIZE] = {0};
hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, NULL, 0, digest); hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, NULL, 0, digest);
// Store the digest of the stretched PIN in OID_STRETCHED_PIN. // Store the digest of the stretched PIN in OID_STRETCHED_PIN.
res = res =
optiga_set_data_object(OID_STRETCHED_PIN, false, digest, sizeof(digest)); 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 // 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 // ensures that if the user chooses a high-entropy PIN, then even if the
@ -575,26 +635,20 @@ int optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress,
// integrated into the device in the first place. // integrated into the device in the first place.
hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, pin_secret, hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, pin_secret,
sizeof(pin_secret), stretched_pin); 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(pin_secret, sizeof(pin_secret));
memzero(digest, sizeof(digest)); memzero(digest, sizeof(digest));
if (res != OPTIGA_SUCCESS) {
optiga_clear_auto_state(OID_PIN_SECRET); optiga_clear_auto_state(OID_PIN_SECRET);
optiga_clear_auto_state(OID_STRETCHED_PIN);
return res; 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;
}
int optiga_pin_verify_v4(OPTIGA_UI_PROGRESS ui_progress, int optiga_pin_verify_v4(OPTIGA_UI_PROGRESS ui_progress,
const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE],
uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]) { uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]) {
@ -648,14 +702,6 @@ int optiga_pin_verify_v4(OPTIGA_UI_PROGRESS ui_progress,
return res; 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); ui_progress(200);
// Combine the value of OID_PIN_SECRET with the PIN-derived secret and // 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; 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, int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress,
uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) { uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) {
// Stretch the PIN more with stretching secrets from the Optiga. // 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) { if (res != OPTIGA_SUCCESS) {
return res; return res;
} }
@ -687,7 +770,8 @@ int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress,
uint8_t digest[OPTIGA_PIN_SECRET_SIZE] = {0}; uint8_t digest[OPTIGA_PIN_SECRET_SIZE] = {0};
hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, NULL, 0, digest); 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, res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_STRETCHED_PIN, digest,
sizeof(digest)); sizeof(digest));
memzero(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; 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) { if (res != OPTIGA_SUCCESS) {
optiga_clear_auto_state(OID_STRETCHED_PIN);
return res; return res;
} }
ui_progress(200);
// Read the counter-protected PIN secret from OID_PIN_SECRET. // Read the counter-protected PIN secret from OID_PIN_SECRET.
uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE] = {0}; uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE] = {0};
size_t size = 0; size_t size = 0;
@ -711,26 +801,20 @@ int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress,
return res; return res;
} }
if (size != OPTIGA_PIN_SECRET_SIZE) {
return OPTIGA_ERR_SIZE;
}
// Stretch the PIN more with the counter-protected PIN secret. // Stretch the PIN more with the counter-protected PIN secret.
hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, pin_secret, hmac_sha256(stretched_pin, OPTIGA_PIN_SECRET_SIZE, pin_secret, size,
OPTIGA_PIN_SECRET_SIZE, stretched_pin); stretched_pin);
ui_progress(200); // Authorise using OID_PIN_SECRET so that we can reset OID_STRETCHED_PIN_CTR.
// Authorise using OID_PIN_SECRET so that we can write to OID_PIN_COUNTER.
res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_PIN_SECRET, 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)); memzero(pin_secret, sizeof(pin_secret));
if (res != OPTIGA_SUCCESS) { if (res != OPTIGA_SUCCESS) {
return res; return res;
} }
// Reset the PIN counter. // Reset the counter which limits the guesses at OID_STRETCHED_PIN.
res = optiga_set_data_object(OID_PIN_COUNTER, false, COUNTER_RESET, res = optiga_set_data_object(OID_STRETCHED_PIN_CTR, false, COUNTER_RESET,
sizeof(COUNTER_RESET)); sizeof(COUNTER_RESET));
optiga_clear_auto_state(OID_PIN_SECRET); optiga_clear_auto_state(OID_PIN_SECRET);
if (res != OPTIGA_SUCCESS) { if (res != OPTIGA_SUCCESS) {
@ -763,8 +847,43 @@ static int optiga_get_counter(uint16_t oid, uint32_t *ctr) {
return OPTIGA_SUCCESS; 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) { 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) { 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_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;
} }

View File

@ -174,9 +174,16 @@ int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress,
return OPTIGA_SUCCESS; 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) { int optiga_pin_get_fails(uint32_t *ctr) {
*ctr = 0; *ctr = 0;
return OPTIGA_SUCCESS; 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; } int optiga_pin_fails_increase(uint32_t count) { return OPTIGA_SUCCESS; }

View File

@ -1275,8 +1275,13 @@ uint32_t storage_get_pin_rem(void) {
#if USE_OPTIGA #if USE_OPTIGA
// Synchronize counters in case they diverged. // Synchronize counters in case they diverged.
uint32_t ctr_optiga = 0; uint32_t ctr_optiga = 0;
ensure( optiga_result ret = OPTIGA_SUCCESS;
optiga_pin_get_fails(&ctr_optiga) == OPTIGA_SUCCESS ? sectrue : secfalse, 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"); "optiga_pin_get_fails failed");
while (ctr_mcu < ctr_optiga) { while (ctr_mcu < ctr_optiga) {
@ -1285,9 +1290,12 @@ uint32_t storage_get_pin_rem(void) {
} }
if (ctr_optiga < ctr_mcu) { if (ctr_optiga < ctr_mcu) {
ensure(optiga_pin_fails_increase(ctr_mcu - ctr_optiga) == OPTIGA_SUCCESS if (get_lock_version() >= 5) {
? sectrue ret = optiga_pin_fails_increase(ctr_mcu - ctr_optiga);
: secfalse, } else {
ret = optiga_pin_fails_increase_v4(ctr_mcu - ctr_optiga);
}
ensure(ret == OPTIGA_SUCCESS ? sectrue : secfalse,
"optiga_pin_fails_increase failed"); "optiga_pin_fails_increase failed");
} }
#endif #endif