Skip to content

Commit

Permalink
feat(core): Account for Optiga throttling delay in PIN countdown.
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewkozlik committed Jul 9, 2024
1 parent d82d5a1 commit 9420b38
Show file tree
Hide file tree
Showing 26 changed files with 2,618 additions and 2,317 deletions.
1 change: 1 addition & 0 deletions core/.changelog.d/4000.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Include Optiga throttling delay in PIN countdown.
1 change: 1 addition & 0 deletions core/SConscript.unix
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ SOURCE_UNIX = [
'embed/trezorhal/unix/flash.c',
'embed/trezorhal/unix/random_delays.c',
'embed/trezorhal/unix/rng.c',
'embed/trezorhal/unix/time_estimate.c',
'embed/trezorhal/unix/usb.c',
'embed/unix/main_main.c',
'embed/unix/main.c',
Expand Down
18 changes: 6 additions & 12 deletions core/embed/trezorhal/optiga.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include <stddef.h>
#include <stdint.h>
#include "optiga_common.h"
#include "secbool.h"
#include "storage.h"

#define OPTIGA_DEVICE_CERT_INDEX 1
#define OPTIGA_DEVICE_ECC_KEY_INDEX 0
Expand All @@ -46,14 +46,6 @@ typedef enum _optiga_sign_result {
// Size of secrets used in PIN processing, e.g. salted PIN, master secret etc.
#define OPTIGA_PIN_SECRET_SIZE 32

// The number of milliseconds it takes to execute optiga_pin_set().
#define OPTIGA_PIN_SET_MS 1300

// The number of milliseconds it takes to execute optiga_pin_verify().
#define OPTIGA_PIN_VERIFY_MS 900

typedef secbool (*OPTIGA_UI_PROGRESS)(uint32_t elapsed_ms);

optiga_sign_result __wur 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);
Expand All @@ -67,15 +59,17 @@ bool __wur optiga_read_sec(uint8_t *sec);

bool __wur optiga_random_buffer(uint8_t *dest, size_t size);

bool __wur optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress,
bool __wur optiga_pin_set(optiga_ui_progress_t ui_progress,
uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]);

uint32_t optiga_estimate_time_ms(storage_pin_op_t op);

optiga_pin_result __wur
optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress,
optiga_pin_verify(optiga_ui_progress_t ui_progress,
uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]);

optiga_pin_result __wur
optiga_pin_verify_v4(OPTIGA_UI_PROGRESS ui_progress,
optiga_pin_verify_v4(optiga_ui_progress_t ui_progress,
const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE],
uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]);

Expand Down
70 changes: 56 additions & 14 deletions core/embed/trezorhal/optiga/optiga.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "hmac.h"
#include "memzero.h"
#include "optiga_commands.h"
#include "optiga_transport.h"
#include "rand.h"
#include "storage.h"

Expand Down Expand Up @@ -59,6 +60,9 @@
// The number of times that PIN stretching is repeated.
#define PIN_STRETCH_ITERATIONS 2

// The throttling delay when the security event counter is at its maximum.
#define OPTIGA_T_MAX_MS 5000

// 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};

Expand Down Expand Up @@ -172,6 +176,37 @@ bool optiga_read_sec(uint8_t *sec) {
return ret == OPTIGA_SUCCESS && size == sizeof(uint8_t);
}

uint32_t optiga_estimate_time_ms(storage_pin_op_t op) {
uint8_t sec = 0;
if (!optiga_read_sec(&sec)) {
return UINT32_MAX;
}

// Heuristic: The SEC will increase by about 4 during the operation up to a
// maximum of 255.
sec = (sec < 255 - 4) ? sec + 4 : 255;

// If the SEC is above 127, then Optiga introduces a throttling delay before
// the execution of each protected command. The delay grows propotionally to
// the SEC value up to a maximum delay of OPTIGA_T_MAX_MS.
uint32_t throttling_delay =
sec > 127 ? (sec - 127) * OPTIGA_T_MAX_MS / 128 : 0;

// To estimate the overall time of the PIN operation we multiply the
// throttling delay by the number of protected Optiga commands and add the
// time required to execute all Optiga commands without throttling delays.
switch (op) {
case STORAGE_PIN_OP_SET:
return throttling_delay * 6 + 1300;
case STORAGE_PIN_OP_VERIFY:
return throttling_delay * 7 + 1000;
case STORAGE_PIN_OP_CHANGE:
return throttling_delay * 13 + 2300;
default:
return 0;
}
}

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) {
Expand Down Expand Up @@ -371,7 +406,7 @@ static bool optiga_pin_init_stretch(void) {
}

static bool optiga_pin_stretch_common(
OPTIGA_UI_PROGRESS ui_progress, HMAC_SHA256_CTX *ctx,
optiga_ui_progress_t ui_progress, HMAC_SHA256_CTX *ctx,
const uint8_t input[OPTIGA_PIN_SECRET_SIZE], bool version4) {
// Implements the functionality that is common to
// optiga_pin_stretch_cmac_ecdh() and the legacy function
Expand Down Expand Up @@ -417,7 +452,7 @@ static bool optiga_pin_stretch_common(
goto end;
}

ui_progress(250);
ui_progress();

hmac_sha256_Update(ctx, buffer, size);

Expand All @@ -428,7 +463,7 @@ static bool optiga_pin_stretch_common(
}

static bool optiga_pin_stretch_secret_v4(
OPTIGA_UI_PROGRESS ui_progress, uint8_t secret[OPTIGA_PIN_SECRET_SIZE]) {
optiga_ui_progress_t ui_progress, uint8_t secret[OPTIGA_PIN_SECRET_SIZE]) {
// Legacy PIN verification method used in storage versions 3 and 4.

// This step hardens the PIN verification process in case an attacker is able
Expand Down Expand Up @@ -459,7 +494,7 @@ static bool optiga_pin_stretch_secret_v4(
}

static bool optiga_pin_stretch_cmac_ecdh(
OPTIGA_UI_PROGRESS ui_progress,
optiga_ui_progress_t ui_progress,
uint8_t stretched_pin[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
Expand Down Expand Up @@ -503,15 +538,17 @@ static bool optiga_pin_stretch_cmac_ecdh(
return ret;
}

bool optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress,
bool optiga_pin_set(optiga_ui_progress_t ui_progress,
uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) {
optiga_set_ui_progress(ui_progress);

bool ret = true;
if (!optiga_pin_init_metadata() || !optiga_pin_init_stretch()) {
ret = false;
goto end;
}

ui_progress(300);
ui_progress();

// Stretch the PIN more with stretching secrets from the Optiga. This step
// ensures that if an attacker extracts the value of OID_STRETCHED_PIN or
Expand Down Expand Up @@ -590,7 +627,7 @@ bool optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress,
goto end;
}

ui_progress(250);
ui_progress();

// Authorise using OID_STRETCHED_PIN so that we can write to OID_PIN_HMAC and
// OID_PIN_HMAC_CTR.
Expand All @@ -614,7 +651,7 @@ bool optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress,
goto end;
}

ui_progress(250);
ui_progress();

// 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
Expand All @@ -631,15 +668,17 @@ bool optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress,
memzero(digest, sizeof(digest));
optiga_clear_auto_state(OID_PIN_SECRET);
optiga_clear_auto_state(OID_STRETCHED_PIN);
optiga_set_ui_progress(NULL);
return ret;
}

optiga_pin_result optiga_pin_verify_v4(
OPTIGA_UI_PROGRESS ui_progress,
optiga_ui_progress_t ui_progress,
const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE],
uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]) {
// Legacy PIN verification method used in storage version 3 and 4.

optiga_set_ui_progress(ui_progress);
optiga_pin_result ret = OPTIGA_PIN_SUCCESS;

// Process the PIN-derived secret using a one-way function before sending it
Expand Down Expand Up @@ -687,7 +726,7 @@ optiga_pin_result optiga_pin_verify_v4(
goto end;
}

ui_progress(200);
ui_progress();

// Authorise using OID_PIN_SECRET so that we can write to OID_PIN_COUNTER.
if (optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_PIN_SECRET, out_secret,
Expand All @@ -696,7 +735,7 @@ optiga_pin_result optiga_pin_verify_v4(
goto end;
}

ui_progress(200);
ui_progress();

// Combine the value of OID_PIN_SECRET with the PIN-derived secret and
// stretching secrets from the Optiga.
Expand All @@ -715,6 +754,7 @@ optiga_pin_result optiga_pin_verify_v4(
end:
memzero(stretched_pin, sizeof(stretched_pin));
optiga_clear_auto_state(OID_STRETCHED_PIN);
optiga_set_ui_progress(NULL);
return ret;
}

Expand Down Expand Up @@ -756,8 +796,9 @@ static optiga_pin_result optiga_pin_stretch_hmac(
}

optiga_pin_result optiga_pin_verify(
OPTIGA_UI_PROGRESS ui_progress,
optiga_ui_progress_t ui_progress,
uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) {
optiga_set_ui_progress(ui_progress);
optiga_pin_result ret = OPTIGA_PIN_SUCCESS;

// Stretch the PIN more with stretching secrets from the Optiga.
Expand Down Expand Up @@ -801,7 +842,7 @@ optiga_pin_result optiga_pin_verify(
goto end;
}

ui_progress(200);
ui_progress();

// Reset the counter which limits the use of OID_PIN_HMAC.
if (optiga_set_data_object(OID_PIN_HMAC_CTR, false, COUNTER_RESET,
Expand Down Expand Up @@ -837,13 +878,14 @@ optiga_pin_result optiga_pin_verify(
goto end;
}

ui_progress(200);
ui_progress();

end:
memzero(pin_secret, sizeof(pin_secret));
memzero(digest, sizeof(digest));
optiga_clear_auto_state(OID_STRETCHED_PIN);
optiga_clear_auto_state(OID_PIN_SECRET);
optiga_set_ui_progress(NULL);
return ret;
}

Expand Down
7 changes: 7 additions & 0 deletions core/embed/trezorhal/optiga/optiga_transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ static uint8_t frame_num_out = 0xff;
static uint8_t frame_num_in = 0xff;
static uint8_t frame_buffer[1 + OPTIGA_DATA_REG_LEN];
static size_t frame_size = 0; // Set by optiga_read().
static optiga_ui_progress_t ui_progress = NULL;

// Secure channel constants.
#define SEC_CHAN_SCTR_SIZE 1
Expand Down Expand Up @@ -169,6 +170,8 @@ void optiga_transport_set_log_hex(optiga_log_hex_t f) { log_hex = f; }
}
#endif

void optiga_set_ui_progress(optiga_ui_progress_t f) { ui_progress = f; }

static uint16_t calc_crc_byte(uint16_t seed, uint8_t c) {
uint16_t h1 = (seed ^ c) & 0xFF;
uint16_t h2 = h1 & 0x0F;
Expand Down Expand Up @@ -430,6 +433,10 @@ static optiga_result optiga_read(void) {
}
not_busy_count += 1;
}

if (ui_progress != NULL) {
ui_progress();
}
} while (hal_ticks_ms() < deadline);

return OPTIGA_ERR_TIMEOUT;
Expand Down
4 changes: 4 additions & 0 deletions core/embed/trezorhal/optiga_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#ifndef TREZORHAL_OPTIGA_COMMON_H
#define TREZORHAL_OPTIGA_COMMON_H

#include "secbool.h"

typedef enum _optiga_result {
OPTIGA_SUCCESS = 0, // Operation completed successfully.
OPTIGA_ERR_I2C_WRITE, // HAL failed on I2C write.
Expand All @@ -34,6 +36,8 @@ typedef enum _optiga_result {
OPTIGA_ERR_CMD, // Command error. See error code data object 0xF1C2.
} optiga_result;

typedef secbool (*optiga_ui_progress_t)(void);

#if !PRODUCTION
typedef void (*optiga_log_hex_t)(const char *prefix, const uint8_t *data,
size_t data_size);
Expand Down
2 changes: 2 additions & 0 deletions core/embed/trezorhal/optiga_transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ optiga_result optiga_resync(void);
optiga_result optiga_soft_reset(void);
optiga_result optiga_set_data_reg_len(size_t size);

void optiga_set_ui_progress(optiga_ui_progress_t f);

#if !PRODUCTION
void optiga_transport_set_log_hex(optiga_log_hex_t f);
#endif
Expand Down
28 changes: 28 additions & 0 deletions core/embed/trezorhal/stm32f4/time_estimate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

#include "time_estimate.h"

// The number of CPU cycles required to execute one iteration of PBKDF2.
#define PIN_PBKDF2_CYCLES_PER_ITER 11100

uint32_t time_estimate_pbkdf2_ms(uint32_t iterations) {
extern uint32_t SystemCoreClock;
return PIN_PBKDF2_CYCLES_PER_ITER * iterations / (SystemCoreClock / 1000);
}
1 change: 1 addition & 0 deletions core/embed/trezorhal/stm32u5/time_estimate.c
27 changes: 27 additions & 0 deletions core/embed/trezorhal/time_estimate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifndef TREZORHAL_TIME_ESTIMATE_H
#define TREZORHAL_TIME_ESTIMATE_H

#include <stdint.h>

uint32_t time_estimate_pbkdf2_ms(uint32_t iterations);

#endif
Loading

0 comments on commit 9420b38

Please sign in to comment.