feat(core): Account for Optiga throttling delay in PIN countdown.

pull/4016/head
Andrew Kozlik 3 months ago committed by Andrew Kozlik
parent d82d5a1fe5
commit 9420b38a35

@ -0,0 +1 @@
Include Optiga throttling delay in PIN countdown.

@ -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',

@ -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
@ -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);
@ -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]);

@ -23,6 +23,7 @@
#include "hmac.h"
#include "memzero.h"
#include "optiga_commands.h"
#include "optiga_transport.h"
#include "rand.h"
#include "storage.h"
@ -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};
@ -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) {
@ -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
@ -417,7 +452,7 @@ static bool optiga_pin_stretch_common(
goto end;
}
ui_progress(250);
ui_progress();
hmac_sha256_Update(ctx, buffer, size);
@ -428,7 +463,7 @@ end:
}
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
@ -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
@ -503,15 +538,17 @@ end:
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
@ -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.
@ -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
@ -631,15 +668,17 @@ end:
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
@ -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,
@ -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.
@ -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;
}
@ -756,8 +796,9 @@ end:
}
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.
@ -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,
@ -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;
}

@ -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
@ -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;
@ -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;

@ -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.
@ -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);

@ -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

@ -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);
}

@ -0,0 +1 @@
../stm32f4/time_estimate.c

@ -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

@ -88,30 +88,29 @@ bool optiga_read_sec(uint8_t *sec) {
return true;
}
uint32_t optiga_estimate_time_ms(storage_pin_op_t op) { return 0; }
bool optiga_random_buffer(uint8_t *dest, size_t size) {
random_buffer(dest, size);
return true;
}
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]) {
ui_progress(OPTIGA_PIN_SET_MS);
return true;
}
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]) {
memcpy(out_secret, pin_secret, OPTIGA_PIN_SECRET_SIZE);
ui_progress(OPTIGA_PIN_VERIFY_MS);
return OPTIGA_PIN_SUCCESS;
}
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]) {
ui_progress(OPTIGA_PIN_VERIFY_MS);
return OPTIGA_PIN_SUCCESS;
}

@ -0,0 +1,25 @@
/*
* 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"
uint32_t time_estimate_pbkdf2_ms(uint32_t iterations) {
(void)iterations;
return 500;
}

@ -52,6 +52,7 @@ def stm32f4_common_files(env, defines, sources, paths):
"embed/trezorhal/stm32f4/secret.c",
"embed/trezorhal/stm32f4/systick.c",
"embed/trezorhal/stm32f4/supervise.c",
"embed/trezorhal/stm32f4/time_estimate.c",
"embed/trezorhal/stm32f4/random_delays.c",
"embed/trezorhal/stm32f4/rng.c",
"embed/trezorhal/stm32f4/vectortable.s",

@ -66,6 +66,7 @@ def stm32u5_common_files(env, defines, sources, paths):
"embed/trezorhal/stm32u5/random_delays.c",
"embed/trezorhal/stm32u5/rng.c",
"embed/trezorhal/stm32u5/tamper.c",
"embed/trezorhal/stm32u5/time_estimate.c",
"embed/trezorhal/stm32u5/trustzone.c",
"embed/trezorhal/stm32u5/vectortable.s",
]

@ -15,6 +15,7 @@ OBJS += oled.o
OBJS += random_delays.o
OBJS += rng.o
OBJS += supervise.o
OBJS += time_estimate.o
OBJS += usb21_standard.o
OBJS += usb_standard.o
OBJS += util.o

@ -26,6 +26,7 @@
#include "layout.h"
#include "oled.h"
#include "rng.h"
#include "timer.h"
#include "util.h"
uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN];
@ -98,6 +99,8 @@ void hal_delay(uint32_t ms) {
#endif
}
uint32_t hal_ticks_ms(void) { return timer_ms(); }
void drbg_init(void) {
uint8_t entropy[48] = {0};
random_buffer(entropy, sizeof(entropy));

@ -39,6 +39,7 @@ void show_pin_too_many_screen(void);
(((expr) == sectrue) ? (void)0 : __fatal_error(msg, __FILE__, __LINE__))
void hal_delay(uint32_t ms);
uint32_t hal_ticks_ms(void);
void drbg_init(void);
void drbg_reseed(const uint8_t *entropy, size_t len);

@ -0,0 +1,35 @@
/*
* 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
// MCU clock 120 MHz
#define MCU_CLOCK 120000000
uint32_t time_estimate_pbkdf2_ms(uint32_t iterations) {
#if EMULATOR
(void)iterations;
return 500;
#else
return PIN_PBKDF2_CYCLES_PER_ITER * iterations / (MCU_CLOCK / 1000);
#endif
}

@ -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

@ -31,6 +31,7 @@
#include "sha2.h"
#include "storage.h"
#include "storage_utils.h"
#include "time_estimate.h"
#if USE_OPTIGA
#include "optiga.h"
@ -88,22 +89,8 @@ const uint32_t V0_PIN_EMPTY = 1;
// The total number of iterations to use in PBKDF2.
#define PIN_ITER_COUNT 20000
// The number of milliseconds required to execute PBKDF2.
#define PIN_PBKDF2_MS 1280
// The number of milliseconds required to set the PIN.
#if USE_OPTIGA
#define PIN_SET_MS (PIN_PBKDF2_MS + OPTIGA_PIN_SET_MS)
#else
#define PIN_SET_MS PIN_PBKDF2_MS
#endif
// The number of milliseconds required to verify the PIN.
#if USE_OPTIGA
#define PIN_VERIFY_MS (PIN_PBKDF2_MS + OPTIGA_PIN_VERIFY_MS)
#else
#define PIN_VERIFY_MS PIN_PBKDF2_MS
#endif
// The minimum number of milliseconds between progress updates.
#define MIN_PROGRESS_UPDATE_MS 100
// The length of the hashed hardware salt in bytes.
#define HARDWARE_SALT_SIZE SHA256_DIGEST_LENGTH
@ -152,7 +139,8 @@ CONFIDENTIAL static secbool initialized = secfalse;
CONFIDENTIAL static secbool unlocked = secfalse;
static PIN_UI_WAIT_CALLBACK ui_callback = NULL;
static uint32_t ui_total = 0;
static uint32_t ui_rem = 0;
static uint32_t ui_begin = 0;
static uint32_t ui_next_update = 0;
static enum storage_ui_message_t ui_message = NO_MSG;
CONFIDENTIAL static uint8_t cached_keys[KEYS_SIZE] = {0};
CONFIDENTIAL static uint8_t *const cached_dek = cached_keys;
@ -458,31 +446,83 @@ static secbool is_not_wipe_code(const uint8_t *pin, size_t pin_len) {
return sectrue;
}
static void ui_total_init(uint32_t total_ms) {
ui_total = total_ms;
ui_rem = total_ms;
static uint32_t ui_estimate_time_ms(storage_pin_op_t op) {
uint32_t time_ms = 0;
#if USE_OPTIGA
time_ms += optiga_estimate_time_ms(op);
#endif
uint32_t pbkdf2_ms = time_estimate_pbkdf2_ms(PIN_ITER_COUNT);
switch (op) {
case STORAGE_PIN_OP_SET:
case STORAGE_PIN_OP_VERIFY:
time_ms += pbkdf2_ms;
break;
case STORAGE_PIN_OP_CHANGE:
time_ms += 2 * pbkdf2_ms;
break;
default:
return 1;
}
return time_ms;
}
static void ui_total_add(uint32_t added_ms) {
ui_total += added_ms;
ui_rem += added_ms;
static void ui_progress_init(storage_pin_op_t op) {
ui_total = ui_estimate_time_ms(op);
ui_next_update = 0;
}
static secbool ui_progress(uint32_t elapsed_ms) {
ui_rem -= elapsed_ms;
if (ui_callback && ui_message) {
uint32_t progress = 0;
if (ui_total < 1000000) {
progress = 1000 * (ui_total - ui_rem) / ui_total;
} else {
// Avoid overflow. Precise enough.
progress = (ui_total - ui_rem) / (ui_total / 1000);
}
// Round the remaining time to the nearest second.
return ui_callback((ui_rem + 500) / 1000, progress, ui_message);
} else {
static void ui_progress_add(uint32_t added_ms) { ui_total += added_ms; }
static secbool ui_progress(void) {
uint32_t now = hal_ticks_ms();
if (ui_callback == NULL || ui_message == 0 || now < ui_next_update) {
return secfalse;
}
// The UI dialog is initialized by calling ui_callback() with progress = 0. If
// this is the first call, i.e. ui_next_update == 0, then make sure that
// progress comes out exactly 0.
if (ui_next_update == 0) {
ui_begin = now;
}
ui_next_update = now + MIN_PROGRESS_UPDATE_MS;
uint32_t ui_elapsed = now - ui_begin;
// Round the remaining time to the nearest second.
uint32_t ui_rem_sec = (ui_total - ui_elapsed + 500) / 1000;
#ifndef TREZOR_EMULATOR
uint32_t progress = 0;
if (ui_total < 1000000) {
progress = 1000 * ui_elapsed / ui_total;
} else {
// Avoid uint32 overflow. Precise enough.
progress = ui_elapsed / (ui_total / 1000);
}
#else
// In the emulator we derive the progress from the number of remaining seconds
// to avoid flaky UI tests.
uint32_t ui_total_sec = (ui_total + 500) / 1000;
uint32_t progress = 1000 - 1000 * ui_rem_sec / ui_total_sec;
#endif
// Avoid reaching progress = 1000 or overflowing the total time, since calling
// ui_callback() with progress = 1000 terminates the UI dialog.
if (progress >= 1000) {
progress = 999;
ui_elapsed = ui_total;
}
return ui_callback(ui_rem_sec, progress, ui_message);
}
static void ui_progress_finish(void) {
// The UI dialog is terminated by calling ui_callback() with progress = 1000.
if (ui_callback != NULL && ui_message != 0) {
ui_callback(0, 1000, ui_message);
}
}
#if !USE_OPTIGA
@ -510,7 +550,7 @@ static void derive_kek_v4(const uint8_t *pin, size_t pin_len,
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);
ui_progress();
}
#ifdef STM32U5
@ -527,7 +567,7 @@ static void derive_kek_v4(const uint8_t *pin, size_t pin_len,
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);
ui_progress();
}
pbkdf2_hmac_sha256_Final(&ctx, keiv);
@ -569,7 +609,7 @@ static void stretch_pin(const uint8_t *pin, size_t pin_len,
for (int i = 1; i <= 10; i++) {
pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10);
ui_progress(PIN_PBKDF2_MS / 10);
ui_progress();
}
#ifdef STM32U5
uint8_t stretched_pin_tmp[SHA256_DIGEST_LENGTH] = {0};
@ -683,7 +723,6 @@ static secbool set_pin(const uint8_t *pin, size_t pin_len,
uint8_t keiv[12] = {0};
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),
"derive_kek_set failed");
rfc7539_init(&ctx, kek, keiv);
@ -739,9 +778,10 @@ static void init_wiped_storage(void) {
ensure(set_wipe_code(WIPE_CODE_EMPTY, WIPE_CODE_EMPTY_LEN),
"set_wipe_code failed");
ui_total_init(PIN_SET_MS);
ui_progress_init(STORAGE_PIN_OP_SET);
ui_message = PROCESSING_MSG;
ensure(set_pin(PIN_EMPTY, PIN_EMPTY_LEN, NULL), "init_pin failed");
ui_progress_finish();
}
void storage_init(PIN_UI_WAIT_CALLBACK callback, const uint8_t *salt,
@ -938,7 +978,7 @@ static secbool unlock(const uint8_t *pin, size_t pin_len,
// In case of an upgrade from version 4 or earlier bump the total time of UI
// progress to account for the set_pin() call in storage_upgrade_unlocked().
if (get_lock_version() <= 4) {
ui_total_add(PIN_SET_MS);
ui_progress_add(ui_estimate_time_ms(STORAGE_PIN_OP_SET));
}
// Now we can check for wipe code.
@ -960,11 +1000,13 @@ static secbool unlock(const uint8_t *pin, size_t pin_len,
}
// Sleep for 2^ctr - 1 seconds before checking the PIN.
uint32_t wait = (1 << ctr) - 1;
ui_total_add(wait * 1000);
ui_progress(0);
for (uint32_t i = 0; i < 10 * wait; i++) {
if (sectrue == ui_progress(100)) {
uint32_t wait_ms = 1000 * ((1 << ctr) - 1);
ui_progress_add(wait_ms);
ui_progress();
uint32_t begin = hal_ticks_ms();
while (hal_ticks_ms() - begin < wait_ms) {
if (sectrue == ui_progress()) {
memzero(&legacy_pin, sizeof(legacy_pin));
return secfalse;
}
@ -995,10 +1037,10 @@ static secbool unlock(const uint8_t *pin, size_t pin_len,
show_pin_too_many_screen();
}
// Finish the countdown. Check for ui_rem underflow.
while (0 < ui_rem && ui_rem < ui_total) {
// Finish the countdown.
while (hal_ticks_ms() - ui_begin < ui_total) {
ui_message = WRONG_PIN_MSG;
if (sectrue == ui_progress(100)) {
if (sectrue == ui_progress()) {
return secfalse;
}
hal_delay(100);
@ -1030,7 +1072,7 @@ secbool storage_unlock(const uint8_t *pin, size_t pin_len,
return secfalse;
}
ui_total_init(PIN_VERIFY_MS);
ui_progress_init(STORAGE_PIN_OP_VERIFY);
if (pin_len == 0) {
if (ui_message == NO_MSG) {
ui_message = STARTING_MSG;
@ -1040,7 +1082,10 @@ secbool storage_unlock(const uint8_t *pin, size_t pin_len,
} else {
ui_message = VERIFYING_PIN_MSG;
}
return unlock(pin, pin_len, ext_salt);
secbool ret = unlock(pin, pin_len, ext_salt);
ui_progress_finish();
return ret;
}
/*
@ -1326,20 +1371,26 @@ secbool storage_change_pin(const uint8_t *oldpin, size_t oldpin_len,
return secfalse;
}
ui_total_init(PIN_VERIFY_MS + PIN_SET_MS);
ui_progress_init(STORAGE_PIN_OP_CHANGE);
ui_message =
(oldpin_len != 0 && newpin_len == 0) ? VERIFYING_PIN_MSG : PROCESSING_MSG;
if (sectrue != unlock(oldpin, oldpin_len, old_ext_salt)) {
return secfalse;
secbool ret = unlock(oldpin, oldpin_len, old_ext_salt);
if (sectrue != ret) {
goto end;
}
// Fail if the new PIN is the same as the wipe code.
if (sectrue != is_not_wipe_code(newpin, newpin_len)) {
return secfalse;
ret = is_not_wipe_code(newpin, newpin_len);
if (sectrue != ret) {
goto end;
}
return set_pin(newpin, newpin_len, new_ext_salt);
ret = set_pin(newpin, newpin_len, new_ext_salt);
end:
ui_progress_finish();
return ret;
}
void storage_ensure_not_wipe_code(const uint8_t *pin, size_t pin_len) {
@ -1374,14 +1425,19 @@ secbool storage_change_wipe_code(const uint8_t *pin, size_t pin_len,
return secfalse;
}
ui_total_init(PIN_VERIFY_MS);
ui_progress_init(STORAGE_PIN_OP_VERIFY);
ui_message =
(pin_len != 0 && wipe_code_len == 0) ? VERIFYING_PIN_MSG : PROCESSING_MSG;
secbool ret = secfalse;
if (sectrue == unlock(pin, pin_len, ext_salt)) {
ret = set_wipe_code(wipe_code, wipe_code_len);
secbool ret = unlock(pin, pin_len, ext_salt);
if (sectrue != ret) {
goto end;
}
ret = set_wipe_code(wipe_code, wipe_code_len);
end:
ui_progress_finish();
return ret;
}
@ -1550,15 +1606,17 @@ static secbool storage_upgrade(void) {
}
// Set EDEK_PVC_KEY and PIN_NOT_SET_KEY.
ui_total_init(PIN_SET_MS);
ui_message = PROCESSING_MSG;
uint8_t pin[V0_MAX_PIN_LEN] = {0};
size_t pin_len = 0;
secbool found = norcow_get(V0_PIN_KEY, &val, &len);
if (sectrue == found && *(const uint32_t *)val != V0_PIN_EMPTY) {
pin_len = int_to_pin(*(const uint32_t *)val, pin);
}
ui_progress_init(STORAGE_PIN_OP_SET);
ui_message = PROCESSING_MSG;
set_pin(pin, pin_len, NULL);
ui_progress_finish();
memzero(pin, sizeof(pin));
// Convert PIN failure counter.

@ -61,6 +61,12 @@ enum storage_ui_message_t {
WRONG_PIN_MSG,
};
typedef enum {
STORAGE_PIN_OP_SET = 0,
STORAGE_PIN_OP_VERIFY,
STORAGE_PIN_OP_CHANGE,
} storage_pin_op_t;
typedef secbool (*PIN_UI_WAIT_CALLBACK)(uint32_t wait, uint32_t progress,
enum storage_ui_message_t message);

@ -4,6 +4,7 @@ CFLAGS = -Wall -Wshadow -Wextra -Wpedantic -Werror -Wno-missing-braces
CFLAGS += -fPIC
CFALGS += -fsanitize=address,undefined
CFLAGS += -DTREZOR_MODEL_T
CFLAGS += -DTREZOR_EMULATOR
CFLAGS += -DUSE_INSECURE_PRNG
CFLAGS += -DCONFIDENTIAL=""

@ -23,6 +23,8 @@
#include "common.h"
static uint32_t ticks_ms = 0;
void __shutdown(void) {
printf("SHUTDOWN\n");
exit(3);
@ -41,3 +43,6 @@ void __fatal_error(const char *msg, const char *file, int line) {
void show_wipe_code_screen(void) {}
void show_pin_too_many_screen(void) {}
void hal_delay(uint32_t delay_ms) { ticks_ms += delay_ms; }
uint32_t hal_ticks_ms(void) { return ticks_ms; }

@ -30,6 +30,7 @@ void show_pin_too_many_screen(void);
#define ensure(expr, msg) \
(((expr) == sectrue) ? (void)0 : __fatal_error(msg, __FILE__, __LINE__))
#define hal_delay(ms) (void)ms;
void hal_delay(uint32_t delay_ms);
uint32_t hal_ticks_ms(void);
#endif

@ -0,0 +1,30 @@
/*
* 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) {
(void)iterations;
return 500;
}
#endif

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save