Andrew Kozlik 1 month ago committed by GitHub
commit 952977e3f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -104,6 +104,19 @@
// from util.s
extern void shutdown_privileged(void);
#ifdef USE_OPTIGA
#if !PYOPT
static void optiga_log_hex(const char *prefix, const uint8_t *data,
size_t data_size) {
printf("%s: ", prefix);
for (size_t i = 0; i < data_size; i++) {
printf("%02x", data[i]);
}
printf("\n");
}
#endif
#endif
int main(void) {
random_delays_init();
@ -199,6 +212,14 @@ int main(void) {
#endif
#ifdef USE_OPTIGA
#if !PYOPT
// command log is relatively quiet so we enable it in debug builds
optiga_command_set_log_hex(optiga_log_hex);
// transport log can be spammy, uncomment if you want it:
// optiga_transport_set_log_hex(optiga_log_hex);
#endif
optiga_init();
optiga_open_application();
if (sectrue == secret_ok) {

@ -46,10 +46,10 @@ static optiga_pairing optiga_pairing_state = OPTIGA_PAIRING_UNPAIRED;
// Data object access conditions.
static const optiga_metadata_item ACCESS_PAIRED =
OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_CONF, OID_KEY_PAIRING);
static const optiga_metadata_item KEY_USE_SIGN = {
(const uint8_t[]){OPTIGA_KEY_USAGE_SIGN}, 1};
static const optiga_metadata_item TYPE_PTFBIND = {
(const uint8_t[]){OPTIGA_DATA_TYPE_PTFBIND}, 1};
static const optiga_metadata_item KEY_USE_SIGN =
OPTIGA_META_VALUE(OPTIGA_KEY_USAGE_SIGN);
static const optiga_metadata_item TYPE_PTFBIND =
OPTIGA_META_VALUE(OPTIGA_DATA_TYPE_PTFBIND);
// Identifier of context-specific constructed tag 3, which is used for
// extensions in X.509.
@ -173,6 +173,12 @@ void pair_optiga(void) {
return;
}
#if PRODUCTION
#define METADATA_SET_LOCKED(metadata) { metadata.lcso = OPTIGA_META_LCS_OPERATIONAL; }
#else
#define METADATA_SET_LOCKED(metadata)
#endif
void optiga_lock(void) {
if (!optiga_paired()) return;
@ -190,7 +196,7 @@ void optiga_lock(void) {
// Set metadata for device certificate.
memzero(&metadata, sizeof(metadata));
metadata.lcso = OPTIGA_META_LCS_OPERATIONAL;
METADATA_SET_LOCKED(metadata);
metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_ALWAYS;
metadata.execute = OPTIGA_META_ACCESS_ALWAYS;
@ -200,7 +206,7 @@ void optiga_lock(void) {
// Set metadata for FIDO attestation certificate.
memzero(&metadata, sizeof(metadata));
metadata.lcso = OPTIGA_META_LCS_OPERATIONAL;
METADATA_SET_LOCKED(metadata);
metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_ALWAYS;
metadata.execute = OPTIGA_META_ACCESS_ALWAYS;
@ -210,7 +216,7 @@ void optiga_lock(void) {
// Set metadata for device private key.
memzero(&metadata, sizeof(metadata));
metadata.lcso = OPTIGA_META_LCS_OPERATIONAL;
METADATA_SET_LOCKED(metadata);
metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_NEVER;
metadata.execute = ACCESS_PAIRED;
@ -221,7 +227,7 @@ void optiga_lock(void) {
// Set metadata for FIDO attestation private key.
memzero(&metadata, sizeof(metadata));
metadata.lcso = OPTIGA_META_LCS_OPERATIONAL;
METADATA_SET_LOCKED(metadata);
metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_NEVER;
metadata.execute = ACCESS_PAIRED;
@ -232,7 +238,7 @@ void optiga_lock(void) {
// Set metadata for pairing key.
memzero(&metadata, sizeof(metadata));
metadata.lcso = OPTIGA_META_LCS_OPERATIONAL;
METADATA_SET_LOCKED(metadata);
metadata.change = OPTIGA_META_ACCESS_NEVER;
metadata.read = OPTIGA_META_ACCESS_NEVER;
metadata.execute = OPTIGA_META_ACCESS_ALWAYS;
@ -503,6 +509,14 @@ void keyfido_write(char *data) {
return;
}
// Set the data type of OID 0xE0E8 to trust anchor, so that we can use it to
// write the FIDO key.
memzero(&metadata, sizeof(metadata));
metadata.data_type = OPTIGA_META_VALUE(OPTIGA_DATA_TYPE_TA);
if (!set_metadata(OID_TRUST_ANCHOR, &metadata)) {
return;
}
// Write trust anchor certificate to OID 0xE0E8
ret = optiga_set_trust_anchor();
if (OPTIGA_SUCCESS != ret) {
@ -514,8 +528,8 @@ void keyfido_write(char *data) {
// Set change access condition for the FIDO key to Int(0xE0E8), so that we
// can write the FIDO key using the trust anchor in OID 0xE0E8.
memzero(&metadata, sizeof(metadata));
metadata.change = (const optiga_metadata_item)OPTIGA_ACCESS_CONDITION(
OPTIGA_ACCESS_COND_INT, OID_TRUST_ANCHOR);
metadata.change =
OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_INT, OID_TRUST_ANCHOR);
metadata.version = OPTIGA_META_VERSION_DEFAULT;
if (!set_metadata(OID_KEY_FIDO, &metadata)) {
return;

@ -24,11 +24,11 @@
#include <stddef.h>
#include <stdint.h>
#define OID_CERT_INF OPTIGA_OID_CERT + 0
#define OID_CERT_DEV OPTIGA_OID_CERT + 1
#define OID_CERT_FIDO OPTIGA_OID_CERT + 2
#define OID_KEY_DEV OPTIGA_OID_ECC_KEY + 0
#define OID_KEY_FIDO OPTIGA_OID_ECC_KEY + 2
#define OID_CERT_INF (OPTIGA_OID_CERT + 0)
#define OID_CERT_DEV (OPTIGA_OID_CERT + 1)
#define OID_CERT_FIDO (OPTIGA_OID_CERT + 2)
#define OID_KEY_DEV (OPTIGA_OID_ECC_KEY + 0)
#define OID_KEY_FIDO (OPTIGA_OID_ECC_KEY + 2)
#define OID_KEY_PAIRING OPTIGA_OID_PTFBIND_SECRET
#define OID_TRUST_ANCHOR (OPTIGA_OID_CA_CERT + 0)

@ -45,7 +45,7 @@ void vcp_println(const char *fmt, ...) {
vcp_puts("\r\n", 2);
}
void vcp_println_hex(uint8_t *data, uint16_t len) {
void vcp_println_hex(const uint8_t *data, uint16_t len) {
for (int i = 0; i < len; i++) {
vcp_print("%02X", data[i]);
}

@ -28,7 +28,7 @@ enum { VCP_IFACE = 0x00 };
void vcp_puts(const char *s, size_t len);
void vcp_print(const char *fmt, ...);
void vcp_println(const char *fmt, ...);
void vcp_println_hex(uint8_t *data, uint16_t len);
void vcp_println_hex(const uint8_t *data, uint16_t len);
int get_from_hex(uint8_t *buf, uint16_t buf_len, const char *hex);
#endif

@ -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,
@ -71,4 +75,6 @@ int __wur optiga_pin_get_fails(uint32_t *ctr);
int __wur optiga_pin_fails_increase(uint32_t count);
bool optiga_pin_wipe(void);
#endif

@ -58,10 +58,10 @@ static const uint8_t COUNTER_RESET[] = {0, 0, 0, 0, 0, 0, 0, PIN_MAX_TRIES};
// 100000 / PIN_STRETCH_ITERATIONS unlock operations.
static const uint8_t STRETCH_COUNTER_INIT[] = {0, 0, 0, 0, 0, 0x09, 0x27, 0xC0};
static const optiga_metadata_item TYPE_AUTOREF = {
(const uint8_t[]){OPTIGA_DATA_TYPE_AUTOREF}, 1};
static const optiga_metadata_item TYPE_PRESSEC = {
(const uint8_t[]){OPTIGA_DATA_TYPE_PRESSEC}, 1};
static const optiga_metadata_item TYPE_AUTOREF =
OPTIGA_META_VALUE(OPTIGA_DATA_TYPE_AUTOREF);
static const optiga_metadata_item TYPE_PRESSEC =
OPTIGA_META_VALUE(OPTIGA_DATA_TYPE_PRESSEC);
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 =
@ -215,7 +215,8 @@ bool optiga_set_metadata(uint16_t oid, const optiga_metadata *metadata) {
}
}
// If the metadata aren't locked, then lock them.
#if PRODUCTION
// If the metadata aren't locked, then lock them in production builds.
optiga_metadata metadata_locked = {0};
metadata_locked.lcso = OPTIGA_META_LCS_OPERATIONAL;
if (!optiga_compare_metadata(&metadata_locked, &metadata_stored)) {
@ -231,6 +232,7 @@ bool optiga_set_metadata(uint16_t oid, const optiga_metadata *metadata) {
return false;
}
}
#endif
return true;
}
@ -510,14 +512,14 @@ int optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress,
sizeof(stretched_pin));
memzero(stretched_pin, sizeof(stretched_pin));
if (res != OPTIGA_SUCCESS) {
optiga_clear_auto_state(OID_PIN_SECRET);
(void)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);
(void)optiga_clear_auto_state(OID_PIN_SECRET);
if (res != OPTIGA_SUCCESS) {
return res;
}
@ -588,7 +590,7 @@ int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress,
size_t size = 0;
res = optiga_get_data_object(OID_PIN_SECRET, false, out_secret,
OPTIGA_PIN_SECRET_SIZE, &size);
optiga_clear_auto_state(OID_STRETCHED_PIN);
(void)optiga_clear_auto_state(OID_STRETCHED_PIN);
if (res != OPTIGA_SUCCESS) {
return res;
}
@ -609,7 +611,7 @@ int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress,
// 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);
(void)optiga_clear_auto_state(OID_PIN_SECRET);
if (res != OPTIGA_SUCCESS) {
return res;
}
@ -664,3 +666,32 @@ int optiga_pin_fails_increase(uint32_t count) {
return optiga_count_data_object(OID_PIN_COUNTER, count);
}
bool optiga_pin_wipe(void) {
bool ret = true;
if (optiga_pin_init_stretch() != OPTIGA_SUCCESS) {
ret = false;
}
// Wipe the master secret / PIN counter reset key.
const uint8_t empty[] = {0};
if (optiga_set_data_object(OID_PIN_SECRET, false, empty, sizeof(empty)) !=
OPTIGA_SUCCESS) {
ret = false;
}
// Authorise using OID_PIN_SECRET so that we can wipe OID_STRETCHED_PIN.
if (optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_PIN_SECRET, empty,
sizeof(empty)) != OPTIGA_SUCCESS) {
ret = false;
}
// Wipe the stretched PIN.
if (optiga_set_data_object(OID_STRETCHED_PIN, false, empty, sizeof(empty)) !=
OPTIGA_SUCCESS) {
ret = false;
}
return ret;
}

@ -37,19 +37,30 @@
static uint8_t tx_buffer[OPTIGA_MAX_APDU_SIZE] = {0};
static size_t tx_size = 0;
const optiga_metadata_item OPTIGA_META_LCS_OPERATIONAL = {
(const uint8_t *)"\x07", 1};
const optiga_metadata_item OPTIGA_META_ACCESS_ALWAYS = {
(const uint8_t[]){OPTIGA_ACCESS_COND_ALW}, 1};
const optiga_metadata_item OPTIGA_META_ACCESS_NEVER = {
(const uint8_t[]){OPTIGA_ACCESS_COND_NEV}, 1};
const optiga_metadata_item OPTIGA_META_KEY_USE_ENC = {
(const uint8_t[]){OPTIGA_KEY_USAGE_ENC}, 1};
const optiga_metadata_item OPTIGA_META_KEY_USE_KEYAGREE = {
(const uint8_t[]){OPTIGA_KEY_USAGE_KEYAGREE}, 1};
const optiga_metadata_item OPTIGA_META_LCS_OPERATIONAL =
OPTIGA_META_VALUE(OPTIGA_LCS_OP);
const optiga_metadata_item OPTIGA_META_ACCESS_ALWAYS =
OPTIGA_META_VALUE(OPTIGA_ACCESS_COND_ALW);
const optiga_metadata_item OPTIGA_META_ACCESS_NEVER =
OPTIGA_META_VALUE(OPTIGA_ACCESS_COND_NEV);
const optiga_metadata_item OPTIGA_META_KEY_USE_ENC =
OPTIGA_META_VALUE(OPTIGA_KEY_USAGE_ENC);
const optiga_metadata_item OPTIGA_META_KEY_USE_KEYAGREE =
OPTIGA_META_VALUE(OPTIGA_KEY_USAGE_KEYAGREE);
const optiga_metadata_item OPTIGA_META_VERSION_DEFAULT = {
(const uint8_t[]){0x00, 0x00}, 2};
#if PRODUCTION
#define OPTIGA_LOG(prefix, data, data_size)
#else
static optiga_log_hex_t log_hex = NULL;
void optiga_command_set_log_hex(optiga_log_hex_t f) { log_hex = f; }
#define OPTIGA_LOG(prefix, data, data_size) \
if (log_hex != NULL) { \
log_hex(prefix, data, data_size); \
}
#endif
static optiga_result process_output(uint8_t **out_data, size_t *out_size) {
// Check that there is no trailing output data in the response.
if (tx_size < 4 || (tx_buffer[2] << 8) + tx_buffer[3] != tx_size - 4) {
@ -58,11 +69,13 @@ static optiga_result process_output(uint8_t **out_data, size_t *out_size) {
// Check response status code.
if (tx_buffer[0] != 0) {
OPTIGA_LOG("FAILED", NULL, 0)
return OPTIGA_ERR_CMD;
}
*out_data = tx_buffer + 4;
*out_size = tx_size - 4;
OPTIGA_LOG("SUCCESS", *out_data, *out_size)
return OPTIGA_SUCCESS;
}
@ -270,6 +283,7 @@ optiga_result optiga_open_application(void) {
0x65, 0x6E, 0x41, 0x75, 0x74, 0x68, 0x41, 0x70, 0x70, 0x6C,
};
OPTIGA_LOG(__func__, OPEN_APP, sizeof(OPEN_APP))
optiga_result ret = optiga_execute_command(
OPEN_APP, sizeof(OPEN_APP), tx_buffer, sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -288,6 +302,7 @@ optiga_result optiga_get_error_code(uint8_t *error_code) {
write_uint16(&ptr, OPTIGA_OID_ERROR_CODE);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -311,6 +326,7 @@ optiga_result optiga_get_data_object(uint16_t oid, bool get_metadata,
write_uint16(&ptr, oid);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -342,6 +358,7 @@ optiga_result optiga_set_data_object(uint16_t oid, bool set_metadata,
memcpy(ptr, data, data_size);
}
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -404,6 +421,7 @@ optiga_result optiga_get_random(uint8_t *random, size_t random_size) {
write_uint16(&ptr, random_size);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -436,6 +454,7 @@ optiga_result optiga_encrypt_sym(optiga_sym_mode mode, uint16_t oid,
*(ptr++) = 0x01; // start and final data block
write_prefixed_data(&ptr, input, input_size);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret == OPTIGA_SUCCESS) {
@ -465,6 +484,7 @@ optiga_result optiga_set_auto_state(uint16_t nonce_oid, uint16_t key_oid,
*(ptr++) = 0x41; // pre-pending optional data tag
write_uint16(&ptr, 0);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
@ -491,6 +511,7 @@ optiga_result optiga_set_auto_state(uint16_t nonce_oid, uint16_t key_oid,
write_uint16(&ptr, SHA256_DIGEST_LENGTH);
hmac_sha256(key, key_size, nonce, sizeof(nonce), ptr);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer, sizeof(tx_buffer),
&tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -515,6 +536,7 @@ optiga_result optiga_clear_auto_state(uint16_t key_oid) {
*(ptr++) = 0x43; // verification value tag
write_uint16(&ptr, 0); // verification value length
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -554,6 +576,7 @@ optiga_result optiga_calc_sign(uint16_t oid, const uint8_t *digest,
write_uint16(&ptr, 2);
write_uint16(&ptr, oid);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -621,6 +644,7 @@ optiga_result optiga_verify_sign(optiga_curve curve, const uint8_t *public_key,
*(ptr++) = 0x06; // public key tag
write_prefixed_data(&ptr, public_key, public_key_size);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -653,6 +677,7 @@ optiga_result optiga_gen_key_pair(optiga_curve curve, optiga_key_usage usage,
write_uint16(&ptr, 1);
*(ptr++) = usage;
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -682,6 +707,7 @@ optiga_result optiga_gen_sym_key(optiga_aes algorithm, optiga_key_usage usage,
write_uint16(&ptr, 1);
*(ptr++) = usage;
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -726,6 +752,7 @@ optiga_result optiga_calc_ssec(optiga_curve curve, uint16_t oid,
*(ptr++) = 0x07; // export tag
write_uint16(&ptr, 0);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -779,6 +806,7 @@ optiga_result optiga_derive_key(optiga_key_derivation deriv, uint16_t oid,
*(ptr++) = 0x07; // export tag
write_uint16(&ptr, 0);
OPTIGA_LOG(__func__, tx_buffer, tx_size)
optiga_result ret = optiga_execute_command(tx_buffer, tx_size, tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret == OPTIGA_SUCCESS) {
@ -907,6 +935,7 @@ optiga_result optiga_set_priv_key(uint16_t oid, const uint8_t priv_key[32]) {
return OPTIGA_ERR_PROCESS;
}
OPTIGA_LOG(__func__, sop_cmd1, sizeof(sop_cmd1))
ret = optiga_execute_command(sop_cmd1, sizeof(sop_cmd1), tx_buffer,
sizeof(tx_buffer), &tx_size);
if (ret != OPTIGA_SUCCESS) {
@ -920,6 +949,7 @@ optiga_result optiga_set_priv_key(uint16_t oid, const uint8_t priv_key[32]) {
return ret;
}
OPTIGA_LOG(__func__, sop_cmd2, sizeof(sop_cmd2))
ret = optiga_execute_command(sop_cmd2, sizeof(sop_cmd2), tx_buffer,
sizeof(tx_buffer), &tx_size);
memzero(sop_cmd2, sizeof(sop_cmd2));

@ -134,11 +134,11 @@ static uint8_t sec_chan_buffer[OPTIGA_MAX_APDU_SIZE + SEC_CHAN_OVERHEAD_SIZE] =
{0};
static size_t sec_chan_size = 0;
#ifdef NDEBUG
#if PRODUCTION
#define OPTIGA_LOG(prefix, data, data_size)
#else
static optiga_log_hex_t log_hex = NULL;
void optiga_set_log_hex(optiga_log_hex_t f) { log_hex = f; }
void optiga_transport_set_log_hex(optiga_log_hex_t f) { log_hex = f; }
#define OPTIGA_LOG(prefix, data, data_size) \
if (log_hex != NULL) { \
static uint8_t prev_data[4]; \
@ -184,7 +184,7 @@ optiga_result optiga_init(void) {
}
static optiga_result optiga_i2c_write(const uint8_t *data, uint16_t data_size) {
OPTIGA_LOG(">>> ", data, data_size)
OPTIGA_LOG(">>>", data, data_size)
for (int try_count = 0; try_count <= I2C_MAX_RETRY_COUNT; ++try_count) {
if (try_count != 0) {
@ -205,7 +205,7 @@ static optiga_result optiga_i2c_read(uint8_t *buffer, uint16_t buffer_size) {
HAL_Delay(1);
if (HAL_OK == i2c_receive(OPTIGA_I2C_INSTANCE, OPTIGA_ADDRESS, buffer,
buffer_size, I2C_TIMEOUT)) {
OPTIGA_LOG("<<< ", buffer, buffer_size)
OPTIGA_LOG("<<<", buffer, buffer_size)
return OPTIGA_SUCCESS;
}
}

@ -105,6 +105,14 @@ typedef enum {
OPTIGA_ACCESS_COND_NEV = 0xFF, // Never.
} optiga_access_cond;
// Life cycle status.
typedef enum {
OPTIGA_LCS_CR = 0x01, // Creation state.
OPTIGA_LCS_IN = 0x03, // Initialization state.
OPTIGA_LCS_OP = 0x07, // Operational state.
OPTIGA_LCS_TE = 0x0f, // Termination state.
} optiga_lcs;
typedef struct {
const uint8_t *ptr;
uint16_t len;
@ -132,8 +140,14 @@ typedef struct {
#define OPTIGA_RANDOM_MAX_SIZE 256
#define OPTIGA_MAX_CERT_SIZE 1728
#define OPTIGA_ACCESS_CONDITION(ac_id, oid) \
{ (const uint8_t[]){ac_id, oid >> 8, oid & 0xff}, 3 }
#define OPTIGA_ACCESS_CONDITION(ac_id, oid) \
(const optiga_metadata_item) { \
(const uint8_t[]){ac_id, oid >> 8, oid & 0xff}, 3 \
}
// Single-byte value of optiga_metadata_item.
#define OPTIGA_META_VALUE(val) \
(const optiga_metadata_item) { (const uint8_t[]){val}, 1 }
// Commonly used data object access conditions.
extern const optiga_metadata_item OPTIGA_META_LCS_OPERATIONAL;
@ -192,4 +206,9 @@ optiga_result optiga_derive_key(optiga_key_derivation deriv, uint16_t oid,
size_t key_size);
optiga_result optiga_set_trust_anchor(void);
optiga_result optiga_set_priv_key(uint16_t oid, const uint8_t priv_key[32]);
#if !PRODUCTION
void optiga_command_set_log_hex(optiga_log_hex_t f);
#endif
#endif

@ -34,4 +34,9 @@ typedef enum _optiga_result {
OPTIGA_ERR_CMD, // Command error. See error code data object 0xF1C2.
} optiga_result;
#if !PRODUCTION
typedef void (*optiga_log_hex_t)(const char *prefix, const uint8_t *data,
size_t data_size);
#endif
#endif

@ -44,10 +44,8 @@ optiga_result optiga_resync(void);
optiga_result optiga_soft_reset(void);
optiga_result optiga_set_data_reg_len(size_t size);
#ifndef NDEBUG
typedef void (*optiga_log_hex_t)(const char *prefix, const uint8_t *data,
size_t data_size);
void optiga_set_log_hex(optiga_log_hex_t f);
#if !PRODUCTION
void optiga_transport_set_log_hex(optiga_log_hex_t f);
#endif
#endif

@ -176,3 +176,5 @@ int optiga_pin_get_fails(uint32_t *ctr) {
}
int optiga_pin_fails_increase(uint32_t count) { return OPTIGA_SUCCESS; }
bool optiga_pin_wipe(void) { return true; }

@ -0,0 +1,208 @@
from __future__ import annotations
import os
import secrets
from dataclasses import dataclass
from typing import Any
from typing_extensions import Self
import click
import requests
import serial
SERVER_TOKEN = ""
SERVER_URL = "http://localhost:8000/provision"
@dataclass
class ProvisioningResult:
device_cert: bytes
fido_privkey: bytes
fido_cert: bytes
production: bool
@classmethod
def from_json(cls, json: dict[str, Any]) -> Self:
return cls(
device_cert=bytes.fromhex(json["device_cert"]),
fido_privkey=bytes.fromhex(json["fido_privkey"]),
fido_cert=bytes.fromhex(json["fido_cert"]),
production=json["production"],
)
def write(self, connection: Connection) -> None:
connection.command("CERTDEV WRITE", self.device_cert)
cert_dev = connection.command("CERTDEV READ")
if cert_dev != self.device_cert:
print("Device certificate mismatch")
print("Expected:", self.device_cert)
print("Got: ", cert_dev)
assert cert_dev == self.device_cert
connection.command("CERTFIDO WRITE", self.fido_cert)
cert_fido = connection.command("CERTFIDO READ")
assert cert_fido == self.fido_cert
connection.command("KEYFIDO WRITE", self.fido_privkey)
key_fido = connection.command("KEYFIDO READ")
assert key_fido is not None
assert key_fido in self.fido_cert
@dataclass
class DeviceInfo:
optiga_id: bytes
cpu_id: bytes
device_cert: bytes
@classmethod
def read(cls, connection: Connection) -> Self:
cpu_id = connection.command("CPUID READ")
optiga_id = connection.command("OPTIGAID READ")
cert_bytes = connection.command("CERTINF READ")
assert optiga_id is not None
assert cpu_id is not None
assert cert_bytes is not None
return cls(optiga_id, cpu_id, cert_bytes)
class ProdtestException(Exception):
def __init__(self, text: str) -> None:
super().__init__(text)
self.text = text
class Connection:
def __init__(self, path: str = "/dev/ttyACM0") -> None:
self.connection = serial.Serial(path, 115200, timeout=5)
def readline(self) -> bytes:
line = self.connection.readline().strip()
line_str = line.decode()
if len(line_str) > 100:
line_str = line_str[:100] + "..."
print("<<<", line_str)
return line
def writeline(self, data: bytes) -> None:
data_str = data.decode()
if len(data_str) > 100:
print(">>>", data_str[:100] + "...")
else:
print(">>>", data_str)
for byte in data:
if byte < 32 or byte > 126:
print("!!!", byte, "is not printable")
continue
self.connection.write(bytes([byte]))
echo = self.connection.read(1)
assert echo[0] == byte
self.connection.write(b"\r")
assert self.connection.read(2) == b"\r\n"
# self.connection.write(data + b"\r")
# echo = self.connection.read(len(data) + 2)
# print(len(echo), len(data) + 2)
# assert echo[:-2] == data
# assert echo[-2:] == b"\r\n"
def command(self, cmd: str, *args: Any) -> bytes | None:
cmd_line = cmd
for arg in args:
if isinstance(arg, bytes):
cmd_line += " " + arg.hex()
else:
cmd_line += " " + str(arg)
self.writeline(cmd_line.encode())
res = self.readline()
if res.startswith(b"ERROR"):
error_text = res[len(b"ERROR ") :].decode()
raise ProdtestException(error_text)
if not res.startswith(b"OK"):
raise ProdtestException("Unexpected response: " + res.decode())
res_arg = res[len(b"OK ") :]
if not res_arg:
return None
try:
return bytes.fromhex(res_arg.decode())
except ValueError:
return res_arg
def provision_request(
device: DeviceInfo, url: str, verify: bool = True
) -> ProvisioningResult:
request = {
"tester_id": SERVER_TOKEN,
"run_id": secrets.token_hex(16),
"optiga_id": device.optiga_id.hex(),
"cpu_id": device.cpu_id.hex(),
"cert": device.device_cert.hex(),
"model": "T2B1",
}
resp = requests.post(url, json=request, verify=verify)
if resp.status_code == 400:
print("Server returned error:", resp.text)
resp.raise_for_status()
resp_json = resp.json()
return ProvisioningResult.from_json(resp_json)
@click.group()
def cli() -> None:
pass
@cli.command()
def identify() -> None:
connection = Connection()
connection.command("PING")
DeviceInfo.read(connection)
@cli.command()
@click.option("--wipe", is_flag=True, help="Wipe the device")
def lock(wipe) -> None:
connection = Connection()
connection.command("PING")
connection.command("LOCK")
if wipe:
connection.command("WIPE")
@cli.command()
@click.option("-u", "--url", default=SERVER_URL, help="Server URL")
@click.option("-d", "--device", default="/dev/ttyACM0", help="Device path")
@click.option(
"--no-verify", is_flag=True, help="Disable server certificate verification"
)
@click.option(
"--lock/--no-lock", default=True, help="Lock the device after provisioning"
)
def provision(url, device, no_verify, lock) -> None:
global SERVER_TOKEN
SERVER_TOKEN = os.environ.get("SERVER_TOKEN")
if SERVER_TOKEN is None:
raise click.ClickException("SERVER_TOKEN environment variable is not set")
connection = Connection(device)
# test the connection
connection.command("PING")
# grab CPUID, OPTIGAID and device certificate
device = DeviceInfo.read(connection)
# call the provisioning server
result = provision_request(device, url, not no_verify)
# write provisioning result to the device
result.write(connection)
if lock:
connection.command("LOCK")
connection.command("WIPE")
if __name__ == "__main__":
cli()

@ -18,6 +18,7 @@
*/
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include "chacha20poly1305/rfc7539.h"
@ -82,17 +83,10 @@ const uint32_t V0_PIN_EMPTY = 1;
#define MAX_WIPE_CODE_LEN 50
// The total number of iterations to use in PBKDF2.
#define PIN_ITER_COUNT 20000
#define PBKDF2_ITER_COUNT 20000
// The number of milliseconds required to execute PBKDF2.
#define PIN_PBKDF2_MS 1280
// The number of milliseconds required to derive the KEK and KEIV.
#if USE_OPTIGA
#define PIN_DERIVE_MS (PIN_PBKDF2_MS + OPTIGA_PIN_DERIVE_MS)
#else
#define PIN_DERIVE_MS PIN_PBKDF2_MS
#endif
// The number of PBKDF2 iterations that can be computed in one second.
#define PBKDF2_ITER_PER_SEC 15625
// The length of the hashed hardware salt in bytes.
#define HARDWARE_SALT_SIZE SHA256_DIGEST_LENGTH
@ -172,6 +166,27 @@ static secbool storage_set_encrypted(const uint16_t key, const void *val,
const uint16_t len);
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, int pbkdf2_iterations);
// The number of milliseconds required to compute PBKDF2.
static int pbkdf2_ms(int pbkdf2_iterations) {
return 1000 * pbkdf2_iterations / PBKDF2_ITER_PER_SEC;
}
// The number of milliseconds required to derive the KEK and KEIV.
static int pin_derive_ms(bool pin_valid, int pbkdf2_iterations) {
(void)pin_valid;
int time_ms = pbkdf2_ms(pbkdf2_iterations);
#if USE_OPTIGA
if (pin_valid) {
time_ms += OPTIGA_PIN_DERIVE_MS;
} else {
time_ms += OPTIGA_PIN_DERIVE_INVALID_MS;
}
#endif
return time_ms;
}
#include "flash.h"
#ifdef FLASH_BIT_ACCESS
@ -471,10 +486,36 @@ static secbool ui_progress(uint32_t elapsed_ms) {
}
}
static void pbkdf2_update_progressive(PBKDF2_HMAC_SHA256_CTX *ctx,
int total_iters) {
// Total number of milliseconds.
const int total_ms = pbkdf2_ms(total_iters);
// Number of milliseconds and iterations per progress step.
int step_ms = 128;
int step_iters = PBKDF2_ITER_PER_SEC * step_ms / 1000;
int current_ms = 0;
int current_iters = 0;
while (current_iters < total_iters) {
if (total_iters - current_iters < step_iters) {
step_iters = total_iters - current_iters;
}
pbkdf2_hmac_sha256_Update(ctx, step_iters);
current_iters += step_iters;
if (total_ms - current_ms < step_ms) {
step_ms = total_ms - current_ms;
}
ui_progress(step_ms);
current_ms += step_ms;
}
}
#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};
@ -493,17 +534,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);
for (int i = 1; i <= 5; i++) {
pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10);
ui_progress(PIN_PBKDF2_MS / 10);
}
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);
for (int i = 6; i <= 10; i++) {
pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10);
ui_progress(PIN_PBKDF2_MS / 10);
}
pbkdf2_update_progressive(&ctx, pbkdf2_iterations / 2);
pbkdf2_hmac_sha256_Final(&ctx, keiv);
memzero(&ctx, sizeof(PBKDF2_HMAC_SHA256_CTX));
@ -514,7 +549,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
@ -542,11 +577,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));
for (int i = 1; i <= 10; i++) {
pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10);
ui_progress(PIN_PBKDF2_MS / 10);
}
pbkdf2_update_progressive(&ctx, pbkdf2_iterations);
pbkdf2_hmac_sha256_Final(&ctx, stretched_pin);
memzero(&ctx, sizeof(ctx));
@ -575,13 +606,26 @@ 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);
int ret = optiga_pin_set(ui_progress, stretched_pin, optiga_secret);
stretch_pin_optiga(pin, pin_len, storage_salt, ext_salt, pbkdf2_iterations,
stretched_pin);
int ret = OPTIGA_SUCCESS;
#if !PYOPT
// Skip usage of Optiga for empty PIN in debug builds to avoid excessive wear
// of Optiga counters.
if (pin_len == PIN_EMPTY_LEN) {
memcpy(optiga_secret, stretched_pin, sizeof(stretched_pin));
ui_progress(OPTIGA_PIN_DERIVE_MS);
} else
#endif
{
ret = optiga_pin_set(ui_progress, stretched_pin, optiga_secret);
}
memzero(stretched_pin, sizeof(stretched_pin));
if (ret != OPTIGA_SUCCESS) {
memzero(optiga_secret, sizeof(optiga_secret));
@ -590,7 +634,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;
}
@ -598,13 +643,26 @@ 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);
int ret = optiga_pin_verify(ui_progress, stretched_pin, optiga_secret);
stretch_pin_optiga(pin, pin_len, storage_salt, ext_salt, pbkdf2_iterations,
stretched_pin);
int ret = OPTIGA_SUCCESS;
#if !PYOPT
// Skip usage of Optiga for empty PIN in debug builds to avoid excessive wear
// of Optiga counters.
if (pin_len == PIN_EMPTY_LEN) {
memcpy(optiga_secret, stretched_pin, sizeof(stretched_pin));
ui_progress(OPTIGA_PIN_DERIVE_MS);
} else
#endif
{
ret = optiga_pin_verify(ui_progress, stretched_pin, optiga_secret);
}
memzero(stretched_pin, sizeof(stretched_pin));
if (ret != OPTIGA_SUCCESS) {
memzero(optiga_secret, sizeof(optiga_secret));
@ -620,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;
@ -638,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));
@ -694,10 +754,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,
@ -734,6 +816,36 @@ secbool storage_pin_fails_increase(void) {
return pin_fails_increase();
}
static secbool pin_get_fails_sync(uint32_t *ctr) {
uint32_t ctr_mcu = 0;
if (sectrue != pin_get_fails(&ctr_mcu)) {
return secfalse;
}
#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");
while (ctr_mcu < ctr_optiga) {
storage_pin_fails_increase();
ctr_mcu++;
}
if (ctr_optiga < ctr_mcu) {
ensure(optiga_pin_fails_increase(ctr_mcu - ctr_optiga) == OPTIGA_SUCCESS
? sectrue
: secfalse,
"optiga_pin_fails_increase failed");
}
#endif
*ctr = ctr_mcu;
return sectrue;
}
secbool storage_is_unlocked(void) {
if (sectrue != initialized) {
return secfalse;
@ -812,7 +924,10 @@ secbool check_storage_version(void) {
return sectrue;
}
static secbool decrypt_dek(const uint8_t *kek, const uint8_t *keiv) {
static secbool decrypt_dek(const uint8_t *pin, size_t pin_len,
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;
uint16_t len = 0;
if (sectrue != initialized ||
@ -822,12 +937,22 @@ static secbool decrypt_dek(const uint8_t *kek, const uint8_t *keiv) {
return secfalse;
}
const uint8_t *rand_salt = (const uint8_t *)buffer;
const uint8_t *ekeys = (const uint8_t *)buffer + STORAGE_SALT_SIZE;
const uint32_t *pvc = (const uint32_t *)buffer +
(STORAGE_SALT_SIZE + KEYS_SIZE) / sizeof(uint32_t);
_Static_assert(((STORAGE_SALT_SIZE + KEYS_SIZE) & 3) == 0, "PVC unaligned");
_Static_assert((PVC_SIZE & 3) == 0, "PVC size unaligned");
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,
pbkdf2_iterations, kek, keiv)) {
memzero(kek, sizeof(kek));
memzero(keiv, sizeof(keiv));
return secfalse;
}
uint8_t keys[KEYS_SIZE] = {0};
uint8_t tag[POLY1305_TAG_SIZE] __attribute__((aligned(sizeof(uint32_t))));
chacha20poly1305_ctx ctx = {0};
@ -837,6 +962,8 @@ static secbool decrypt_dek(const uint8_t *kek, const uint8_t *keiv) {
rfc7539_init(&ctx, kek, keiv);
chacha20poly1305_decrypt(&ctx, ekeys, keys, KEYS_SIZE);
rfc7539_finish(&ctx, 0, KEYS_SIZE, tag);
memzero(kek, sizeof(kek));
memzero(keiv, sizeof(keiv));
memzero(&ctx, sizeof(ctx));
wait_random();
if (secequal32(tag, pvc, PVC_SIZE) != sectrue) {
@ -867,8 +994,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;
ui_rem += PIN_DERIVE_MS;
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);
@ -877,9 +1004,11 @@ static secbool unlock(const uint8_t *pin, size_t pin_len,
// Now we can check for wipe code.
ensure_not_wipe_code(unlock_pin, unlock_pin_len);
// Get the pin failure counter
// Get the pin failure counter. To ensure correct exponential backoff,
// synchronize counters in case they diverged. However, we are unaware of any
// way how the Optiga counter could be ahead of the MCU counter.
uint32_t ctr = 0;
if (sectrue != pin_get_fails(&ctr)) {
if (sectrue != pin_get_fails_sync(&ctr)) {
memzero(&legacy_pin, sizeof(legacy_pin));
return secfalse;
}
@ -919,25 +1048,9 @@ static secbool unlock(const uint8_t *pin, size_t pin_len,
return secfalse;
}
// Read the random salt from EDEK_PVC_KEY and use it to derive the KEK and
// KEIV from the PIN.
const void *rand_salt = NULL;
uint16_t len = 0;
if (sectrue != initialized ||
sectrue != norcow_get(EDEK_PVC_KEY, &rand_salt, &len) ||
len != STORAGE_SALT_SIZE + KEYS_SIZE + PVC_SIZE) {
memzero(&legacy_pin, sizeof(legacy_pin));
handle_fault("no EDEK");
return secfalse;
}
uint8_t kek[SHA256_DIGEST_LENGTH] = {0};
uint8_t keiv[SHA256_DIGEST_LENGTH] = {0};
// Check whether the entered PIN is correct.
if (sectrue != derive_kek_unlock(unlock_pin, unlock_pin_len,
(const uint8_t *)rand_salt, ext_salt, kek,
keiv) ||
sectrue != decrypt_dek(kek, keiv)) {
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();
@ -958,8 +1071,6 @@ static secbool unlock(const uint8_t *pin, size_t pin_len,
return secfalse;
}
memzero(&legacy_pin, sizeof(legacy_pin));
memzero(kek, sizeof(kek));
memzero(keiv, sizeof(keiv));
// Check for storage upgrades that need to be performed after unlocking and
// check that the authenticated version number matches the unauthenticated
@ -983,7 +1094,7 @@ secbool storage_unlock(const uint8_t *pin, size_t pin_len,
return secfalse;
}
ui_total = PIN_DERIVE_MS;
ui_total = pin_derive_ms(true, PBKDF2_ITER_COUNT);
ui_rem = ui_total;
if (pin_len == 0) {
if (ui_message == NULL) {
@ -1235,32 +1346,12 @@ uint32_t storage_get_pin_rem(void) {
return 0;
}
uint32_t ctr_mcu = 0;
if (sectrue != pin_get_fails(&ctr_mcu)) {
uint32_t ctr = 0;
if (sectrue != pin_get_fails_sync(&ctr)) {
return 0;
}
#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");
while (ctr_mcu < ctr_optiga) {
storage_pin_fails_increase();
ctr_mcu++;
}
if (ctr_optiga < ctr_mcu) {
ensure(optiga_pin_fails_increase(ctr_mcu - ctr_optiga) == OPTIGA_SUCCESS
? sectrue
: secfalse,
"optiga_pin_fails_increase failed");
}
#endif
return PIN_MAX_TRIES - ctr_mcu;
return PIN_MAX_TRIES - ctr;
}
secbool storage_change_pin(const uint8_t *oldpin, size_t oldpin_len,
@ -1271,7 +1362,7 @@ secbool storage_change_pin(const uint8_t *oldpin, size_t oldpin_len,
return secfalse;
}
ui_total = 2 * PIN_DERIVE_MS;
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;
@ -1285,7 +1376,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) {
@ -1320,7 +1411,7 @@ secbool storage_change_wipe_code(const uint8_t *pin, size_t pin_len,
return secfalse;
}
ui_total = PIN_DERIVE_MS;
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;
@ -1479,14 +1570,15 @@ static secbool storage_upgrade(void) {
}
// Set EDEK_PVC_KEY and PIN_NOT_SET_KEY.
ui_total = PIN_DERIVE_MS;
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));
}
@ -1603,7 +1695,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;
}
}

Loading…
Cancel
Save