/* * This file is part of the Trezor project, https://trezor.io/ * * Copyright (c) SatoshiLabs * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "optiga.h" #include #include "hash_to_curve.h" #include "hmac.h" #include "memzero.h" #include "optiga_commands.h" #include "rand.h" #include "storage.h" // PIN counter reset key / Master secret (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). #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) // Key for AES-CMAC PIN stretching step (OID 0xE200). #define OID_PIN_CMAC OPTIGA_OID_SYM_KEY // 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. #define PIN_STRETCH_ITERATIONS 1 // 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}; 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 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); // Size of the DER BIT STRING header required for inputs to optiga_calc_ssec(). #define BIT_STRING_HEADER_SIZE 3 // Size of the CMAC/HMAC prefix returned by Optiga. #define ENCRYPT_SYM_PREFIX_SIZE 3 int optiga_sign(uint8_t index, const uint8_t *digest, size_t digest_size, uint8_t *signature, size_t max_sig_size, size_t *sig_size) { if (index >= OPTIGA_ECC_KEY_COUNT) { return OPTIGA_ERR_PARAM; } optiga_result ret = optiga_calc_sign(OPTIGA_OID_ECC_KEY + index, digest, digest_size, &signature[2], max_sig_size - 2, sig_size); if (ret == OPTIGA_ERR_CMD) { uint8_t error_code = 0; optiga_get_error_code(&error_code); return error_code + OPTIGA_COMMAND_ERROR_OFFSET; } if (ret != OPTIGA_SUCCESS) { return ret; } // Add sequence tag and length. if (*sig_size >= 0x80) { // Length not supported. return OPTIGA_ERR_SIZE; } signature[0] = 0x30; signature[1] = *sig_size; *sig_size += 2; return OPTIGA_SUCCESS; } bool optiga_cert_size(uint8_t index, size_t *cert_size) { *cert_size = 0; if (index >= OPTIGA_CERT_COUNT) { return false; } uint8_t metadata_bytes[OPTIGA_MAX_METADATA_SIZE] = {0}; size_t metadata_size = 0; optiga_metadata metadata = {0}; optiga_result ret = optiga_get_data_object(OPTIGA_OID_CERT + index, true, metadata_bytes, sizeof(metadata_bytes), &metadata_size); if (OPTIGA_SUCCESS != ret) { return false; } ret = optiga_parse_metadata(metadata_bytes, metadata_size, &metadata); if (OPTIGA_SUCCESS != ret || metadata.used_size.ptr == NULL) { return false; } for (int i = 0; i < metadata.used_size.len; ++i) { *cert_size = (*cert_size << 8) + metadata.used_size.ptr[i]; } return true; } bool optiga_read_cert(uint8_t index, uint8_t *cert, size_t max_cert_size, size_t *cert_size) { if (index >= OPTIGA_CERT_COUNT) { return false; } optiga_result ret = optiga_get_data_object(OPTIGA_OID_CERT + index, false, cert, max_cert_size, cert_size); return OPTIGA_SUCCESS == ret; } bool optiga_random_buffer(uint8_t *dest, size_t size) { while (size > OPTIGA_RANDOM_MAX_SIZE) { if (optiga_get_random(dest, OPTIGA_RANDOM_MAX_SIZE) != OPTIGA_SUCCESS) { return false; } dest += OPTIGA_RANDOM_MAX_SIZE; size -= OPTIGA_RANDOM_MAX_SIZE; } if (size < OPTIGA_RANDOM_MIN_SIZE) { static uint8_t buffer[OPTIGA_RANDOM_MIN_SIZE] = {0}; optiga_result ret = optiga_get_random(buffer, OPTIGA_RANDOM_MIN_SIZE); memcpy(dest, buffer, size); return ret == OPTIGA_SUCCESS; } return optiga_get_random(dest, size) == OPTIGA_SUCCESS; } static bool read_metadata(uint16_t oid, optiga_metadata *metadata) { static uint8_t serialized[OPTIGA_MAX_METADATA_SIZE] = {0}; size_t size = 0; optiga_result ret = optiga_get_data_object(oid, true, serialized, sizeof(serialized), &size); if (OPTIGA_SUCCESS != ret) { return false; } ret = optiga_parse_metadata(serialized, size, metadata); return OPTIGA_SUCCESS == ret; } static bool write_metadata(uint16_t oid, const optiga_metadata *metadata) { uint8_t serialized[OPTIGA_MAX_METADATA_SIZE] = {0}; size_t size = 0; optiga_result ret = optiga_serialize_metadata(metadata, serialized, sizeof(serialized), &size); if (OPTIGA_SUCCESS != ret) { return false; } ret = optiga_set_data_object(oid, true, serialized, size); return OPTIGA_SUCCESS == ret; } bool optiga_set_metadata(uint16_t oid, const optiga_metadata *metadata) { // Read the stored metadata. optiga_metadata metadata_stored = {0}; if (!read_metadata(oid, &metadata_stored)) { return false; } // If the stored metadata are different, then set them as requested. if (!optiga_compare_metadata(metadata, &metadata_stored)) { if (!write_metadata(oid, metadata)) { return false; } // Check that the metadata was written correctly. if (!read_metadata(oid, &metadata_stored)) { return false; } if (!optiga_compare_metadata(metadata, &metadata_stored)) { return false; } } // If the metadata aren't locked, then lock them. optiga_metadata metadata_locked = {0}; metadata_locked.lcso = OPTIGA_META_LCS_OPERATIONAL; if (!optiga_compare_metadata(&metadata_locked, &metadata_stored)) { if (!write_metadata(oid, &metadata_locked)) { return false; } // Check that metadata were locked correctly. if (!read_metadata(oid, &metadata_stored)) { return false; } if (!optiga_compare_metadata(&metadata_locked, &metadata_stored)) { return false; } } return true; } static bool optiga_pin_init_metadata(void) { optiga_metadata metadata = {0}; // Set metadata for PIN counter reset key / Master secret. memzero(&metadata, sizeof(metadata)); metadata.change = OPTIGA_META_ACCESS_ALWAYS; metadata.read = ACCESS_STRETCHED_PIN; metadata.execute = OPTIGA_META_ACCESS_ALWAYS; metadata.data_type = TYPE_AUTOREF; if (!optiga_set_metadata(OID_PIN_SECRET, &metadata)) { return false; } // Set metadata for PIN counter. 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)) { return false; } // Initialize the PIN stretching counter 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 (res != OPTIGA_SUCCESS) { return false; } } // Set metadata for PIN stretching counter. 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)) { return false; } // Set metadata for AES-CMAC PIN stretching secret. memzero(&metadata, sizeof(metadata)); metadata.change = OPTIGA_META_ACCESS_ALWAYS; metadata.read = OPTIGA_META_ACCESS_NEVER; metadata.execute = ACCESS_PIN_STRETCH_COUNTER; metadata.key_usage = OPTIGA_META_KEY_USE_ENC; if (!optiga_set_metadata(OID_PIN_CMAC, &metadata)) { return false; } // Set metadata for ECDH PIN stretching secret. memzero(&metadata, sizeof(metadata)); metadata.change = OPTIGA_META_ACCESS_ALWAYS; metadata.read = OPTIGA_META_ACCESS_NEVER; metadata.execute = ACCESS_PIN_STRETCH_COUNTER; 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; } static int optiga_pin_init_stretch(void) { // Generate a new key in OID_PIN_CMAC. optiga_result res = optiga_gen_sym_key(OPTIGA_AES_256, OPTIGA_KEY_USAGE_ENC, OID_PIN_CMAC); if (res != OPTIGA_SUCCESS) { return res; } // Generate a new key in OID_PIN_ECDH. uint8_t public_key[6 + 65] = {0}; size_t size = 0; res = optiga_gen_key_pair(OPTIGA_CURVE_P256, OPTIGA_KEY_USAGE_KEYAGREE, OID_PIN_ECDH, public_key, sizeof(public_key), &size); if (res != OPTIGA_SUCCESS) { return res; } return OPTIGA_SUCCESS; } static int optiga_pin_stretch_secret(OPTIGA_UI_PROGRESS ui_progress, uint8_t secret[OPTIGA_PIN_SECRET_SIZE]) { // This step hardens the PIN verification process in case an attacker is able // to extract the secret value of a data object in Optiga that has a // particular configuration, but does not allow secret extraction for other // kinds of data objects. An attacker would need to be able to extract each of // the secrets in the different data objects to conduct an offline brute-force // 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. // Pseudocode for the stretching process: // result_0 = secret // for i in range(PIN_STRETCH_ITERATIONS): // 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} HMAC_SHA256_CTX ctx = {0}; uint8_t result[OPTIGA_PIN_SECRET_SIZE] = {0}; memcpy(result, secret, sizeof(result)); uint8_t buffer[ENCRYPT_SYM_PREFIX_SIZE + OPTIGA_PIN_SECRET_SIZE] = {0}; size_t size = 0; for (int i = 0; i < PIN_STRETCH_ITERATIONS; ++i) { hmac_sha256_Init(&ctx, secret, OPTIGA_PIN_SECRET_SIZE); // Combine intermediate result with OID_PIN_CMAC. optiga_result res = optiga_encrypt_sym(OPTIGA_SYM_MODE_CMAC, OID_PIN_CMAC, result, sizeof(result), buffer, sizeof(buffer), &size); if (res != OPTIGA_SUCCESS) { memzero(buffer, sizeof(buffer)); memzero(result, sizeof(result)); memzero(&ctx, sizeof(ctx)); return res; } 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, result, sizeof(result), buffer, sizeof(buffer), &size); if (res != OPTIGA_SUCCESS) { memzero(buffer, sizeof(buffer)); memzero(result, sizeof(result)); memzero(&ctx, sizeof(ctx)); return res; } hmac_sha256_Update(&ctx, buffer, size); ui_progress(200); // Combine intermediate result with OID_PIN_ECDH uint8_t encoded_point[BIT_STRING_HEADER_SIZE + 65] = {0x03, 0x42, 0x00}; if (!hash_to_curve_optiga(result, &encoded_point[BIT_STRING_HEADER_SIZE])) { memzero(buffer, sizeof(buffer)); memzero(result, sizeof(result)); memzero(&ctx, sizeof(ctx)); return -1; } res = optiga_calc_ssec(OPTIGA_CURVE_P256, OID_PIN_ECDH, encoded_point, sizeof(encoded_point), buffer, sizeof(buffer), &size); memzero(encoded_point, sizeof(encoded_point)); if (res != OPTIGA_SUCCESS) { memzero(buffer, sizeof(buffer)); memzero(result, sizeof(result)); memzero(&ctx, sizeof(ctx)); return res; } hmac_sha256_Update(&ctx, buffer, size); hmac_sha256_Final(&ctx, result); ui_progress(200); } memcpy(secret, result, sizeof(result)); memzero(buffer, sizeof(buffer)); memzero(result, sizeof(result)); memzero(&ctx, sizeof(ctx)); return OPTIGA_SUCCESS; } int optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress, const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]) { if (!optiga_pin_init_metadata()) { return -1; } optiga_result res = optiga_pin_init_stretch(); if (res != OPTIGA_SUCCESS) { return res; } ui_progress(200); // Process the PIN-derived secret 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 PIN-derived secret. uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE] = {0}; hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, NULL, 0, stretched_pin); // Combine the result with stretching secrets from the Optiga. This step // ensures that if an attacker extracts the value of OID_STRETCHED_PIN, then // it cannot be used to conduct an offline brute-force search for the PIN. res = optiga_pin_stretch_secret(ui_progress, stretched_pin); if (res != OPTIGA_SUCCESS) { memzero(stretched_pin, sizeof(stretched_pin)); return res; } // Generate and store the master secret / PIN counter reset key. res = optiga_get_random(out_secret, OPTIGA_PIN_SECRET_SIZE); if (res != OPTIGA_SUCCESS) { memzero(stretched_pin, sizeof(stretched_pin)); return res; } random_xor(out_secret, OPTIGA_PIN_SECRET_SIZE); res = optiga_set_data_object(OID_PIN_SECRET, false, out_secret, OPTIGA_PIN_SECRET_SIZE); if (res != OPTIGA_SUCCESS) { memzero(stretched_pin, sizeof(stretched_pin)); return res; } // Authorise using OID_PIN_SECRET so that we can write to OID_PIN_COUNTER and // OID_STRETCHED_PIN. res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_PIN_SECRET, out_secret, OPTIGA_PIN_SECRET_SIZE); if (res != OPTIGA_SUCCESS) { memzero(stretched_pin, sizeof(stretched_pin)); return res; } // Set the stretched PIN. res = optiga_set_data_object(OID_STRETCHED_PIN, false, stretched_pin, sizeof(stretched_pin)); memzero(stretched_pin, sizeof(stretched_pin)); 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); // Combine the value of OID_PIN_SECRET with the PIN-derived secret and // stretching secrets from the Optiga. This step ensures that if an attacker // extracts the value of OID_PIN_SECRET, then it cannot be used to conduct an // offline brute-force search for the PIN. hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, out_secret, OPTIGA_PIN_SECRET_SIZE, out_secret); res = optiga_pin_stretch_secret(ui_progress, out_secret); if (res != OPTIGA_SUCCESS) { return res; } // Combine the stretched master secret with the PIN-derived secret to obtain // the output secret. This ensures that in the unlikely case of an attacker // recording communication between the MCU and Optiga, they cannot decrypt the // storage without having to conduct a brute-force search for the PIN. // NOTE: Considering how optiga_pin_stretch_secret() works internally and the // fact that the PIN was already combined with the value of OID_PIN_SECRET, // this step is not necessary. However, it is preferable to explicitly execute // it, than to rely on the internals of optiga_pin_stretch_secret(). hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, out_secret, OPTIGA_PIN_SECRET_SIZE, out_secret); // Recombining the returned secret with the PIN-derived secret means that if // the user chooses a high-entropy PIN, then even if the Optiga and its // communication link is completely compromised, it will not reduce the // security of their device any more than if the Optiga was not integrated // into the device in the first place. return OPTIGA_SUCCESS; } int optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress, const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]) { // Process the PIN-derived secret using a one-way function before sending it // to the Optiga. uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE] = {0}; hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, NULL, 0, stretched_pin); // Combine the result with stretching secrets from the Optiga. optiga_result res = optiga_pin_stretch_secret(ui_progress, stretched_pin); if (res != OPTIGA_SUCCESS) { memzero(stretched_pin, sizeof(stretched_pin)); return res; } // Authorise using OID_STRETCHED_PIN so that we can read from OID_PIN_SECRET. res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_STRETCHED_PIN, stretched_pin, sizeof(stretched_pin)); memzero(stretched_pin, sizeof(stretched_pin)); if (res == OPTIGA_ERR_CMD) { uint8_t error_code = 0; optiga_get_error_code(&error_code); return error_code + OPTIGA_COMMAND_ERROR_OFFSET; } if (res != OPTIGA_SUCCESS) { return res; } // Read the master secret from OID_PIN_SECRET. 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); if (res != OPTIGA_SUCCESS) { return res; } if (size != OPTIGA_PIN_SECRET_SIZE) { return OPTIGA_ERR_SIZE; } ui_progress(200); // 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, out_secret, OPTIGA_PIN_SECRET_SIZE); if (res != OPTIGA_SUCCESS) { 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 // stretching secrets from the Optiga. hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, out_secret, OPTIGA_PIN_SECRET_SIZE, out_secret); res = optiga_pin_stretch_secret(ui_progress, out_secret); if (res != OPTIGA_SUCCESS) { return res; } // Combine the stretched master secret with the PIN-derived secret to derive // the output secret. hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, out_secret, OPTIGA_PIN_SECRET_SIZE, out_secret); return OPTIGA_SUCCESS; } static int optiga_get_counter(uint16_t oid, uint32_t *ctr) { uint8_t counter[8] = {0}; size_t counter_size = 0; optiga_result res = optiga_get_data_object(oid, false, counter, sizeof(counter), &counter_size); if (res != OPTIGA_SUCCESS) { return res; } if (counter_size != sizeof(counter)) { return OPTIGA_ERR_SIZE; } *ctr = counter[0]; *ctr = (*ctr << 8) + counter[1]; *ctr = (*ctr << 8) + counter[2]; *ctr = (*ctr << 8) + counter[3]; return OPTIGA_SUCCESS; } int optiga_pin_get_fails(uint32_t *ctr) { return optiga_get_counter(OID_PIN_COUNTER, ctr); } int optiga_pin_fails_increase(uint32_t count) { if (count > 0xff) { return OPTIGA_ERR_PARAM; } return optiga_count_data_object(OID_PIN_COUNTER, count); }