diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader
index a980acb30c..c68d391de7 100644
--- a/core/SConscript.bootloader
+++ b/core/SConscript.bootloader
@@ -40,6 +40,7 @@ SOURCE_MOD += [
'vendor/trezor-crypto/secp256k1.c',
'vendor/trezor-crypto/memzero.c',
'vendor/trezor-crypto/rand.c',
+ 'vendor/trezor-crypto/hmac.c',
'vendor/trezor-crypto/sha2.c',
'vendor/trezor-crypto/aes/aes_modes.c',
'vendor/trezor-crypto/aes/aescrypt.c',
@@ -178,6 +179,7 @@ SOURCE_TREZORHAL = [
'embed/trezorhal/thd89.c',
'embed/trezorhal/thd89_boot.c',
'embed/trezorhal/se_thd89.c',
+ 'embed/trezorhal/se_thd89_v2.c',
'embed/trezorhal/util.s',
'embed/trezorhal/vectortable.s',
'embed/trezorhal/camera.c',
diff --git a/core/SConscript.firmware b/core/SConscript.firmware
index 591f7e6462..4646f244f8 100644
--- a/core/SConscript.firmware
+++ b/core/SConscript.firmware
@@ -495,6 +495,7 @@ if PRODUCTION_MODEL == 'H':
'embed/trezorhal/usbd_ulpi.c',
'embed/trezorhal/thd89.c',
'embed/trezorhal/se_thd89.c',
+ 'embed/trezorhal/se_thd89_v2.c',
'embed/trezorhal/trans_fifo.c',
'embed/trezorhal/usart.c',
'embed/trezorhal/motor.c',
diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c
index 9a9673a5a6..e31346d78d 100644
--- a/core/embed/bootloader/bootui.c
+++ b/core/embed/bootloader/bootui.c
@@ -272,8 +272,10 @@ void ui_screen_install_confirm_newvendor_or_downgrade_wipe(char* new_version) {
vendor_header current_vhdr;
image_header current_hdr;
// char str[128] = {0};
- if (sectrue == load_vendor_header((const uint8_t*)FIRMWARE_START, FW_KEY_M,
- FW_KEY_N, FW_KEYS, ¤t_vhdr)) {
+ if (sectrue ==
+ load_vendor_header((const uint8_t*)FIRMWARE_START,
+ VENDOR_HEADER_MAX_SIZE, FW_KEY_M, FW_KEY_N, FW_KEYS,
+ ¤t_vhdr)) {
if (sectrue ==
load_image_header((const uint8_t*)FIRMWARE_START + current_vhdr.hdrlen,
FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE,
@@ -365,6 +367,7 @@ static bool ui_progress_bar_visible = false;
void ui_screen_progress_bar_init(char* title, char* notes, int progress) {
ui_statusbar_update();
ui_logo_onekey();
+ ui_progress_bar_visible = true;
if (title != NULL) {
display_text_center(DISPLAY_RESX / 2, TITLE_OFFSET_Y, title, -1,
FONT_PJKS_BOLD_38, COLOR_BL_FG, COLOR_BL_BG);
@@ -407,6 +410,8 @@ void ui_screen_progress_bar_update(char* title, char* notes, int progress) {
}
}
+bool ui_progress_bar_is_visible(void) { return ui_progress_bar_visible; }
+
void ui_progress_bar_visible_clear(void) { ui_progress_bar_visible = false; }
void ui_screen_install_start(void) {
@@ -541,6 +546,7 @@ void ui_screen_fail(void) {
void ui_fadein(void) { display_fade(0, BACKLIGHT_NORMAL, 200); }
void ui_fadeout(void) {
+ ui_progress_bar_visible_clear();
display_fade(BACKLIGHT_NORMAL, 0, 200);
display_clear();
}
@@ -1041,6 +1047,21 @@ void ui_bootloader_first(const image_header* const hdr) {
}
}
+void ui_bootloader_se_version_required(const image_header* const hdr) {
+ ui_bootloader_first(hdr);
+ ui_logo_warning();
+ display_bar(0, SUBTITLE_OFFSET_Y - 10, DISPLAY_RESX, 150, COLOR_BL_BG);
+ display_text_center(DISPLAY_RESX / 2, SUBTITLE_OFFSET_Y,
+ "SE firmware update required", -1, FONT_NORMAL,
+ COLOR_BL_FG, COLOR_BL_BG);
+ display_text_center(DISPLAY_RESX / 2, SUBTITLE_OFFSET_Y + 36,
+ "Version 1.3.2 or later is required", -1, FONT_NORMAL,
+ COLOR_BL_SUBTITLE, COLOR_BL_BG);
+ display_text_center(DISPLAY_RESX / 2, SUBTITLE_OFFSET_Y + 72,
+ "Install an SE firmware update", -1, FONT_NORMAL,
+ COLOR_BL_SUBTITLE, COLOR_BL_BG);
+}
+
void ui_bootloader_main_menu(const image_header* const hdr) {
ui_bootloader_page_current = 5;
diff --git a/core/embed/bootloader/bootui.h b/core/embed/bootloader/bootui.h
index 398e53fcca..814ccf903f 100644
--- a/core/embed/bootloader/bootui.h
+++ b/core/embed/bootloader/bootui.h
@@ -55,6 +55,7 @@ void ui_screen_confirm(char* title, char* note_l1, char* note_l2, char* note_l3,
void ui_screen_progress_bar_init(char* title, char* notes, int progress);
void ui_screen_progress_bar_prepare(char* title, char* notes);
void ui_screen_progress_bar_update(char* msg_status, char* notes, int progress);
+bool ui_progress_bar_is_visible(void);
void ui_progress_bar_visible_clear(void);
void ui_screen_wipe_confirm(void);
@@ -92,6 +93,7 @@ int ui_input_poll(int zones, bool poll);
int ui_input_match(int zones, uint32_t evt);
void ui_bootloader_simple(void);
void ui_bootloader_first(const image_header* const hdr);
+void ui_bootloader_se_version_required(const image_header* const hdr);
void ui_bootloader_view_details(const image_header* const hdr);
void ui_wipe_confirm(const image_header* const hdr);
void ui_show_version_info(int y, char* current_ver, char* new_ver);
diff --git a/core/embed/bootloader/header.S b/core/embed/bootloader/header.S
index 9487ccb033..f1a1afcb97 100644
--- a/core/embed/bootloader/header.S
+++ b/core/embed/bootloader/header.S
@@ -23,7 +23,15 @@ g_header:
. = . + 8 // reserved
. = . + 512 // hash1 ... hash16
. = . + 399 // reserved
- .string BUILD_COMMIT // commit_id
+ // Keep this field aligned with image_header.build_id[16].
+g_build_id:
+ .string BUILD_COMMIT // NUL-terminated commit ID
+g_build_id_end:
+ .if (g_build_id_end - g_build_id) > 16
+ .error "BUILD_COMMIT must be at most 15 characters"
+ .else
+ .fill 16 - (g_build_id_end - g_build_id), 1, 0
+ .endif
.byte 0 // sigmask
. = . + 64 // sig
g_header_end:
diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c
index b34da1b896..47e7504107 100644
--- a/core/embed/bootloader/main.c
+++ b/core/embed/bootloader/main.c
@@ -17,6 +17,7 @@
* along with this program. If not, see .
*/
+#include
#include
#include
@@ -59,6 +60,10 @@
#define MSG_NAME_TO_ID(x) MessageType_MessageType_##x
+#define REQUIRED_SE_VERSION_MAJOR 1
+#define REQUIRED_SE_VERSION_MINOR 3
+#define REQUIRED_SE_VERSION_PATCH 2
+
#if defined(STM32H747xx)
#include "stm32h7xx_hal.h"
#endif
@@ -755,9 +760,15 @@ int main(void) {
thd89_init();
uint8_t se_mode = se_get_state();
- // all se in app mode
+ secbool se_version_supported = sectrue;
+
if (se_mode == 0) {
- device_para_init();
+ se_version_supported = se_all_versions_at_least(
+ REQUIRED_SE_VERSION_MAJOR, REQUIRED_SE_VERSION_MINOR,
+ REQUIRED_SE_VERSION_PATCH);
+ if (se_version_supported == sectrue) {
+ device_para_init();
+ }
}
if ((!device_serial_set() || !se_has_cerrificate()) && se_mode == 0) {
@@ -812,18 +823,30 @@ int main(void) {
BOOT_TARGET boot_target =
decide_boot_target(&vhdr, &hdr, &vhdr_valid, &hdr_valid, &code_valid);
+ if (boot_target == BOOT_TARGET_NORMAL &&
+ se_version_supported != sectrue) {
+ boot_target = BOOT_TARGET_BOOTLOADER;
+ }
// boot_target = BOOT_TARGET_BOOTLOADER;
if (boot_target == BOOT_TARGET_BOOTLOADER) {
display_clear();
if (sectrue == vhdr_valid && sectrue == hdr_valid) {
- ui_bootloader_first(&hdr);
+ if (se_version_supported == sectrue) {
+ ui_bootloader_first(&hdr);
+ } else {
+ ui_bootloader_se_version_required(&hdr);
+ }
if (bootloader_usb_loop(&vhdr, &hdr) != sectrue) {
return 1;
}
} else {
- ui_bootloader_first(NULL);
+ if (se_version_supported == sectrue) {
+ ui_bootloader_first(NULL);
+ } else {
+ ui_bootloader_se_version_required(NULL);
+ }
if (bootloader_usb_loop(NULL, NULL) != sectrue) {
return 1;
}
diff --git a/core/embed/bootloader/memory.ld b/core/embed/bootloader/memory.ld
index 110fc10f15..733e775ff1 100644
--- a/core/embed/bootloader/memory.ld
+++ b/core/embed/bootloader/memory.ld
@@ -31,6 +31,8 @@ SECTIONS {
.header : ALIGN(4) {
KEEP(*(.header));
} >FLASH AT>FLASH
+ ASSERT(SIZEOF(.header) == 0x400,
+ "bootloader header must be exactly 0x400 bytes")
.flash : ALIGN(512) {
KEEP(*(.vector_table));
@@ -51,7 +53,7 @@ SECTIONS {
.padding : {
KEEP(*(.padding));
FILL(0xDEADBEEF); /* fill pattern */
- . = LENGTH(FLASH) - SIZEOF(.header) - SIZEOF(.flash) - SIZEOF(.data) - 1 - 8;
+ . = LENGTH(FLASH) - SIZEOF(.header) - SIZEOF(.flash) - SIZEOF(.data) - 1;
BYTE(0x00); /* needed to keep this section without references in .s or .c */
} >FLASH AT>FLASH
diff --git a/core/embed/emmc_wrapper/emmc_commands.c b/core/embed/emmc_wrapper/emmc_commands.c
index eb7b68b38d..b881086c8b 100644
--- a/core/embed/emmc_wrapper/emmc_commands.c
+++ b/core/embed/emmc_wrapper/emmc_commands.c
@@ -1,5 +1,6 @@
#include "emmc_commands.h"
#include "emmc_commands_macros.h"
+#include "emmc_ui_progress.h"
#include "bootui.h"
#include "fw_keys.h"
@@ -10,28 +11,30 @@
// SDRAM BUFFER
bootloader_buffer* bl_buffer = (bootloader_buffer*)FMC_SDRAM_BOOLOADER_BUFFER_ADDRESS;
+static emmc_ui_progress_state ui_progress_state = EMMC_UI_PROGRESS_STATE_INIT;
+
static int ui_progress_bar_handle_update(int percentage, const char* title)
{
- static uint32_t ui_percentage_last = 0xff;
char* progress_title = (char*)((title != NULL) ? title : "Transferring Data");
+ emmc_ui_progress_action action = emmc_ui_progress_next_action(
+ &ui_progress_state, true, percentage, ui_progress_bar_is_visible()
+ );
- if ( (percentage < 0) || (percentage > 100) )
+ if ( action == EMMC_UI_PROGRESS_ACTION_INVALID )
{
return -1;
}
- if ( ui_percentage_last == (uint32_t)percentage )
+ if ( action == EMMC_UI_PROGRESS_ACTION_NONE )
{
return 0;
}
- ui_percentage_last = (uint32_t)percentage;
-
- if ( percentage < 100 )
+ if ( action == EMMC_UI_PROGRESS_ACTION_UPDATE )
{
ui_screen_progress_bar_update(progress_title, NULL, percentage);
}
- else
+ else if ( action == EMMC_UI_PROGRESS_ACTION_COMPLETE )
{
ui_screen_progress_bar_update(progress_title, NULL, percentage);
ui_fadeout();
@@ -44,9 +47,15 @@ static int ui_progress_bar_handle_update(int percentage, const char* title)
static void ui_progress_bar_handle_clear(void)
{
- ui_progress_bar_visible_clear();
- display_clear();
- ui_bootloader_first(NULL);
+ emmc_ui_progress_action action = emmc_ui_progress_next_action(
+ &ui_progress_state, false, 0, ui_progress_bar_is_visible()
+ );
+
+ if ( action == EMMC_UI_PROGRESS_ACTION_CLEAR )
+ {
+ display_clear();
+ ui_bootloader_first(NULL);
+ }
}
static void packet_generate_first(
@@ -649,7 +658,7 @@ static int check_file_contents(uint8_t iface_num, const uint8_t* buffer, uint32_
// check firmware header
// check file header
ExecuteCheck_MSGS_ADV(
- load_vendor_header(p_data, FW_KEY_M, FW_KEY_N, FW_KEYS, &file_vhdr), sectrue,
+ load_vendor_header(p_data, buffer_len, FW_KEY_M, FW_KEY_N, FW_KEYS, &file_vhdr), sectrue,
{
send_failure(
iface_num, FailureType_Failure_ProcessError, "Update file vendor header invalid!"
@@ -657,6 +666,11 @@ static int check_file_contents(uint8_t iface_num, const uint8_t* buffer, uint32_
return -1;
}
);
+ if ( file_vhdr.hdrlen > buffer_len || buffer_len - file_vhdr.hdrlen < IMAGE_HEADER_SIZE )
+ {
+ send_failure(iface_num, FailureType_Failure_ProcessError, "Update file header truncated!");
+ return -1;
+ }
ExecuteCheck_MSGS_ADV(
load_image_header(
p_data + file_vhdr.hdrlen, FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE, file_vhdr.vsig_m,
@@ -669,6 +683,14 @@ static int check_file_contents(uint8_t iface_num, const uint8_t* buffer, uint32_
}
);
+ uint32_t firmware_item_len = file_vhdr.hdrlen + file_hdr.hdrlen;
+ if ( firmware_item_len > buffer_len || file_hdr.codelen > buffer_len - firmware_item_len )
+ {
+ send_failure(iface_num, FailureType_Failure_ProcessError, "Firmware file truncated!");
+ return -1;
+ }
+ firmware_item_len += file_hdr.codelen;
+
if ( file_hdr.codelen - (FIRMWARE_IMAGE_INNER_SIZE - (file_vhdr.hdrlen + file_hdr.hdrlen)) >
FMC_SDRAM_FIRMWARE_P2_LEN )
{
@@ -691,7 +713,7 @@ static int check_file_contents(uint8_t iface_num, const uint8_t* buffer, uint32_
// check file size
ExecuteCheck_MSGS_ADV(
- (file_vhdr.hdrlen + file_hdr.hdrlen + file_hdr.codelen <= FIRMWARE_IMAGE_MAXSIZE), true,
+ (firmware_item_len <= FIRMWARE_IMAGE_MAXSIZE), true,
{
send_failure(iface_num, FailureType_Failure_ProcessError, "Firmware file is too big!");
return -1;
@@ -703,7 +725,8 @@ static int check_file_contents(uint8_t iface_num, const uint8_t* buffer, uint32_
update_info.mcu_update_info.purpose_changed = secfalse;
// vhdr
if ( load_vendor_header(
- (const uint8_t*)FIRMWARE_START, FW_KEY_M, FW_KEY_N, FW_KEYS, ¤t_vhdr
+ (const uint8_t*)FIRMWARE_START, VENDOR_HEADER_MAX_SIZE, FW_KEY_M, FW_KEY_N, FW_KEYS,
+ ¤t_vhdr
) == sectrue )
{
if ( load_image_header(
@@ -773,14 +796,13 @@ static int check_file_contents(uint8_t iface_num, const uint8_t* buffer, uint32_
update_info.items[update_info.item_count].type = UPDATE_MCU;
update_info.items[update_info.item_count].offset = p_data - buffer;
- update_info.items[update_info.item_count].length =
- file_vhdr.hdrlen + file_hdr.hdrlen + file_hdr.codelen;
+ update_info.items[update_info.item_count].length = firmware_item_len;
update_info.item_count++;
update_info.mcu_location = update_info.item_count;
update_info.mcu_update_info.purpose = file_hdr.purpose;
- p_data += file_vhdr.hdrlen + file_hdr.hdrlen + file_hdr.codelen;
- buffer_len -= file_vhdr.hdrlen + file_hdr.hdrlen + file_hdr.codelen;
+ p_data += firmware_item_len;
+ buffer_len -= firmware_item_len;
continue;
}
// SE
diff --git a/core/embed/emmc_wrapper/emmc_ui_progress.c b/core/embed/emmc_wrapper/emmc_ui_progress.c
new file mode 100644
index 0000000000..edc6430718
--- /dev/null
+++ b/core/embed/emmc_wrapper/emmc_ui_progress.c
@@ -0,0 +1,39 @@
+#include "emmc_ui_progress.h"
+
+static void emmc_ui_progress_state_reset(emmc_ui_progress_state* state)
+{
+ state->last_percentage = 0;
+ state->has_last_percentage = false;
+}
+
+emmc_ui_progress_action emmc_ui_progress_next_action(
+ emmc_ui_progress_state* state, bool has_percentage, int percentage, bool progress_visible
+)
+{
+ if ( !has_percentage )
+ {
+ emmc_ui_progress_state_reset(state);
+ return progress_visible ? EMMC_UI_PROGRESS_ACTION_CLEAR : EMMC_UI_PROGRESS_ACTION_NONE;
+ }
+
+ if ( (percentage < 0) || (percentage > 100) )
+ {
+ return EMMC_UI_PROGRESS_ACTION_INVALID;
+ }
+
+ if ( progress_visible && state->has_last_percentage && state->last_percentage == (uint32_t)percentage )
+ {
+ return EMMC_UI_PROGRESS_ACTION_NONE;
+ }
+
+ state->last_percentage = (uint32_t)percentage;
+ state->has_last_percentage = true;
+
+ if ( percentage == 100 )
+ {
+ emmc_ui_progress_state_reset(state);
+ return EMMC_UI_PROGRESS_ACTION_COMPLETE;
+ }
+
+ return EMMC_UI_PROGRESS_ACTION_UPDATE;
+}
diff --git a/core/embed/emmc_wrapper/emmc_ui_progress.h b/core/embed/emmc_wrapper/emmc_ui_progress.h
new file mode 100644
index 0000000000..0fbe6eb8fb
--- /dev/null
+++ b/core/embed/emmc_wrapper/emmc_ui_progress.h
@@ -0,0 +1,28 @@
+#ifndef __EMMC_UI_PROGRESS_H__
+#define __EMMC_UI_PROGRESS_H__
+
+#include
+#include
+
+typedef enum
+{
+ EMMC_UI_PROGRESS_ACTION_NONE = 0,
+ EMMC_UI_PROGRESS_ACTION_UPDATE,
+ EMMC_UI_PROGRESS_ACTION_COMPLETE,
+ EMMC_UI_PROGRESS_ACTION_CLEAR,
+ EMMC_UI_PROGRESS_ACTION_INVALID,
+} emmc_ui_progress_action;
+
+typedef struct
+{
+ uint32_t last_percentage;
+ bool has_last_percentage;
+} emmc_ui_progress_state;
+
+#define EMMC_UI_PROGRESS_STATE_INIT {0, false}
+
+emmc_ui_progress_action emmc_ui_progress_next_action(
+ emmc_ui_progress_state* state, bool has_percentage, int percentage, bool progress_visible
+);
+
+#endif
diff --git a/core/embed/extmod/modtrezorconfig/modtrezorconfig.c b/core/embed/extmod/modtrezorconfig/modtrezorconfig.c
index ec87553676..33c4f7f158 100644
--- a/core/embed/extmod/modtrezorconfig/modtrezorconfig.c
+++ b/core/embed/extmod/modtrezorconfig/modtrezorconfig.c
@@ -339,37 +339,42 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
mod_trezorconfig_change_wipe_code_obj, 3, 3,
mod_trezorconfig_change_wipe_code);
-/// def get_needs_backup() -> bool:
+/// def get_mnemonic_export_enabled() -> bool:
/// """
-/// Returns needs_backup.
+/// Returns whether mnemonic export is enabled in the SE.
/// """
-STATIC mp_obj_t mod_trezorconfig_get_needs_backup(void) {
- bool needs_backup = false;
- if (sectrue != se_get_needs_backup(&needs_backup)) {
+STATIC mp_obj_t mod_trezorconfig_get_mnemonic_export_enabled(void) {
+ bool enabled = false;
+ if (sectrue != se_get_mnemonic_export_enabled(&enabled)) {
return mp_const_false;
}
-
- return needs_backup ? mp_const_true : mp_const_false;
+ return enabled ? mp_const_true : mp_const_false;
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_get_needs_backup_obj,
- mod_trezorconfig_get_needs_backup);
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(
+ mod_trezorconfig_get_mnemonic_export_enabled_obj,
+ mod_trezorconfig_get_mnemonic_export_enabled);
-/// def set_needs_backup(needs_backup: bool = False) -> bool:
+/// def set_mnemonic_export_enabled(enabled: bool, pin: str) -> bool:
/// """
-/// Set needs_backup.
+/// Enable or disable mnemonic export in the SE.
/// """
-STATIC mp_obj_t mod_trezorconfig_set_needs_backup(mp_obj_t needs_backup) {
- bool needs_backup_b = mp_obj_is_true(needs_backup);
+STATIC mp_obj_t mod_trezorconfig_set_mnemonic_export_enabled(
+ mp_obj_t enabled, mp_obj_t pin) {
+ mp_buffer_info_t pin_b = {0};
+ mp_get_buffer_raise(pin, &pin_b, MP_BUFFER_READ);
+ if (pin_b.len < 4 || pin_b.len > PIN_MAX_LENGTH) {
+ mp_raise_ValueError("Invalid PIN length");
+ }
- if (sectrue != se_set_needs_backup(needs_backup_b)) {
+ if (sectrue != se_set_mnemonic_export_enabled(
+ mp_obj_is_true(enabled), pin_b.buf, pin_b.len)) {
return mp_const_false;
}
-
return mp_const_true;
}
-
-STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorconfig_set_needs_backup_obj,
- mod_trezorconfig_set_needs_backup);
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(
+ mod_trezorconfig_set_mnemonic_export_enabled_obj,
+ mod_trezorconfig_set_mnemonic_export_enabled);
/// def get_val_len(app: int, key: int, public: bool = False) -> int:
/// """
@@ -414,17 +419,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_get_val_len_obj, 2,
/// """
STATIC mp_obj_t mod_trezorconfig_get(size_t n_args, const mp_obj_t *args) {
uint8_t app = trezor_obj_get_uint8(args[0]);
- // webauthn resident credentials, FIDO2
if (app == 4) {
- uint32_t index = trezor_obj_get_uint(args[1]);
- uint16_t len = sizeof(CTAP_credential_id_storage) -
- FIDO2_RESIDENT_CREDENTIALS_HEADER_LEN;
- CTAP_credential_id_storage cred_id = {0};
-
- if (!se_get_fido2_resident_credentials(index, cred_id.rp_id_hash, &len)) {
- return mp_const_none;
- }
- return mp_obj_new_bytes(cred_id.rp_id_hash, len);
+ mp_raise_msg(&mp_type_RuntimeError,
+ "FIDO credentials are managed by the secure element");
}
uint32_t key = trezor_obj_get_uint(args[1]);
@@ -470,20 +467,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_get_obj, 2, 3,
/// """
STATIC mp_obj_t mod_trezorconfig_set(size_t n_args, const mp_obj_t *args) {
uint8_t app = trezor_obj_get_uint8(args[0]);
- // webauthn resident credentials, FIDO2
if (app == 4) {
- uint32_t index = trezor_obj_get_uint(args[1]);
-
- mp_buffer_info_t cred_id;
- mp_get_buffer_raise(args[2], &cred_id, MP_BUFFER_READ);
- if (cred_id.len > sizeof(CTAP_credential_id_storage) -
- FIDO2_RESIDENT_CREDENTIALS_HEADER_LEN) {
- mp_raise_msg(&mp_type_RuntimeError, "Credential ID too long");
- }
- if (!se_set_fido2_resident_credentials(index, cred_id.buf, cred_id.len)) {
- mp_raise_msg(&mp_type_RuntimeError, "Could not save value");
- }
- return mp_const_none;
+ mp_raise_msg(&mp_type_RuntimeError,
+ "FIDO credentials are managed by the secure element");
}
uint32_t key = trezor_obj_get_uint(args[1]);
@@ -520,13 +506,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_set_obj, 3, 4,
/// """
STATIC mp_obj_t mod_trezorconfig_delete(size_t n_args, const mp_obj_t *args) {
uint8_t app = trezor_obj_get_uint8(args[0]);
- // webauthn resident credentials, FIDO2
if (app == 4) {
- uint32_t index = trezor_obj_get_uint(args[1]);
- if (!se_delete_fido2_resident_credentials(index)) {
- mp_raise_msg(&mp_type_RuntimeError, "Could not delete value");
- }
- return mp_const_true;
+ mp_raise_msg(&mp_type_RuntimeError,
+ "FIDO credentials are managed by the secure element");
}
uint32_t key = trezor_obj_get_uint(args[1]);
@@ -641,14 +623,21 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
mod_trezorconfig_se_import_slip39_obj, 4, 4,
mod_trezorconfig_se_import_slip39);
-/// def se_export_mnemonic() -> bytes:
+/// def se_export_mnemonic(pin: str) -> bytes:
/// """
/// Export mnemonic from SE.
/// """
-STATIC mp_obj_t mod_trezorconfig_se_export_mnemonic(void) {
- char mnemonic[MAX_MNEMONIC_LEN + 1];
+STATIC mp_obj_t mod_trezorconfig_se_export_mnemonic(mp_obj_t pin) {
+ mp_buffer_info_t pin_b = {0};
+ char mnemonic[MAX_MNEMONIC_LEN + 1] = {0};
+ mp_get_buffer_raise(pin, &pin_b, MP_BUFFER_READ);
+ if (pin_b.len < 4 || pin_b.len > PIN_MAX_LENGTH) {
+ mp_raise_ValueError("Invalid PIN length");
+ }
- if (sectrue != se_exportMnemonic(mnemonic, sizeof(mnemonic))) {
+ if (sectrue !=
+ se_exportMnemonic(pin_b.buf, pin_b.len, mnemonic, sizeof(mnemonic))) {
+ memzero(mnemonic, sizeof(mnemonic));
mp_raise_ValueError("Get se mnemonic");
}
@@ -658,7 +647,7 @@ STATIC mp_obj_t mod_trezorconfig_se_export_mnemonic(void) {
return res;
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_se_export_mnemonic_obj,
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorconfig_se_export_mnemonic_obj,
mod_trezorconfig_se_export_mnemonic);
/// def fingerprint_is_unlocked() -> bool:
@@ -834,10 +823,10 @@ STATIC const mp_rom_map_elem_t mp_module_trezorconfig_globals_table[] = {
MP_ROM_PTR(&mod_trezorconfig_get_serial_obj)},
{MP_ROM_QSTR(MP_QSTR_get_capacity),
MP_ROM_PTR(&mod_trezorconfig_get_capacity_obj)},
- {MP_ROM_QSTR(MP_QSTR_get_needs_backup),
- MP_ROM_PTR(&mod_trezorconfig_get_needs_backup_obj)},
- {MP_ROM_QSTR(MP_QSTR_set_needs_backup),
- MP_ROM_PTR(&mod_trezorconfig_set_needs_backup_obj)},
+ {MP_ROM_QSTR(MP_QSTR_get_mnemonic_export_enabled),
+ MP_ROM_PTR(&mod_trezorconfig_get_mnemonic_export_enabled_obj)},
+ {MP_ROM_QSTR(MP_QSTR_set_mnemonic_export_enabled),
+ MP_ROM_PTR(&mod_trezorconfig_set_mnemonic_export_enabled_obj)},
{MP_ROM_QSTR(MP_QSTR_fingerprint_is_unlocked),
MP_ROM_PTR(&mod_trezorcrypto_se_fingerprint_is_unlocked_obj)},
{MP_ROM_QSTR(MP_QSTR_fingerprint_lock),
diff --git a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
index 6b87230aa3..8a05793cfd 100644
--- a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+++ b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
@@ -20,6 +20,7 @@
#include "py/objstr.h"
#include "py/runtime.h"
+#include "memzero.h"
#include "se_thd89.h"
/// package: trezorcrypto.se_thd89
@@ -529,35 +530,53 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
mod_trezorcrypto_se_thd89_aes256_decrypt_obj, 2, 3,
mod_trezorcrypto_se_thd89_aes256_decrypt);
-/// def slip21_node() -> bytes:
-/// """
-/// Returns slip21 node.
-/// """
-STATIC mp_obj_t mod_trezorcrypto_se_thd89_slip21_node(void) {
- vstr_t vstr = {0};
- vstr_init_len(&vstr, 64);
- if (se_slip21_node((uint8_t *)vstr.buf) != 0) {
- mp_raise_ValueError("slip21_node failed");
- }
- return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+/// def slip21_ownership_id(script_pubkey: bytes) -> bytes:
+/// """Return the SLIP-0019 ownership identifier for script_pubkey."""
+STATIC mp_obj_t
+mod_trezorcrypto_se_thd89_slip21_ownership_id(mp_obj_t script_pubkey) {
+ mp_buffer_info_t script = {0};
+ uint8_t out[32] = {0};
+ mp_get_buffer_raise(script_pubkey, &script, MP_BUFFER_READ);
+ if (script.len == 0 || script.len > 1024 ||
+ se_slip21_ownership_id(script.buf, script.len, out) != sectrue) {
+ mp_raise_ValueError("slip21 ownership id failed");
+ }
+ return mp_obj_new_bytes(out, sizeof(out));
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorcrypto_se_thd89_slip21_node_obj,
- mod_trezorcrypto_se_thd89_slip21_node);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(
+ mod_trezorcrypto_se_thd89_slip21_ownership_id_obj,
+ mod_trezorcrypto_se_thd89_slip21_ownership_id);
+
+/// def slip21_address_mac(slip44: int, address: bytes) -> bytes:
+/// """Return the SLIP-0024 address MAC."""
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_slip21_address_mac(
+ mp_obj_t slip44_obj, mp_obj_t address_obj) {
+ uint32_t slip44 = trezor_obj_get_uint(slip44_obj);
+ mp_buffer_info_t address = {0};
+ uint8_t out[32] = {0};
+ mp_get_buffer_raise(address_obj, &address, MP_BUFFER_READ);
+ if (address.len == 0 || address.len > 1018 ||
+ se_slip21_address_mac(slip44, address.buf, address.len, out) != sectrue) {
+ mp_raise_ValueError("slip21 address MAC failed");
+ }
+ return mp_obj_new_bytes(out, sizeof(out));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(
+ mod_trezorcrypto_se_thd89_slip21_address_mac_obj,
+ mod_trezorcrypto_se_thd89_slip21_address_mac);
-/// def slip21_fido_node() -> bytes:
-/// """
-/// Returns slip21 fido node, seed without passphrase.
-/// """
-STATIC mp_obj_t mod_trezorcrypto_se_thd89_slip21_fido_node(void) {
- vstr_t vstr = {0};
- vstr_init_len(&vstr, 64);
- if (se_slip21_fido_node((uint8_t *)vstr.buf) != 0) {
- mp_raise_ValueError("slip21 fido node failed");
+/// def slip21_slip25_mac() -> bytes:
+/// """Return the SLIP-0025 keychain authorization MAC."""
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_slip21_slip25_mac(void) {
+ uint8_t out[32] = {0};
+ if (se_slip21_slip25_mac(out) != sectrue) {
+ mp_raise_ValueError("slip21 SLIP-0025 MAC failed");
}
- return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+ return mp_obj_new_bytes(out, sizeof(out));
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorcrypto_se_thd89_slip21_fido_node_obj,
- mod_trezorcrypto_se_thd89_slip21_fido_node);
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(
+ mod_trezorcrypto_se_thd89_slip21_slip25_mac_obj,
+ mod_trezorcrypto_se_thd89_slip21_slip25_mac);
/// def authorization_set(
/// authorization_type: int,
@@ -712,32 +731,6 @@ STATIC mp_obj_t mod_trezorcrypto_se_thd89_derive_xmr(mp_obj_t path) {
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_se_thd89_derive_xmr_obj,
mod_trezorcrypto_se_thd89_derive_xmr);
-/// def derive_xmr_privare(
-/// deriv: bytes
-/// index: int,
-/// ) -> bytes:
-/// """
-/// base + H_s(derivation || varint(output_index))
-/// """
-STATIC mp_obj_t mod_trezorcrypto_se_thd89_derive_xmr_private(mp_obj_t deriv,
- mp_obj_t index) {
- mp_buffer_info_t pub_key = {0};
- mp_get_buffer_raise(deriv, &pub_key, MP_BUFFER_READ);
-
- uint32_t idx = mp_obj_get_int(index);
-
- uint8_t out_pri[32];
- if (!se_derive_xmr_private_key(pub_key.buf, idx, out_pri)) {
- mp_raise_ValueError("Failed to derive private key");
- }
-
- return mp_obj_new_str_copy(&mp_type_bytes, (const uint8_t *)out_pri, 32);
-}
-
-STATIC MP_DEFINE_CONST_FUN_OBJ_2(
- mod_trezorcrypto_se_thd89_derive_xmr_private_obj,
- mod_trezorcrypto_se_thd89_derive_xmr_private);
-
/// def xmr_get_tx_key(
/// rand: bytes
/// hash: bytes,
@@ -764,6 +757,122 @@ STATIC mp_obj_t mod_trezorcrypto_se_thd89_xmr_get_tx_key(mp_obj_t rand,
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_se_thd89_xmr_get_tx_key_obj,
mod_trezorcrypto_se_thd89_xmr_get_tx_key);
+/// def xmr_generate_key_image(
+/// recv_deriv: bytes,
+/// real_idx: int,
+/// subaddr_sk: bytes,
+/// out_key: bytes,
+/// ) -> bytes:
+/// """Generates a key image without exporting the one-time spend key."""
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_xmr_generate_key_image(
+ size_t n_args, const mp_obj_t *args) {
+ mp_buffer_info_t recv_deriv = {0};
+ mp_buffer_info_t subaddr_sk = {0};
+ mp_buffer_info_t out_key = {0};
+ uint8_t key_image[32] = {0};
+
+ mp_get_buffer_raise(args[0], &recv_deriv, MP_BUFFER_READ);
+ mp_get_buffer_raise(args[2], &subaddr_sk, MP_BUFFER_READ);
+ mp_get_buffer_raise(args[3], &out_key, MP_BUFFER_READ);
+ if (recv_deriv.len != 32 || subaddr_sk.len != 32 || out_key.len != 32) {
+ mp_raise_ValueError("Invalid XMR key data length");
+ }
+
+ uint32_t real_idx = trezor_obj_get_uint(args[1]);
+ if (se_xmr_generate_key_image(recv_deriv.buf, real_idx, subaddr_sk.buf,
+ out_key.buf, key_image) != sectrue) {
+ mp_raise_ValueError("Failed to generate XMR key image");
+ }
+
+ mp_obj_t result = mp_obj_new_bytes(key_image, sizeof(key_image));
+ memzero(key_image, sizeof(key_image));
+ return result;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
+ mod_trezorcrypto_se_thd89_xmr_generate_key_image_obj, 4, 4,
+ mod_trezorcrypto_se_thd89_xmr_generate_key_image);
+
+/// def xmr_secret_nonce_begin(
+/// recv_deriv: bytes,
+/// real_idx: int,
+/// subaddr_sk: bytes,
+/// out_key: bytes,
+/// ) -> tuple[bytes, bytes, bytes, int]:
+/// """Starts a one-time XMR secret-response session."""
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_xmr_secret_nonce_begin(
+ size_t n_args, const mp_obj_t *args) {
+ mp_buffer_info_t recv_deriv = {0};
+ mp_buffer_info_t subaddr_sk = {0};
+ mp_buffer_info_t out_key = {0};
+ uint8_t response[97] = {0};
+
+ mp_get_buffer_raise(args[0], &recv_deriv, MP_BUFFER_READ);
+ mp_get_buffer_raise(args[2], &subaddr_sk, MP_BUFFER_READ);
+ mp_get_buffer_raise(args[3], &out_key, MP_BUFFER_READ);
+ if (recv_deriv.len != 32 || subaddr_sk.len != 32 || out_key.len != 32) {
+ mp_raise_ValueError("Invalid XMR key data length");
+ }
+
+ uint32_t real_idx = trezor_obj_get_uint(args[1]);
+ if (se_xmr_secret_nonce_begin(recv_deriv.buf, real_idx, subaddr_sk.buf,
+ out_key.buf, response) != sectrue) {
+ mp_raise_ValueError("Failed to start XMR secret response");
+ }
+
+ mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL));
+ result->items[0] = mp_obj_new_bytes(response, 32);
+ result->items[1] = mp_obj_new_bytes(response + 32, 32);
+ result->items[2] = mp_obj_new_bytes(response + 64, 32);
+ result->items[3] = MP_OBJ_NEW_SMALL_INT(response[96]);
+ memzero(response, sizeof(response));
+ return MP_OBJ_FROM_PTR(result);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
+ mod_trezorcrypto_se_thd89_xmr_secret_nonce_begin_obj, 4, 4,
+ mod_trezorcrypto_se_thd89_xmr_secret_nonce_begin);
+
+/// def xmr_secret_response_finish(
+/// session_id: int,
+/// c: bytes,
+/// mu_p: bytes,
+/// mu_c: bytes,
+/// z: bytes,
+/// ) -> bytes:
+/// """Finishes a one-time XMR secret-response session."""
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_xmr_secret_response_finish(
+ size_t n_args, const mp_obj_t *args) {
+ mp_buffer_info_t c = {0};
+ mp_buffer_info_t mu_p = {0};
+ mp_buffer_info_t mu_c = {0};
+ mp_buffer_info_t z = {0};
+ uint8_t response[32] = {0};
+
+ uint32_t session_id = trezor_obj_get_uint(args[0]);
+ if (session_id == 0 || session_id > 0xFF) {
+ mp_raise_ValueError("Invalid XMR session ID");
+ }
+
+ mp_get_buffer_raise(args[1], &c, MP_BUFFER_READ);
+ mp_get_buffer_raise(args[2], &mu_p, MP_BUFFER_READ);
+ mp_get_buffer_raise(args[3], &mu_c, MP_BUFFER_READ);
+ mp_get_buffer_raise(args[4], &z, MP_BUFFER_READ);
+ if (c.len != 32 || mu_p.len != 32 || mu_c.len != 32 || z.len != 32) {
+ mp_raise_ValueError("Invalid XMR scalar length");
+ }
+
+ if (se_xmr_secret_response_finish((uint8_t)session_id, c.buf, mu_p.buf,
+ mu_c.buf, z.buf, response) != sectrue) {
+ mp_raise_ValueError("Failed to finish XMR secret response");
+ }
+
+ mp_obj_t result = mp_obj_new_bytes(response, sizeof(response));
+ memzero(response, sizeof(response));
+ return result;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
+ mod_trezorcrypto_se_thd89_xmr_secret_response_finish_obj, 5, 5,
+ mod_trezorcrypto_se_thd89_xmr_secret_response_finish);
+
/// def fido_seed(
/// callback: Callable[[int, int], None] | None = None,
/// ) -> bool:
@@ -974,18 +1083,211 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(
mod_trezorcrypto_se_thd89_fido_att_sign_digest_obj,
mod_trezorcrypto_se_thd89_fido_att_sign_digest);
-/// def fido_delete_all_credentials() -> None:
-/// """
-/// Delete all FIDO2 credentials.
-/// """
-STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_delete_all_credentials(void) {
- se_delete_all_fido2_credentials();
+/// def fido_credential_create(
+/// plaintext: bytes, resident: bool
+/// ) -> tuple[bytes, int, int]:
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_credential_create(
+ mp_obj_t plaintext_obj, mp_obj_t resident_obj) {
+ mp_buffer_info_t plaintext = {0};
+ uint16_t response_len = 516;
+ uint16_t credential_id_len = 0;
+ vstr_t response = {0};
+ mp_obj_t result[3] = {0};
+
+ mp_get_buffer_raise(plaintext_obj, &plaintext, MP_BUFFER_READ);
+ if (plaintext.len == 0 || plaintext.len > 480 ||
+ (resident_obj != mp_const_false && resident_obj != mp_const_true)) {
+ mp_raise_ValueError("invalid FIDO credential data");
+ }
+ vstr_init_len(&response, response_len);
+ if (se_fido_credential_create(plaintext.buf, plaintext.len,
+ resident_obj == mp_const_true ? 1 : 0,
+ (uint8_t *)response.buf,
+ &response_len) != sectrue) {
+ memzero(response.buf, response.len);
+ vstr_clear(&response);
+ mp_raise_ValueError("FIDO credential creation failed");
+ }
+ credential_id_len = ((uint16_t)(uint8_t)response.buf[0] << 8) |
+ (uint8_t)response.buf[1];
+ result[0] = mp_obj_new_bytes((const uint8_t *)response.buf + 2,
+ credential_id_len);
+ result[1] = mp_obj_new_int((uint8_t)response.buf[credential_id_len + 2]);
+ result[2] = mp_obj_new_int((uint8_t)response.buf[credential_id_len + 3]);
+ memzero(response.buf, response.len);
+ vstr_clear(&response);
+ return mp_obj_new_tuple(3, result);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(
+ mod_trezorcrypto_se_thd89_fido_credential_create_obj,
+ mod_trezorcrypto_se_thd89_fido_credential_create);
+
+/// def fido_credential_validate(
+/// credential_id: bytes, rp_id_hash: bytes | None
+/// ) -> bytes:
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_credential_validate(
+ mp_obj_t credential_id_obj, mp_obj_t rp_id_hash_obj) {
+ mp_buffer_info_t credential_id = {0};
+ mp_buffer_info_t rp_id_hash = {0};
+ const uint8_t *rp_id_hash_ptr = NULL;
+ uint16_t plaintext_len = 0;
+ vstr_t plaintext = {0};
+
+ mp_get_buffer_raise(credential_id_obj, &credential_id, MP_BUFFER_READ);
+ if (credential_id.len < 33 || credential_id.len > 512) {
+ mp_raise_ValueError("invalid FIDO credential ID");
+ }
+ if (rp_id_hash_obj != mp_const_none) {
+ mp_get_buffer_raise(rp_id_hash_obj, &rp_id_hash, MP_BUFFER_READ);
+ if (rp_id_hash.len != 32) {
+ mp_raise_ValueError("invalid FIDO RP ID hash");
+ }
+ rp_id_hash_ptr = rp_id_hash.buf;
+ }
+ plaintext_len = credential_id.len - 32;
+ vstr_init_len(&plaintext, plaintext_len);
+ if (se_fido_credential_validate(rp_id_hash_ptr, credential_id.buf,
+ credential_id.len,
+ (uint8_t *)plaintext.buf,
+ &plaintext_len) != sectrue) {
+ memzero(plaintext.buf, plaintext.len);
+ vstr_clear(&plaintext);
+ mp_raise_ValueError("FIDO credential validation failed");
+ }
+ plaintext.len = plaintext_len;
+ return mp_obj_new_str_from_vstr(&mp_type_bytes, &plaintext);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(
+ mod_trezorcrypto_se_thd89_fido_credential_validate_obj,
+ mod_trezorcrypto_se_thd89_fido_credential_validate);
+
+/// def fido_resident_credentials_list() -> tuple[int, ...]:
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_resident_credentials_list(void) {
+ uint8_t indexes[FIDO2_RESIDENT_CREDENTIALS_COUNT] = {0};
+ uint16_t count = FIDO2_RESIDENT_CREDENTIALS_COUNT;
+ mp_obj_tuple_t *result = NULL;
+
+ if (se_fido_resident_credentials_list(indexes, &count) != sectrue) {
+ mp_raise_ValueError("FIDO resident credential list failed");
+ }
+ result = MP_OBJ_TO_PTR(mp_obj_new_tuple(count, NULL));
+ for (uint16_t i = 0; i < count; i++) {
+ result->items[i] = mp_obj_new_int(indexes[i]);
+ }
+ return MP_OBJ_FROM_PTR(result);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(
+ mod_trezorcrypto_se_thd89_fido_resident_credentials_list_obj,
+ mod_trezorcrypto_se_thd89_fido_resident_credentials_list);
+
+/// def fido_resident_credential_read(index: int) -> tuple[bytes, bytes]:
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_resident_credential_read(
+ mp_obj_t index_obj) {
+ uint8_t index = trezor_obj_get_uint8(index_obj);
+ uint16_t packed_len = 920;
+ uint16_t credential_id_len = 0;
+ uint16_t plaintext_len = 0;
+ vstr_t packed = {0};
+ mp_obj_t result[2] = {0};
+
+ if (index >= FIDO2_RESIDENT_CREDENTIALS_COUNT) {
+ mp_raise_ValueError("invalid FIDO resident credential index");
+ }
+ vstr_init_len(&packed, packed_len);
+ if (se_fido_resident_credential_read(index, (uint8_t *)packed.buf,
+ &packed_len) != sectrue) {
+ memzero(packed.buf, packed.len);
+ vstr_clear(&packed);
+ mp_raise_ValueError("FIDO resident credential read failed");
+ }
+ credential_id_len = ((uint16_t)(uint8_t)packed.buf[0] << 8) |
+ (uint8_t)packed.buf[1];
+ plaintext_len = ((uint16_t)(uint8_t)packed.buf[credential_id_len + 2] << 8) |
+ (uint8_t)packed.buf[credential_id_len + 3];
+ result[0] = mp_obj_new_bytes((const uint8_t *)packed.buf + 2,
+ credential_id_len);
+ result[1] = mp_obj_new_bytes(
+ (const uint8_t *)packed.buf + credential_id_len + 4, plaintext_len);
+ memzero(packed.buf, packed.len);
+ vstr_clear(&packed);
+ return mp_obj_new_tuple(2, result);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(
+ mod_trezorcrypto_se_thd89_fido_resident_credential_read_obj,
+ mod_trezorcrypto_se_thd89_fido_resident_credential_read);
+
+/// def fido_resident_credential_import(credential_id: bytes) -> tuple[int, int]:
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_resident_credential_import(
+ mp_obj_t credential_id_obj) {
+ mp_buffer_info_t credential_id = {0};
+ uint8_t slot = 0;
+ uint8_t action = 0;
+ mp_obj_t result[2] = {0};
+
+ mp_get_buffer_raise(credential_id_obj, &credential_id, MP_BUFFER_READ);
+ if (credential_id.len < 33 || credential_id.len > 474) {
+ mp_raise_ValueError("invalid FIDO credential ID");
+ }
+ if (se_fido_resident_credential_import(credential_id.buf, credential_id.len,
+ &slot, &action) != sectrue) {
+ mp_raise_ValueError("FIDO resident credential import failed");
+ }
+ result[0] = mp_obj_new_int(slot);
+ result[1] = mp_obj_new_int(action);
+ return mp_obj_new_tuple(2, result);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(
+ mod_trezorcrypto_se_thd89_fido_resident_credential_import_obj,
+ mod_trezorcrypto_se_thd89_fido_resident_credential_import);
+
+/// def fido_resident_credential_delete(index: int) -> None:
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_resident_credential_delete(
+ mp_obj_t index_obj) {
+ uint8_t index = trezor_obj_get_uint8(index_obj);
+
+ if (index >= FIDO2_RESIDENT_CREDENTIALS_COUNT ||
+ se_fido_resident_credential_delete(index) != sectrue) {
+ mp_raise_ValueError("FIDO resident credential delete failed");
+ }
return mp_const_none;
}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(
+ mod_trezorcrypto_se_thd89_fido_resident_credential_delete_obj,
+ mod_trezorcrypto_se_thd89_fido_resident_credential_delete);
+/// def fido_resident_credentials_clear() -> None:
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_resident_credentials_clear(void) {
+ if (se_fido_resident_credentials_clear() != sectrue) {
+ mp_raise_ValueError("FIDO resident credential clear failed");
+ }
+ return mp_const_none;
+}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(
- mod_trezorcrypto_se_thd89_fido_delete_all_credentials_obj,
- mod_trezorcrypto_se_thd89_fido_delete_all_credentials);
+ mod_trezorcrypto_se_thd89_fido_resident_credentials_clear_obj,
+ mod_trezorcrypto_se_thd89_fido_resident_credentials_clear);
+
+/// def fido_hmac_secret(credential_id: bytes, salt: bytes) -> bytes:
+/// """Return the purpose-bound hmac-secret output for one or two salts."""
+STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_hmac_secret(
+ mp_obj_t credential_id_obj, mp_obj_t salt_obj) {
+ mp_buffer_info_t credential_id = {0};
+ mp_buffer_info_t salt = {0};
+ vstr_t out = {0};
+ mp_get_buffer_raise(credential_id_obj, &credential_id, MP_BUFFER_READ);
+ mp_get_buffer_raise(salt_obj, &salt, MP_BUFFER_READ);
+ if (credential_id.len < 33 || credential_id.len > 512 ||
+ (salt.len != 32 && salt.len != 64)) {
+ mp_raise_ValueError("invalid FIDO hmac-secret data");
+ }
+ vstr_init_len(&out, salt.len);
+ if (se_fido_hmac_secret(credential_id.buf, credential_id.len, salt.buf,
+ salt.len, (uint8_t *)out.buf) != sectrue) {
+ mp_raise_ValueError("FIDO hmac-secret failed");
+ }
+ return mp_obj_new_str_from_vstr(&mp_type_bytes, &out);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_se_thd89_fido_hmac_secret_obj,
+ mod_trezorcrypto_se_thd89_fido_hmac_secret);
/// def get_pin_passphrase_space() -> int:
/// """
@@ -1186,10 +1488,12 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_se_thd89_globals_table[] = {
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_aes256_encrypt_obj)},
{MP_ROM_QSTR(MP_QSTR_aes256_decrypt),
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_aes256_decrypt_obj)},
- {MP_ROM_QSTR(MP_QSTR_slip21_node),
- MP_ROM_PTR(&mod_trezorcrypto_se_thd89_slip21_node_obj)},
- {MP_ROM_QSTR(MP_QSTR_slip21_fido_node),
- MP_ROM_PTR(&mod_trezorcrypto_se_thd89_slip21_fido_node_obj)},
+ {MP_ROM_QSTR(MP_QSTR_slip21_ownership_id),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_slip21_ownership_id_obj)},
+ {MP_ROM_QSTR(MP_QSTR_slip21_address_mac),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_slip21_address_mac_obj)},
+ {MP_ROM_QSTR(MP_QSTR_slip21_slip25_mac),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_slip21_slip25_mac_obj)},
{MP_ROM_QSTR(MP_QSTR_authorization_set),
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_authorization_set_obj)},
{MP_ROM_QSTR(MP_QSTR_authorization_get_type),
@@ -1204,10 +1508,14 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_se_thd89_globals_table[] = {
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_sign_message_obj)},
{MP_ROM_QSTR(MP_QSTR_derive_xmr),
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_derive_xmr_obj)},
- {MP_ROM_QSTR(MP_QSTR_derive_xmr_private),
- MP_ROM_PTR(&mod_trezorcrypto_se_thd89_derive_xmr_private_obj)},
{MP_ROM_QSTR(MP_QSTR_xmr_get_tx_key),
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_xmr_get_tx_key_obj)},
+ {MP_ROM_QSTR(MP_QSTR_xmr_generate_key_image),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_xmr_generate_key_image_obj)},
+ {MP_ROM_QSTR(MP_QSTR_xmr_secret_nonce_begin),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_xmr_secret_nonce_begin_obj)},
+ {MP_ROM_QSTR(MP_QSTR_xmr_secret_response_finish),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_xmr_secret_response_finish_obj)},
{MP_ROM_QSTR(MP_QSTR_fido_seed),
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_seed_obj)},
{MP_ROM_QSTR(MP_QSTR_fido_u2f_register),
@@ -1218,12 +1526,26 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_se_thd89_globals_table[] = {
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_u2f_authenticate_obj)},
{MP_ROM_QSTR(MP_QSTR_fido_u2f_validate),
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_u2f_validate_obj)},
+ {MP_ROM_QSTR(MP_QSTR_fido_credential_create),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_credential_create_obj)},
+ {MP_ROM_QSTR(MP_QSTR_fido_credential_validate),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_credential_validate_obj)},
+ {MP_ROM_QSTR(MP_QSTR_fido_resident_credentials_list),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_resident_credentials_list_obj)},
+ {MP_ROM_QSTR(MP_QSTR_fido_resident_credential_read),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_resident_credential_read_obj)},
+ {MP_ROM_QSTR(MP_QSTR_fido_resident_credential_import),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_resident_credential_import_obj)},
+ {MP_ROM_QSTR(MP_QSTR_fido_resident_credential_delete),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_resident_credential_delete_obj)},
+ {MP_ROM_QSTR(MP_QSTR_fido_resident_credentials_clear),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_resident_credentials_clear_obj)},
+ {MP_ROM_QSTR(MP_QSTR_fido_hmac_secret),
+ MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_hmac_secret_obj)},
{MP_ROM_QSTR(MP_QSTR_fido_sign_digest),
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_sign_digest_obj)},
{MP_ROM_QSTR(MP_QSTR_fido_att_sign_digest),
MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_att_sign_digest_obj)},
- {MP_ROM_QSTR(MP_QSTR_fido_delete_all_credentials),
- MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_delete_all_credentials_obj)},
{MP_ROM_QSTR(MP_QSTR_FIDO2_CRED_COUNT_MAX),
MP_ROM_INT(FIDO2_RESIDENT_CREDENTIALS_COUNT)},
{MP_ROM_QSTR(MP_QSTR_get_pin_passphrase_space),
diff --git a/core/embed/extmod/modtrezorutils/modtrezorutils.c b/core/embed/extmod/modtrezorutils/modtrezorutils.c
index 212e6f8ee6..f7a6dfff53 100644
--- a/core/embed/extmod/modtrezorutils/modtrezorutils.c
+++ b/core/embed/extmod/modtrezorutils/modtrezorutils.c
@@ -197,7 +197,7 @@ STATIC mp_obj_t mod_trezorutils_firmware_vendor(void) {
vendor_header vhdr = {0};
uint32_t size = flash_sector_size(FLASH_SECTOR_FIRMWARE_START);
const void *data = flash_get_address(FLASH_SECTOR_FIRMWARE_START, 0, size);
- if (data == NULL || sectrue != read_vendor_header(data, &vhdr)) {
+ if (data == NULL || sectrue != read_vendor_header(data, size, &vhdr)) {
mp_raise_msg(&mp_type_RuntimeError, "Failed to read vendor header.");
}
return mp_obj_new_str_copy(&mp_type_str, (const uint8_t *)vhdr.vstr,
@@ -461,7 +461,7 @@ STATIC mp_obj_t mod_trezorutils_se_boot_build_id(mp_obj_t se_addr) {
return mp_obj_new_str_copy(&mp_type_str, (const uint8_t *)"EMULATOR", 8);
#else
int addr = mp_obj_get_int(se_addr);
- char str[8] = {0};
+ char str[16] = {0};
se_get_boot_build_id(addr, str, sizeof(str));
return mp_obj_new_str_copy(&mp_type_str, (const uint8_t *)str, strlen(str));
diff --git a/core/embed/firmware/main.c b/core/embed/firmware/main.c
index 37df6eae7c..8a17bf339b 100644
--- a/core/embed/firmware/main.c
+++ b/core/embed/firmware/main.c
@@ -79,6 +79,23 @@
// from util.s
extern void shutdown_privileged(void);
+static void __attribute__((noreturn)) show_se_version_required(void) {
+ display_orientation(0);
+ display_clear();
+ display_backlight(255);
+ display_image(9, 50, 46, 40, toi_icon_warning + 12,
+ sizeof(toi_icon_warning) - 12);
+ display_text(8, 140, "SE firmware update required.", -1, FONT_NORMAL,
+ COLOR_WHITE, COLOR_BLACK);
+ display_text(8, 720, "Version 1.3.2 or later is required.", -1,
+ FONT_NORMAL, RGB16(0x69, 0x69, 0x69), COLOR_BLACK);
+ display_text(8, 784, "Tap to enter Update Mode.", -1, FONT_NORMAL,
+ COLOR_WHITE, COLOR_BLACK);
+ while (!touch_click()) {
+ }
+ reboot_to_bootloader();
+}
+
static void copyflash2sdram(void) {
extern int _flash2_load_addr, _flash2_start, _flash2_end;
volatile uint32_t *dst = (volatile uint32_t *)&_flash2_start;
@@ -150,6 +167,11 @@ int main(void) {
motor_init();
thd89_init();
+ if (se_all_versions_at_least(SE_MINIMUM_VERSION_MAJOR,
+ SE_MINIMUM_VERSION_MINOR,
+ SE_MINIMUM_VERSION_PATCH) != sectrue) {
+ show_se_version_required();
+ }
camera_init();
fingerprint_init();
nfc_init();
diff --git a/core/embed/firmware/version.h b/core/embed/firmware/version.h
index 3c52c9bcb6..1234e120bc 100644
--- a/core/embed/firmware/version.h
+++ b/core/embed/firmware/version.h
@@ -15,8 +15,8 @@
// Minimum SE version required for firmware upgrade
#define SE_MINIMUM_VERSION_MAJOR 1
-#define SE_MINIMUM_VERSION_MINOR 1
-#define SE_MINIMUM_VERSION_PATCH 7
+#define SE_MINIMUM_VERSION_MINOR 3
+#define SE_MINIMUM_VERSION_PATCH 2
#define SE_MINIMUM_VERSION_UINT32 \
(SE_MINIMUM_VERSION_MAJOR | (SE_MINIMUM_VERSION_MINOR << 8) | \
(SE_MINIMUM_VERSION_PATCH << 16) | (0 << 24))
diff --git a/core/embed/trezorhal/image.c b/core/embed/trezorhal/image.c
index c7af7d1452..af68710331 100644
--- a/core/embed/trezorhal/image.c
+++ b/core/embed/trezorhal/image.c
@@ -170,13 +170,19 @@ secbool load_thd89_image_header(const uint8_t* const data, const uint32_t magic,
return sectrue;
}
-secbool read_vendor_header(const uint8_t* const data,
+secbool read_vendor_header(const uint8_t* const data, size_t data_size,
vendor_header* const vhdr) {
+ // The fixed fields and the first possible vendor-string length byte must fit.
+ if (data == NULL || vhdr == NULL || data_size < 33) return secfalse;
+
memcpy(&vhdr->magic, data, 4);
if (vhdr->magic != 0x56544B4F) return secfalse; // OKTV
memcpy(&vhdr->hdrlen, data + 4, 4);
- if (vhdr->hdrlen > 64 * 1024) return secfalse;
+ if (vhdr->hdrlen < IMAGE_SIG_SIZE ||
+ vhdr->hdrlen > VENDOR_HEADER_MAX_SIZE || vhdr->hdrlen > data_size) {
+ return secfalse;
+ }
memcpy(&vhdr->expiry, data + 8, 4);
if (vhdr->expiry != 0) return secfalse;
@@ -191,6 +197,12 @@ secbool read_vendor_header(const uint8_t* const data,
return secfalse;
}
+ const size_t signature_offset = vhdr->hdrlen - IMAGE_SIG_SIZE;
+ const size_t vstr_len_offset = 32 + (size_t)vhdr->vsig_n * 32;
+ if (vstr_len_offset >= signature_offset) {
+ return secfalse;
+ }
+
for (int i = 0; i < vhdr->vsig_n; i++) {
vhdr->vpub[i] = data + 32 + i * 32;
}
@@ -198,26 +210,34 @@ secbool read_vendor_header(const uint8_t* const data,
vhdr->vpub[i] = 0;
}
- memcpy(&vhdr->vstr_len, data + 32 + vhdr->vsig_n * 32, 1);
+ memcpy(&vhdr->vstr_len, data + vstr_len_offset, 1);
- vhdr->vstr = (const char*)(data + 32 + vhdr->vsig_n * 32 + 1);
+ const size_t vstr_offset = vstr_len_offset + 1;
+ if ((size_t)vhdr->vstr_len > signature_offset - vstr_offset) {
+ return secfalse;
+ }
+
+ vhdr->vstr = (const char*)(data + vstr_offset);
- vhdr->vimg = data + 32 + vhdr->vsig_n * 32 + 1 + vhdr->vstr_len;
+ vhdr->vimg = data + vstr_offset + vhdr->vstr_len;
// align to 4 bytes
vhdr->vimg += (-(uintptr_t)vhdr->vimg) & 3;
+ if (vhdr->vimg > data + signature_offset) {
+ return secfalse;
+ }
- memcpy(&vhdr->sigmask, data + vhdr->hdrlen - IMAGE_SIG_SIZE, 1);
+ memcpy(&vhdr->sigmask, data + signature_offset, 1);
- memcpy(vhdr->sig, data + vhdr->hdrlen - IMAGE_SIG_SIZE + 1,
- IMAGE_SIG_SIZE - 1);
+ memcpy(vhdr->sig, data + signature_offset + 1, IMAGE_SIG_SIZE - 1);
return sectrue;
}
-secbool load_vendor_header(const uint8_t* const data, uint8_t key_m,
- uint8_t key_n, const uint8_t* const* keys,
+secbool load_vendor_header(const uint8_t* const data, size_t data_size,
+ uint8_t key_m, uint8_t key_n,
+ const uint8_t* const* keys,
vendor_header* const vhdr) {
- if (sectrue != read_vendor_header(data, vhdr)) {
+ if (sectrue != read_vendor_header(data, data_size, vhdr)) {
return secfalse;
}
@@ -712,8 +732,9 @@ secbool verify_firmware(vendor_header* const vhdr, image_header* const hdr,
const size_t fw_external_size = FMC_SDRAM_FIRMWARE_P2_LEN;
// verify vhdr
- ExecuteCheck_ADV(load_vendor_header((const uint8_t*)FIRMWARE_START, FW_KEY_M,
- FW_KEY_N, FW_KEYS, &_vhdr),
+ ExecuteCheck_ADV(load_vendor_header((const uint8_t*)FIRMWARE_START,
+ VENDOR_HEADER_MAX_SIZE, FW_KEY_M, FW_KEY_N,
+ FW_KEYS, &_vhdr),
sectrue, {
if (error_msg != NULL)
strncpy(error_msg, "Firmware vendor header invalid!",
diff --git a/core/embed/trezorhal/image.h b/core/embed/trezorhal/image.h
index 5a96e10226..5eb5b05309 100644
--- a/core/embed/trezorhal/image.h
+++ b/core/embed/trezorhal/image.h
@@ -20,6 +20,7 @@
#ifndef __TREZORHAL_IMAGE_H__
#define __TREZORHAL_IMAGE_H__
+#include
#include
#include "secbool.h"
@@ -33,6 +34,7 @@
#define IMAGE_HEADER_SIZE 0x400 // size of the bootloader or firmware header
#define IMAGE_SIG_SIZE 65
+#define VENDOR_HEADER_MAX_SIZE (64 * 1024)
#define IMAGE_CHUNK_SIZE (128 * 1024)
#define IMAGE_INIT_CHUNK_SIZE (16 * 1024)
@@ -179,11 +181,12 @@ secbool __wur load_thd89_image_header(const uint8_t* const data,
const uint32_t maxsize,
image_header_th89* const hdr);
-secbool __wur load_vendor_header(const uint8_t* const data, uint8_t key_m,
- uint8_t key_n, const uint8_t* const* keys,
+secbool __wur load_vendor_header(const uint8_t* const data, size_t data_size,
+ uint8_t key_m, uint8_t key_n,
+ const uint8_t* const* keys,
vendor_header* const vhdr);
-secbool __wur read_vendor_header(const uint8_t* const data,
+secbool __wur read_vendor_header(const uint8_t* const data, size_t data_size,
vendor_header* const vhdr);
void vendor_header_hash(const vendor_header* const vhdr, uint8_t* hash);
diff --git a/core/embed/trezorhal/se_thd89.c b/core/embed/trezorhal/se_thd89.c
index 3d0b0b878c..630b8579d7 100644
--- a/core/embed/trezorhal/se_thd89.c
+++ b/core/embed/trezorhal/se_thd89.c
@@ -13,10 +13,12 @@
#include "rand.h"
#include "se_thd89.h"
+#include "se_thd89_v2.h"
#include "secp256k1.h"
#include "thd89.h"
#define PIN_MAX_LEN (50)
+#define MNEMONIC_EXPORT_PIN_MIN_LEN (4)
#define CURVE_NIST256P1 (0x00)
#define CURVE_SECP256K1 (0x01)
@@ -37,8 +39,12 @@
#define SE_INS_HASHR 0xED
#define SE_INS_HASHRAM 0xEE
#define SE_INS_FINGERPRINT 0xEF
+#define SE_INS_GET_STATE 0xCA
+#define SE_INS_COMPONENT_VERSION 0xF3
#define SE_INS_FIDO 0xF9
+#define SE_COMPONENT_VERSION_SLOT_COUNT 10
+
typedef enum {
SE_FIDO_GEN_SEED = 0x00,
SE_FIDO_U2F_REGISTER,
@@ -51,18 +57,63 @@ typedef enum {
SE_FIDO_DERIVE_NODE,
SE_FIDO_NODE_SIGN,
SE_FIDO_ATT_SIGN,
+ SE_FIDO_SLIP21_HMAC_SECRET = 0x0E,
+ SE_FIDO_LIST_RESIDENT_CREDENTIALS,
+ SE_FIDO_READ_RESIDENT_CREDENTIAL,
+ SE_FIDO_CREATE_CREDENTIAL,
+ SE_FIDO_VALIDATE_CREDENTIAL,
+ SE_FIDO_DELETE_RESIDENT_CREDENTIAL,
+ SE_FIDO_CLEAR_RESIDENT_CREDENTIALS,
+ SE_FIDO_IMPORT_RESIDENT_CREDENTIAL,
} SE_FIDO_P2;
-#define SE_PIN_RETRY_MAX 10
+#define SE_FIDO_CREDENTIAL_ID_MIN_LEN 33U
+#define SE_FIDO_CREDENTIAL_ID_MAX_LEN 512U
+#define SE_FIDO_CREDENTIAL_PLAINTEXT_MAX_LEN 480U
+#define SE_FIDO_RESIDENT_CREDENTIAL_ID_MAX_LEN 474U
+#define SE_FIDO_RESIDENT_CREDENTIAL_PLAINTEXT_MAX_LEN 442U
+#define SE_FIDO_RESIDENT_CREDENTIAL_READ_MAX_LEN 920U
+
+#define SE_PIN_RETRY_MAX 5
+#define SE_SW_PIN_RETRY_LIMIT_REACHED 0x6983
#define SE_DATA_MAX_LEN (1024)
#define SE_BUF_MAX_LEN (1024 + 64)
static uint8_t se_session_key[SESSION_KEYLEN];
+static uint8_t se_session_mac_key[SESSION_KEYLEN];
static uint8_t se_fp_session_key[SESSION_KEYLEN];
+static uint8_t se_fp_session_mac_key[SESSION_KEYLEN];
static bool se_session_init = false;
static bool se_fp_session_init = false;
+typedef enum {
+ SE_LONG_OPERATION_NONE = 0,
+ SE_LONG_OPERATION_SET_PASSPHRASE_PIN,
+ SE_LONG_OPERATION_SESSION_SEED,
+ SE_LONG_OPERATION_CARDANO_SEED,
+ SE_LONG_OPERATION_FIDO_SEED,
+} se_long_operation_t;
+
+typedef enum {
+ SE_SECURE_RESPONSE_OK = 0,
+ SE_SECURE_RESPONSE_AUTHENTICATED_ERROR,
+ SE_SECURE_RESPONSE_NO_MAC_6C,
+ SE_SECURE_RESPONSE_INVALID,
+} se_secure_response_result_t;
+
+typedef enum {
+ SE_FATAL_STATUS_NONE = 0,
+ SE_FATAL_STATUS_SECURITY_ALERT,
+ SE_FATAL_STATUS_CONFIGURATION_ERROR,
+} se_fatal_status_t;
+
+static se_long_operation_t se_pending_operation = SE_LONG_OPERATION_NONE;
+static se_long_operation_t se_fp_pending_operation = SE_LONG_OPERATION_NONE;
+static se_fatal_status_t se_pending_fatal_status = SE_FATAL_STATUS_NONE;
+
+static secbool se_query_progress_percent_ex(uint8_t addr, uint8_t *percent);
+
static pin_result_t pin_result_type = PIN_FAILED;
static pin_result_t pin_passphrase_ret = PIN_FAILED;
@@ -81,15 +132,81 @@ static uint16_t se_recv_len;
static UI_WAIT_CALLBACK ui_callback = NULL;
-static void xor_cal(uint8_t *data1, uint8_t *data2, uint16_t len,
- uint8_t * xor) {
- uint16_t i;
+static se_long_operation_t *se_get_pending_operation(uint8_t addr) {
+ return addr == THD89_FINGER_ADDRESS ? &se_fp_pending_operation
+ : &se_pending_operation;
+}
- for (i = 0; i < len; i++) {
- xor[i] = data1[i] ^ data2[i];
+static void se_invalidate_session(uint8_t addr) {
+ if (addr == THD89_FINGER_ADDRESS) {
+ memzero(se_fp_session_key, sizeof(se_fp_session_key));
+ memzero(se_fp_session_mac_key, sizeof(se_fp_session_mac_key));
+ se_fp_session_init = false;
+ se_fp_pending_operation = SE_LONG_OPERATION_NONE;
+ } else {
+ memzero(se_session_key, sizeof(se_session_key));
+ memzero(se_session_mac_key, sizeof(se_session_mac_key));
+ se_session_init = false;
+ se_pending_operation = SE_LONG_OPERATION_NONE;
}
}
+static void se_record_fatal_status(uint16_t sw1sw2) {
+ se_fatal_status_t status = SE_FATAL_STATUS_NONE;
+ if (sw1sw2 == 0x6601) {
+ status = SE_FATAL_STATUS_SECURITY_ALERT;
+ } else if (sw1sw2 == 0x6f01) {
+ status = SE_FATAL_STATUS_CONFIGURATION_ERROR;
+ }
+ if (status == SE_FATAL_STATUS_NONE) {
+ return;
+ }
+ se_invalidate_session(THD89_MASTER_ADDRESS);
+ se_invalidate_session(THD89_FINGER_ADDRESS);
+ if (se_pending_fatal_status == SE_FATAL_STATUS_NONE) {
+ se_pending_fatal_status = status;
+ }
+}
+
+static void se_halt_for_pending_fatal_status(void) {
+ se_fatal_status_t status = se_pending_fatal_status;
+ if (status == SE_FATAL_STATUS_NONE) {
+ return;
+ }
+
+ se_pending_fatal_status = SE_FATAL_STATUS_NONE;
+ memzero(se_send_buffer, sizeof(se_send_buffer));
+ memzero(se_recv_buffer, sizeof(se_recv_buffer));
+ se_recv_len = 0;
+ pin_result_type = PIN_FAILED;
+ pin_passphrase_ret = PIN_FAILED;
+
+ if (status == SE_FATAL_STATUS_SECURITY_ALERT) {
+ error_shutdown("Security alert", "Secure element authentication",
+ "failed.", "Please restart.");
+ }
+ error_shutdown("SE configuration error", "Secure element configuration",
+ "failed.", "Please restart.");
+}
+
+void se_handle_status(uint16_t sw1sw2) {
+ se_record_fatal_status(sw1sw2);
+ se_halt_for_pending_fatal_status();
+}
+
+static secbool se_session_is_initialized(uint8_t addr,
+ const uint8_t *session_key) {
+ if (addr == THD89_MASTER_ADDRESS && session_key == se_session_key &&
+ se_session_init) {
+ return sectrue;
+ }
+ if (addr == THD89_FINGER_ADDRESS && session_key == se_fp_session_key &&
+ se_fp_session_init) {
+ return sectrue;
+ }
+ return secfalse;
+}
+
void se_set_ui_callback(UI_WAIT_CALLBACK callback) { ui_callback = callback; }
static secbool se_get_rand_ex(uint8_t addr, uint8_t *rand, uint16_t rand_len) {
@@ -111,8 +228,9 @@ secbool se_fp_get_rand(uint8_t *rand, uint16_t rand_len) {
static secbool se_reset_se_ex(uint8_t addr) {
uint8_t cmd[5] = {0x00, 0xF0, 0x00, 0x00, 0x00};
- uint16_t resp_len;
+ uint16_t resp_len = 0;
+ se_invalidate_session(addr);
secbool result = thd89_transmit_ex(addr, cmd, sizeof(cmd), NULL, &resp_len);
hal_delay(400); // time for se to power up
@@ -124,8 +242,18 @@ secbool se_reset_se(void) { return se_reset_se_ex(THD89_MASTER_ADDRESS); }
secbool se_fp_reset_se(void) { return se_reset_se_ex(THD89_FINGER_ADDRESS); }
-static void cal_mac(uint8_t *session_key, uint8_t *data, uint32_t len,
- uint8_t *mac) {
+static uint8_t *se_get_session_mac_key(uint8_t *session_key) {
+ if (session_key == se_session_key) {
+ return se_session_mac_key;
+ }
+ if (session_key == se_fp_session_key) {
+ return se_fp_session_mac_key;
+ }
+ return NULL;
+}
+
+static void cal_mac(uint8_t *session_key, const uint8_t *nonce, uint8_t *data,
+ uint32_t len, uint8_t *mac) {
uint8_t pad_buf[16], mac_buf[16], iv[16];
uint32_t pad_len, res_len;
aes_encrypt_ctx ctxe;
@@ -142,6 +270,10 @@ static void cal_mac(uint8_t *session_key, uint8_t *data, uint32_t len,
pad_buf[res_len] = 0x80;
aes_encrypt_key128(session_key, &ctxe);
+ if (nonce) {
+ aes_cbc_encrypt(nonce, mac_buf, AES_BLOCK_SIZE, iv, &ctxe);
+ memcpy(iv, mac_buf, AES_BLOCK_SIZE);
+ }
len += pad_len;
for (uint32_t i = 0; i < (len - AES_BLOCK_SIZE); i += AES_BLOCK_SIZE) {
aes_cbc_encrypt(data + i, mac_buf, AES_BLOCK_SIZE, iv, &ctxe);
@@ -149,117 +281,188 @@ static void cal_mac(uint8_t *session_key, uint8_t *data, uint32_t len,
}
aes_cbc_encrypt(pad_buf, mac_buf, AES_BLOCK_SIZE, iv, &ctxe);
memcpy(mac, mac_buf, 4);
+ memzero(&ctxe, sizeof(ctxe));
+ memzero(iv, sizeof(iv));
+ memzero(pad_buf, sizeof(pad_buf));
+ memzero(mac_buf, sizeof(mac_buf));
}
-static secbool se_transmit_mac_ex(uint8_t addr, uint8_t *session_key,
- uint8_t ins, uint8_t p1, uint8_t p2,
- uint8_t *data, uint16_t data_len,
- uint8_t *recv, uint16_t *recv_len) {
- uint8_t mac[4], iv_random[16];
- uint16_t pad_len;
+static se_secure_response_result_t se_transmit_mac_result_ex(
+ uint8_t addr, uint8_t *session_key, uint8_t ins, uint8_t p1, uint8_t p2,
+ uint8_t *data, uint16_t data_len, uint8_t *recv, uint16_t *recv_len,
+ uint16_t *response_status) {
+ uint8_t *mac_key = NULL;
+ uint8_t mac[4] = {0};
+ uint8_t iv_random[16] = {0};
+ uint8_t request_header[4] = {0};
+ uint16_t pad_len = 0;
+ uint16_t sw1sw2 = 0;
+ se_secure_response_result_t result = SE_SECURE_RESPONSE_INVALID;
+
+ if (response_status != NULL) {
+ *response_status = 0;
+ }
+ if (se_session_is_initialized(addr, session_key) != sectrue) {
+ goto cleanup;
+ }
+ mac_key = se_get_session_mac_key(session_key);
+ if (mac_key == NULL) {
+ goto cleanup;
+ }
+
APDU_CLA = 0x84;
APDU_INS = ins;
APDU_P1 = p1;
APDU_P2 = p2;
APDU_P3 = 0x00;
-
- memset(iv_random, 0x00, sizeof(iv_random));
+ memcpy(request_header, APDU, sizeof(request_header));
if (!se_random_encrypted_ex(addr, session_key, iv_random, 16)) {
- ensure(secfalse, "se_random_encrypted_ex failed");
- }
-
- if (data != NULL && data_len != 0) {
- pad_len = AES_BLOCK_SIZE - (data_len % AES_BLOCK_SIZE);
- memset(APDU_DATA + data_len, 0x00, pad_len);
- APDU_DATA[data_len] = 0x80;
- data_len += pad_len;
- // header + data + mac
- if (data_len > SE_BUF_MAX_LEN - 7 - 4) {
- ensure(secfalse, "data_len too long");
- }
-
- memmove(APDU_DATA, data, data_len - pad_len);
-
- aes_encrypt_ctx ctxe;
- uint8_t iv[16];
- memcpy(iv, iv_random, 16);
- aes_encrypt_key128(session_key, &ctxe);
- aes_cbc_encrypt(APDU_DATA, se_recv_buffer, data_len, iv, &ctxe);
-
- if (data_len > 255) {
- APDU_P3 = 0x00;
- APDU_DATA[0] = (data_len >> 8) & 0xFF;
- APDU_DATA[1] = data_len & 0xFF;
- data_len += 7;
- memcpy(APDU_DATA + 2, se_recv_buffer, data_len);
-
- } else {
- APDU_P3 = data_len & 0xFF;
- data_len += 5;
- memcpy(APDU_DATA, se_recv_buffer, data_len);
+ if ((thd89_last_error() & 0xff00) != 0x6c00) {
+ se_invalidate_session(addr);
}
+ goto cleanup;
+ }
+
+ uint16_t plaintext_len = data_len;
+ pad_len = AES_BLOCK_SIZE - (plaintext_len % AES_BLOCK_SIZE);
+ data_len = plaintext_len + pad_len;
+ // header + data + mac
+ if (data_len > SE_BUF_MAX_LEN - 7 - 4) {
+ goto cleanup;
+ }
+
+ if (data != NULL && plaintext_len != 0) {
+ memmove(APDU_DATA, data, plaintext_len);
+ } else if (plaintext_len != 0) {
+ goto cleanup;
+ }
+ memset(APDU_DATA + plaintext_len, 0x00, pad_len);
+ APDU_DATA[plaintext_len] = 0x80;
+
+ aes_encrypt_ctx ctxe = {0};
+ uint8_t iv[16] = {0};
+ memcpy(iv, iv_random, 16);
+ if (aes_encrypt_key128(session_key, &ctxe) != EXIT_SUCCESS ||
+ aes_cbc_encrypt(APDU_DATA, se_recv_buffer, data_len, iv, &ctxe) !=
+ EXIT_SUCCESS) {
+ memzero(&ctxe, sizeof(ctxe));
+ memzero(iv, sizeof(iv));
+ se_invalidate_session(addr);
+ goto cleanup;
+ }
+ memzero(&ctxe, sizeof(ctxe));
+ memzero(iv, sizeof(iv));
+ uint16_t enc_data_len = data_len;
+
+ if (enc_data_len > 255) {
+ APDU_P3 = 0x00;
+ APDU_DATA[0] = (enc_data_len >> 8) & 0xFF;
+ APDU_DATA[1] = enc_data_len & 0xFF;
+ memcpy(APDU_DATA + 2, se_recv_buffer, enc_data_len);
+ data_len = enc_data_len + 7;
- cal_mac(session_key, APDU, data_len, mac);
- memcpy(APDU + data_len, mac, 4);
- data_len += 4;
} else {
- data_len = 5;
+ APDU_P3 = enc_data_len & 0xFF;
+ memcpy(APDU_DATA, se_recv_buffer, enc_data_len);
+ data_len = enc_data_len + 5;
}
+
+ cal_mac(mac_key, iv_random, APDU, data_len, mac);
+ memcpy(APDU + data_len, mac, 4);
+ data_len += 4;
se_recv_len = sizeof(se_recv_buffer);
- if (!thd89_transmit_ex(addr, APDU, data_len, se_recv_buffer, &se_recv_len)) {
- memset(APDU, 0x00, sizeof(APDU));
- return secfalse;
+ if (thd89_transmit_raw_ex(addr, APDU, data_len, se_recv_buffer, &se_recv_len,
+ &sw1sw2) != sectrue) {
+ se_invalidate_session(addr);
+ goto cleanup;
+ }
+ if (response_status != NULL) {
+ *response_status = sw1sw2;
}
- if (se_recv_len) {
- if ((se_recv_len - 4) % AES_BLOCK_SIZE) {
- ensure(secfalse, "se_recv_len error");
- }
- cal_mac(session_key, se_recv_buffer, se_recv_len - 4, mac);
- if (memcmp(mac, se_recv_buffer + se_recv_len - 4, 4) != 0) {
- ensure(secfalse, "se_recv_buffer mac error");
- }
+ thd89_v2_response_shape_t shape =
+ thd89_v2_classify_response(se_recv_len, sw1sw2);
+ if (shape == THD89_V2_RESPONSE_NO_MAC_6C) {
+ result = SE_SECURE_RESPONSE_NO_MAC_6C;
+ goto cleanup;
+ }
+ if (shape != THD89_V2_RESPONSE_MAC_REQUIRED) {
+ se_invalidate_session(addr);
+ goto cleanup;
+ }
- se_recv_len -= 4;
-
- aes_decrypt_ctx dtxe;
- uint8_t iv[16];
- memcpy(iv, iv_random, 16);
- aes_decrypt_key128(session_key, &dtxe);
- aes_cbc_decrypt(se_recv_buffer, APDU, se_recv_len, iv, &dtxe);
- pad_len = 1;
- for (uint8_t i = 0; i < 16; i++) {
- if (APDU[se_recv_len - 1 - i] == 0x80) {
- break;
- } else if (APDU[se_recv_len - 1 - i] == 0x00) {
- pad_len++;
- } else {
- memset(APDU, 0x00, sizeof(APDU));
- ensure(secfalse, "se_recv_buffer pad error");
- }
- }
- se_recv_len -= pad_len;
+ uint16_t ciphertext_len = se_recv_len - 4;
+ thd89_v2_calculate_response_mac(mac_key, request_header, iv_random,
+ se_recv_buffer, ciphertext_len, sw1sw2, mac);
+ if (!thd89_v2_constant_time_equal(mac, se_recv_buffer + ciphertext_len, 4)) {
+ se_invalidate_session(addr);
+ goto cleanup;
+ }
- if (recv_len == NULL) {
- ensure(secfalse, "recv_len is NULL");
- }
+ se_record_fatal_status(sw1sw2);
- if (*recv_len < se_recv_len) {
- memset(APDU, 0x00, sizeof(APDU));
- ensure(secfalse, "recv_len too short");
- }
- *recv_len = se_recv_len;
- if (recv) {
- memcpy(recv, APDU, *recv_len);
- }
- } else {
+ if (sw1sw2 != 0x9000) {
+ result = SE_SECURE_RESPONSE_AUTHENTICATED_ERROR;
+ goto cleanup;
+ }
+
+ if (ciphertext_len == 0) {
if (recv_len != NULL) {
*recv_len = 0;
}
+ result = SE_SECURE_RESPONSE_OK;
+ goto cleanup;
+ }
+
+ aes_decrypt_ctx dtxe = {0};
+ memcpy(iv, iv_random, sizeof(iv));
+ if (aes_decrypt_key128(session_key, &dtxe) != EXIT_SUCCESS ||
+ aes_cbc_decrypt(se_recv_buffer, APDU, ciphertext_len, iv, &dtxe) !=
+ EXIT_SUCCESS) {
+ memzero(&dtxe, sizeof(dtxe));
+ memzero(iv, sizeof(iv));
+ se_invalidate_session(addr);
+ goto cleanup;
+ }
+ memzero(&dtxe, sizeof(dtxe));
+ memzero(iv, sizeof(iv));
+
+ uint16_t unpadded_len = 0;
+ if (!thd89_v2_unpad_iso7816_4(APDU, ciphertext_len, &unpadded_len)) {
+ se_invalidate_session(addr);
+ goto cleanup;
+ }
+ if (recv_len == NULL || (unpadded_len != 0 && recv == NULL) ||
+ *recv_len < unpadded_len) {
+ goto cleanup;
+ }
+ *recv_len = unpadded_len;
+ if (unpadded_len != 0) {
+ memcpy(recv, APDU, unpadded_len);
+ }
+ result = SE_SECURE_RESPONSE_OK;
+
+cleanup:
+ memzero(mac, sizeof(mac));
+ memzero(iv_random, sizeof(iv_random));
+ memzero(request_header, sizeof(request_header));
+ memzero(se_send_buffer, sizeof(se_send_buffer));
+ memzero(se_recv_buffer, sizeof(se_recv_buffer));
+ se_recv_len = 0;
+ if (thd89_irq_nest == 0) {
+ se_halt_for_pending_fatal_status();
}
- memset(APDU, 0x00, sizeof(APDU));
- return sectrue;
+ return result;
+}
+
+static secbool se_transmit_mac_ex(uint8_t addr, uint8_t *session_key,
+ uint8_t ins, uint8_t p1, uint8_t p2,
+ uint8_t *data, uint16_t data_len,
+ uint8_t *recv, uint16_t *recv_len) {
+ return sectrue * (se_transmit_mac_result_ex(addr, session_key, ins, p1, p2,
+ data, data_len, recv, recv_len,
+ NULL) == SE_SECURE_RESPONSE_OK);
}
secbool se_transmit_mac(uint8_t ins, uint8_t p1, uint8_t p2, uint8_t *data,
@@ -272,6 +475,7 @@ secbool se_transmit_mac(uint8_t ins, uint8_t p1, uint8_t p2, uint8_t *data,
if (thd89_irq_nest == 0) {
enable_irq(irq);
}
+ se_halt_for_pending_fatal_status();
return result;
}
@@ -287,6 +491,7 @@ secbool se_fp_transmit_mac(uint8_t ins, uint8_t p1, uint8_t p2, uint8_t *data,
if (thd89_irq_nest == 0) {
enable_irq(irq);
}
+ se_halt_for_pending_fatal_status();
return result;
}
@@ -303,115 +508,98 @@ secbool se_random_encrypted(uint8_t *rand, uint16_t len) {
secbool se_random_encrypted_ex(uint8_t addr, uint8_t *session_key,
uint8_t *rand, uint16_t len) {
+ uint8_t *mac_key = NULL;
uint16_t recv_len = SE_BUF_MAX_LEN;
uint8_t cmd[7] = {0xa4, 0x84, 0x00, 0x00, 0x02};
- uint8_t mac[4];
- uint8_t pad_len;
- secbool ret;
- cmd[5] = (len >> 8) & 0xff;
- cmd[6] = len & 0xff;
+ uint8_t mac[4] = {0};
+ uint8_t transaction[16] = {0};
+ uint16_t sw1sw2 = 0;
+ secbool ret = secfalse;
- for (int retry = 0; retry < 3; retry++) {
- recv_len = SE_BUF_MAX_LEN;
- ret = thd89_transmit_ex(addr, cmd, sizeof(cmd), se_recv_buffer, &recv_len);
- if (ret == sectrue) {
- break;
- }
+ if (se_session_is_initialized(addr, session_key) != sectrue) {
+ return secfalse;
+ }
+ mac_key = se_get_session_mac_key(session_key);
+ if (mac_key == NULL) {
+ return secfalse;
}
- ensure(ret, "thd89_transmit_ex failed");
-
- if (recv_len) {
- if ((recv_len - 4) % AES_BLOCK_SIZE) {
- ensure(secfalse, "recv_len error");
- }
+ if (rand == NULL && len != 0) {
+ return secfalse;
+ }
+ cmd[5] = (len >> 8) & 0xff;
+ cmd[6] = len & 0xff;
- cal_mac(session_key, se_recv_buffer, recv_len - 4, mac);
- if (memcmp(mac, se_recv_buffer + recv_len - 4, 4) != 0) {
- ensure(secfalse, "mac error");
- }
+ if (thd89_transmit_raw_ex(addr, cmd, sizeof(cmd), se_recv_buffer, &recv_len,
+ &sw1sw2) != sectrue) {
+ se_invalidate_session(addr);
+ goto cleanup;
+ }
- recv_len -= 4;
-
- aes_decrypt_ctx dtxe;
- aes_decrypt_key128(session_key, &dtxe);
- aes_ecb_decrypt(se_recv_buffer, se_recv_buffer, recv_len, &dtxe);
- pad_len = 1;
- for (uint8_t i = 0; i < 16; i++) {
- if (se_recv_buffer[recv_len - 1 - i] == 0x80) {
- break;
- } else if (se_recv_buffer[recv_len - 1 - i] == 0x00) {
- pad_len++;
- } else {
- ensure(secfalse, "pad error");
- }
- }
- recv_len -= pad_len;
+ thd89_v2_response_shape_t shape =
+ thd89_v2_classify_response(recv_len, sw1sw2);
+ if (shape == THD89_V2_RESPONSE_NO_MAC_6C) {
+ goto cleanup;
+ }
+ if (shape != THD89_V2_RESPONSE_MAC_REQUIRED) {
+ se_invalidate_session(addr);
+ goto cleanup;
+ }
- if (recv_len != len) {
- ensure(secfalse, "recv_len error");
+ uint16_t ciphertext_len = recv_len - 4;
+ thd89_v2_calculate_response_mac(mac_key, cmd, transaction, se_recv_buffer,
+ ciphertext_len, sw1sw2, mac);
+ if (!thd89_v2_constant_time_equal(mac, se_recv_buffer + ciphertext_len, 4)) {
+ se_invalidate_session(addr);
+ goto cleanup;
+ }
+ se_record_fatal_status(sw1sw2);
+ if (sw1sw2 != 0x9000) {
+ goto cleanup;
+ }
+ if (ciphertext_len == 0) {
+ if (len == 0) {
+ ret = sectrue;
}
+ goto cleanup;
}
- memcpy(rand, se_recv_buffer, recv_len);
-
- return sectrue;
-}
-
-secbool se_sync_session_key_ex_old(uint8_t addr, uint8_t *session_key) {
- uint8_t r1[16], r2[16], r3[32];
- uint8_t default_key[16] = {0xff};
-
- memset(default_key, 0xff, 16);
- uint8_t data_buf[64], hash_buf[32];
- uint8_t sync_cmd[5 + 48] = {0x00, 0xfa, 0x00, 0x00, 0x30};
- uint16_t recv_len = sizeof(data_buf);
- aes_encrypt_ctx en_ctxe;
- aes_decrypt_ctx de_ctxe;
- memzero(data_buf, sizeof(data_buf));
- ensure(flash_otp_read(FLASH_OTP_BLOCK_THD89_SESSION_KEY, 0, default_key, 16),
- NULL);
-
- // get random from se
- se_get_rand_ex(addr, r1, 16);
- // get random itself
- random_buffer(r2, 16);
- // organization data1
- memcpy(r3, r1, sizeof(r1));
- memcpy(r3 + sizeof(r1), r2, sizeof(r2));
- aes_init();
- aes_encrypt_key128(default_key, &en_ctxe);
- aes_ecb_encrypt(r3, data_buf, sizeof(r1) + sizeof(r2), &en_ctxe);
-
- // cal tmp sessionkey with x hash256
- memzero(r3, sizeof(r3));
- xor_cal(r1, r2, sizeof(r1), r3);
- memcpy(r3 + 16, default_key, 16);
- sha256_Raw(r3, 32, hash_buf);
- // use session key organization data2
- memcpy(session_key, hash_buf, 16);
- aes_encrypt_key128(session_key, &en_ctxe);
- aes_ecb_encrypt(r1, data_buf + 32, sizeof(r1), &en_ctxe);
- // send data1 + data2 to se and recv returned result
- memcpy(sync_cmd + 5, data_buf, 48);
- if (!thd89_transmit_ex(addr, sync_cmd, sizeof(sync_cmd), data_buf,
- &recv_len)) {
- memset(session_key, 0x00, SESSION_KEYLEN);
- return secfalse;
+ aes_decrypt_ctx dtxe = {0};
+ if (aes_decrypt_key128(session_key, &dtxe) != EXIT_SUCCESS ||
+ aes_ecb_decrypt(se_recv_buffer, se_recv_buffer, ciphertext_len, &dtxe) !=
+ EXIT_SUCCESS) {
+ memzero(&dtxe, sizeof(dtxe));
+ se_invalidate_session(addr);
+ goto cleanup;
}
+ memzero(&dtxe, sizeof(dtxe));
- // handle the returned data
- aes_decrypt_key128(session_key, &de_ctxe);
- aes_ecb_decrypt(data_buf, r3, recv_len, &de_ctxe);
- if (memcmp(r2, r3, sizeof(r2)) != 0) {
- memset(session_key, 0x00, SESSION_KEYLEN);
- return secfalse;
+ uint16_t plaintext_len = 0;
+ if (!thd89_v2_unpad_iso7816_4(se_recv_buffer, ciphertext_len,
+ &plaintext_len)) {
+ se_invalidate_session(addr);
+ goto cleanup;
}
+ if (plaintext_len != len) {
+ se_invalidate_session(addr);
+ goto cleanup;
+ }
+ if (len != 0) {
+ memcpy(rand, se_recv_buffer, len);
+ }
+ ret = sectrue;
- return sectrue;
+cleanup:
+ memzero(mac, sizeof(mac));
+ memzero(transaction, sizeof(transaction));
+ memzero(se_recv_buffer, sizeof(se_recv_buffer));
+ if (thd89_irq_nest == 0) {
+ se_halt_for_pending_fatal_status();
+ }
+ return ret;
}
-static void get_pubkey(uint8_t addr, uint8_t *pubkey) {
+static secbool get_pubkey(uint8_t addr, uint8_t *pubkey) {
uint8_t otp_pubkey_1, otp_pubkey_2;
switch (addr) {
case THD89_MASTER_ADDRESS:
@@ -431,65 +619,134 @@ static void get_pubkey(uint8_t addr, uint8_t *pubkey) {
otp_pubkey_2 = FLASH_OTP_BLOCK_THD89_4_PUBKEY2;
break;
default:
- return;
+ return secfalse;
}
- ensure(flash_otp_read(otp_pubkey_1, 0, pubkey, 32), NULL);
- ensure(flash_otp_read(otp_pubkey_2, 0, pubkey + 32, 32), NULL);
+ if (flash_otp_read(otp_pubkey_1, 0, pubkey, 32) != sectrue ||
+ flash_otp_read(otp_pubkey_2, 0, pubkey + 32, 32) != sectrue) {
+ return secfalse;
+ }
+ return sectrue;
}
-static secbool se_sync_session_key_ex(uint8_t addr, uint8_t *session_key) {
- uint8_t pubkey[65], session_tmp[65];
- uint8_t prikey_tmp[32], pubkey_tmp[65];
- uint8_t r1[16], r2[16], r2_enc[16];
- uint8_t digest[32];
- uint16_t recv_len = 64;
- aes_encrypt_ctx en_ctxe;
-
- pubkey[0] = 0x04;
- get_pubkey(addr, pubkey + 1);
-
- random_buffer(r1, 16);
- // get random from se
- se_get_rand_ex(addr, r2, 16);
+static secbool se_get_session_random_ex(uint8_t addr, uint8_t se_random[16]) {
+ uint8_t cmd[7] = {0x00, 0x84, 0x00, 0x00, 0x02, 0x00, 0x10};
+ uint16_t recv_len = 16;
+ uint16_t sw1sw2 = 0;
- random_buffer(prikey_tmp, sizeof(prikey_tmp));
- ecdsa_get_public_key65(&secp256k1, prikey_tmp, pubkey_tmp);
-
- if (ecdh_multiply(&secp256k1, prikey_tmp, pubkey, session_tmp) != 0) {
+ if (thd89_transmit_raw_ex(addr, cmd, sizeof(cmd), se_random, &recv_len,
+ &sw1sw2) != sectrue) {
return secfalse;
}
+ se_handle_status(sw1sw2);
+ return sectrue * (sw1sw2 == 0x9000 && recv_len == 16);
+}
- memcpy(session_key, session_tmp + 1, 16);
-
- aes_init();
- aes_encrypt_key128(session_key, &en_ctxe);
- aes_ecb_encrypt(r2, r2_enc, sizeof(r2), &en_ctxe);
-
- uint8_t sync_cmd[5 + 16 + 16 + 64] = {0x00, 0xfa, 0x00, 0x00, 0x60};
- uint8_t signature[64];
+static secbool se_sync_session_key_ex(uint8_t addr, uint8_t *session_key,
+ uint8_t *session_mac_key) {
+ uint8_t se_public_key[65] = {0};
+ uint8_t ephemeral_private_key[32] = {0};
+ uint8_t ephemeral_public_key[65] = {0};
+ uint8_t shared_point[65] = {0};
+ uint8_t se_random[16] = {0};
+ uint8_t mcu_random[16] = {0};
+ uint8_t candidate_enc_key[16] = {0};
+ uint8_t candidate_mac_key[16] = {0};
+ uint8_t confirm_key[32] = {0};
+ uint8_t encrypted_challenge[16] = {0};
+ uint8_t request_data[96] = {0};
+ uint8_t sync_cmd[5 + 96] = {0x00, 0xfa, 0x01, 0x00, 0x60};
+ uint8_t confirmation[32] = {0};
+ uint8_t expected_confirmation[32] = {0};
+ uint16_t recv_len = sizeof(confirmation);
+ uint16_t sw1sw2 = 0;
+ aes_encrypt_ctx en_ctxe = {0};
+ secbool success = secfalse;
- memcpy(sync_cmd + 5, r1, 16);
- memcpy(sync_cmd + 5 + 16, r2_enc, 16);
- memcpy(sync_cmd + 5 + 32, pubkey_tmp + 1, 64);
- if (!thd89_transmit_ex(addr, sync_cmd, sizeof(sync_cmd), signature,
- &recv_len)) {
- memset(session_key, 0x00, SESSION_KEYLEN);
- return secfalse;
+ se_invalidate_session(addr);
+ se_public_key[0] = 0x04;
+ if (get_pubkey(addr, se_public_key + 1) != sectrue ||
+ se_get_session_random_ex(addr, se_random) != sectrue) {
+ goto cleanup;
}
- if (recv_len != 64) {
- memset(session_key, 0x00, SESSION_KEYLEN);
- return secfalse;
+
+ random_buffer(mcu_random, sizeof(mcu_random));
+ for (uint8_t attempt = 0; attempt < 16; attempt++) {
+ random_buffer(ephemeral_private_key, sizeof(ephemeral_private_key));
+ if (ecdsa_get_public_key65(&secp256k1, ephemeral_private_key,
+ ephemeral_public_key) == 0) {
+ break;
+ }
+ memzero(ephemeral_private_key, sizeof(ephemeral_private_key));
}
- sha256_Raw(r1, 16, digest);
- if (ecdsa_verify_digest(&secp256k1, pubkey, signature, digest) != 0) {
- return secfalse;
+ if (ephemeral_public_key[0] != 0x04 ||
+ ecdh_multiply(&secp256k1, ephemeral_private_key, se_public_key,
+ shared_point) != 0 ||
+ shared_point[0] != 0x04) {
+ goto cleanup;
}
- return sectrue;
+ thd89_v2_derive_session_keys(shared_point + 1, se_random, mcu_random,
+ candidate_enc_key, candidate_mac_key,
+ confirm_key);
+
+ aes_init();
+ if (aes_encrypt_key128(candidate_enc_key, &en_ctxe) != EXIT_SUCCESS ||
+ aes_ecb_encrypt(se_random, encrypted_challenge,
+ sizeof(encrypted_challenge), &en_ctxe) != EXIT_SUCCESS) {
+ goto cleanup;
+ }
+
+ memcpy(request_data, mcu_random, sizeof(mcu_random));
+ memcpy(request_data + 16, encrypted_challenge, sizeof(encrypted_challenge));
+ memcpy(request_data + 32, ephemeral_public_key + 1, 64);
+ memcpy(sync_cmd + 5, request_data, sizeof(request_data));
+
+ if (thd89_transmit_raw_ex(addr, sync_cmd, sizeof(sync_cmd), confirmation,
+ &recv_len, &sw1sw2) != sectrue) {
+ goto cleanup;
+ }
+ se_handle_status(sw1sw2);
+ if (sw1sw2 != 0x9000 || recv_len != sizeof(confirmation)) {
+ goto cleanup;
+ }
+
+ thd89_v2_calculate_confirmation(confirm_key, se_random, request_data,
+ expected_confirmation);
+ if (!thd89_v2_constant_time_equal(confirmation, expected_confirmation,
+ sizeof(confirmation))) {
+ goto cleanup;
+ }
+
+ memcpy(session_key, candidate_enc_key, SESSION_KEYLEN);
+ memcpy(session_mac_key, candidate_mac_key, SESSION_KEYLEN);
+ success = sectrue;
+
+cleanup:
+ memzero(&en_ctxe, sizeof(en_ctxe));
+ memzero(se_public_key, sizeof(se_public_key));
+ memzero(ephemeral_private_key, sizeof(ephemeral_private_key));
+ memzero(ephemeral_public_key, sizeof(ephemeral_public_key));
+ memzero(shared_point, sizeof(shared_point));
+ memzero(se_random, sizeof(se_random));
+ memzero(mcu_random, sizeof(mcu_random));
+ memzero(candidate_enc_key, sizeof(candidate_enc_key));
+ memzero(candidate_mac_key, sizeof(candidate_mac_key));
+ memzero(confirm_key, sizeof(confirm_key));
+ memzero(encrypted_challenge, sizeof(encrypted_challenge));
+ memzero(request_data, sizeof(request_data));
+ memzero(sync_cmd, sizeof(sync_cmd));
+ memzero(confirmation, sizeof(confirmation));
+ memzero(expected_confirmation, sizeof(expected_confirmation));
+ if (success != sectrue) {
+ se_invalidate_session(addr);
+ }
+ return success;
}
static secbool _se_sync_session_key(void) {
- if (sectrue == se_sync_session_key_ex(THD89_MASTER_ADDRESS, se_session_key)) {
+ se_session_init = false;
+ if (sectrue == se_sync_session_key_ex(THD89_MASTER_ADDRESS, se_session_key,
+ se_session_mac_key)) {
se_session_init = true;
return sectrue;
}
@@ -497,8 +754,9 @@ static secbool _se_sync_session_key(void) {
}
static secbool _se_fp_sync_session_key(void) {
- if (sectrue ==
- se_sync_session_key_ex(THD89_FINGER_ADDRESS, se_fp_session_key)) {
+ se_fp_session_init = false;
+ if (sectrue == se_sync_session_key_ex(THD89_FINGER_ADDRESS, se_fp_session_key,
+ se_fp_session_mac_key)) {
se_fp_session_init = true;
return sectrue;
}
@@ -507,7 +765,10 @@ static secbool _se_fp_sync_session_key(void) {
secbool se_sync_session_key(void) {
ensure(_se_sync_session_key(), "se sync session key failed");
- ensure(_se_fp_sync_session_key(), "se fp sync session key failed");
+ if (_se_fp_sync_session_key() != sectrue) {
+ se_invalidate_session(THD89_MASTER_ADDRESS);
+ ensure(secfalse, "se fp sync session key failed");
+ }
return sectrue;
}
@@ -562,25 +823,6 @@ secbool se_derive_xmr_key(const char *curve, const uint32_t *address_n,
return sectrue;
}
-secbool se_derive_xmr_private_key(const uint8_t *pubkey, const uint32_t index,
- uint8_t *prikey) {
- uint8_t resp[32];
- uint16_t resp_len = sizeof(resp);
-
- uint8_t data[32 + 4];
-
- memcpy(data, pubkey, 32);
- memcpy(data + 32, &index, 4);
-
- if (!se_transmit_mac(SE_INS_DERIVE, 0x00, 0x02, data, sizeof(data), resp,
- &resp_len)) {
- return secfalse;
- }
- memcpy(prikey, resp, 32);
-
- return sectrue;
-}
-
secbool se_xmr_get_tx_key(const uint8_t *rand, const uint8_t *hash,
uint8_t *out) {
uint8_t resp[32];
@@ -600,6 +842,125 @@ secbool se_xmr_get_tx_key(const uint8_t *rand, const uint8_t *hash,
return sectrue;
}
+secbool se_xmr_generate_key_image(const uint8_t recv_deriv[32],
+ uint32_t real_idx,
+ const uint8_t subaddr_sk[32],
+ const uint8_t out_key[32],
+ uint8_t key_image[32]) {
+ uint8_t data[100] = {0};
+ uint8_t response[32] = {0};
+ uint16_t response_len = sizeof(response);
+ secbool result = secfalse;
+
+ if (recv_deriv == NULL || subaddr_sk == NULL || out_key == NULL ||
+ key_image == NULL) {
+ goto cleanup;
+ }
+
+ memcpy(data, recv_deriv, 32);
+ data[32] = (uint8_t)real_idx;
+ data[33] = (uint8_t)(real_idx >> 8);
+ data[34] = (uint8_t)(real_idx >> 16);
+ data[35] = (uint8_t)(real_idx >> 24);
+ memcpy(data + 36, subaddr_sk, 32);
+ memcpy(data + 68, out_key, 32);
+
+ if (se_transmit_mac(SE_INS_DERIVE, 0x00, 0x06, data, sizeof(data),
+ response, &response_len) != sectrue ||
+ response_len != sizeof(response)) {
+ goto cleanup;
+ }
+
+ memcpy(key_image, response, sizeof(response));
+ result = sectrue;
+
+cleanup:
+ if (result != sectrue && key_image != NULL) {
+ memzero(key_image, 32);
+ }
+ memzero(data, sizeof(data));
+ memzero(response, sizeof(response));
+ return result;
+}
+
+secbool se_xmr_secret_nonce_begin(const uint8_t recv_deriv[32],
+ uint32_t real_idx,
+ const uint8_t subaddr_sk[32],
+ const uint8_t out_key[32], uint8_t out[97]) {
+ uint8_t data[100] = {0};
+ uint8_t response[97] = {0};
+ uint16_t response_len = sizeof(response);
+ secbool result = secfalse;
+
+ if (recv_deriv == NULL || subaddr_sk == NULL || out_key == NULL ||
+ out == NULL) {
+ goto cleanup;
+ }
+
+ memcpy(data, recv_deriv, 32);
+ data[32] = (uint8_t)real_idx;
+ data[33] = (uint8_t)(real_idx >> 8);
+ data[34] = (uint8_t)(real_idx >> 16);
+ data[35] = (uint8_t)(real_idx >> 24);
+ memcpy(data + 36, subaddr_sk, 32);
+ memcpy(data + 68, out_key, 32);
+
+ if (se_transmit_mac(SE_INS_DERIVE, 0x00, 0x07, data, sizeof(data),
+ response, &response_len) != sectrue ||
+ response_len != sizeof(response) || response[96] == 0) {
+ goto cleanup;
+ }
+
+ memcpy(out, response, sizeof(response));
+ result = sectrue;
+
+cleanup:
+ if (result != sectrue && out != NULL) {
+ memzero(out, 97);
+ }
+ memzero(data, sizeof(data));
+ memzero(response, sizeof(response));
+ return result;
+}
+
+secbool se_xmr_secret_response_finish(uint8_t session_id, const uint8_t c[32],
+ const uint8_t mu_p[32],
+ const uint8_t mu_c[32],
+ const uint8_t z[32], uint8_t s[32]) {
+ uint8_t data[129] = {0};
+ uint8_t response[32] = {0};
+ uint16_t response_len = sizeof(response);
+ secbool result = secfalse;
+
+ if (session_id == 0 || c == NULL || mu_p == NULL || mu_c == NULL ||
+ z == NULL || s == NULL) {
+ goto cleanup;
+ }
+
+ data[0] = session_id;
+ memcpy(data + 1, c, 32);
+ memcpy(data + 33, mu_p, 32);
+ memcpy(data + 65, mu_c, 32);
+ memcpy(data + 97, z, 32);
+
+ if (se_transmit_mac(SE_INS_DERIVE, 0x00, 0x08, data, sizeof(data),
+ response, &response_len) != sectrue ||
+ response_len != sizeof(response)) {
+ goto cleanup;
+ }
+
+ memcpy(s, response, sizeof(response));
+ result = sectrue;
+
+cleanup:
+ if (result != sectrue && s != NULL) {
+ memzero(s, 32);
+ }
+ memzero(data, sizeof(data));
+ memzero(response, sizeof(response));
+ return result;
+}
+
static secbool _se_reset_storage(void) {
uint8_t rand[16];
@@ -682,6 +1043,86 @@ int se_get_version(uint8_t addr, char *ver, uint16_t in_len) {
return _se_get_ver_info(addr, 0x00, (uint8_t *)ver, in_len);
}
+static secbool se_parse_version_component(const char **cursor,
+ uint8_t *component) {
+ uint16_t value = 0;
+ bool has_digit = false;
+
+ while (**cursor >= '0' && **cursor <= '9') {
+ value = value * 10U + (uint8_t)(**cursor - '0');
+ if (value > UINT8_MAX) {
+ return secfalse;
+ }
+ has_digit = true;
+ (*cursor)++;
+ }
+ if (!has_digit) {
+ return secfalse;
+ }
+ *component = (uint8_t)value;
+ return sectrue;
+}
+
+static secbool se_version_is_at_least(const char *version,
+ uint8_t required_major,
+ uint8_t required_minor,
+ uint8_t required_patch) {
+ uint8_t components[4] = {0};
+ const char *cursor = version;
+
+ if (cursor == NULL) {
+ return secfalse;
+ }
+ for (uint8_t index = 0; index < 4; index++) {
+ if (se_parse_version_component(&cursor, &components[index]) != sectrue) {
+ return secfalse;
+ }
+ if (*cursor == '\0') {
+ if (index < 2) {
+ return secfalse;
+ }
+ break;
+ }
+ if (*cursor != '.' || index == 3) {
+ return secfalse;
+ }
+ cursor++;
+ }
+ if (components[0] != required_major) {
+ return sectrue * (components[0] > required_major);
+ }
+ if (components[1] != required_minor) {
+ return sectrue * (components[1] > required_minor);
+ }
+ return sectrue * (components[2] >= required_patch);
+}
+
+secbool se_all_versions_at_least(uint8_t required_major,
+ uint8_t required_minor,
+ uint8_t required_patch) {
+ static const uint8_t addresses[] = {
+ THD89_1ST_ADDRESS,
+ THD89_2ND_ADDRESS,
+ THD89_3RD_ADDRESS,
+ THD89_4TH_ADDRESS,
+ };
+
+ for (size_t i = 0; i < sizeof(addresses); i++) {
+ char version[16] = {0};
+ int version_len =
+ se_get_version(addresses[i], version, sizeof(version) - 1U);
+ if (version_len <= 0 || version_len >= (int)sizeof(version)) {
+ return secfalse;
+ }
+ version[version_len] = '\0';
+ if (se_version_is_at_least(version, required_major, required_minor,
+ required_patch) != sectrue) {
+ return secfalse;
+ }
+ }
+ return sectrue;
+}
+
int se_get_build_id(uint8_t addr, char *build_id, uint16_t in_len) {
return _se_get_ver_info(addr, 0x01, (uint8_t *)build_id, in_len);
}
@@ -752,7 +1193,7 @@ char *se01_get_boot_version(void) {
}
char *se01_get_boot_build_id(void) {
- static char build_id[8] = {0};
+ static char build_id[16] = {0};
if (strlen(build_id) > 0) {
return build_id;
}
@@ -826,7 +1267,7 @@ char *se02_get_boot_version(void) {
}
char *se02_get_boot_build_id(void) {
- static char build_id[8] = {0};
+ static char build_id[16] = {0};
if (strlen(build_id) > 0) {
return build_id;
}
@@ -900,7 +1341,7 @@ char *se03_get_boot_version(void) {
}
char *se03_get_boot_build_id(void) {
- static char build_id[8] = {0};
+ static char build_id[16] = {0};
if (strlen(build_id) > 0) {
return build_id;
}
@@ -974,7 +1415,7 @@ char *se04_get_boot_version(void) {
}
char *se04_get_boot_build_id(void) {
- static char build_id[8] = {0};
+ static char build_id[16] = {0};
if (strlen(build_id) > 0) {
return build_id;
}
@@ -1000,13 +1441,13 @@ uint8_t *se04_get_boot_hash(void) {
}
secbool se_get_ecdh_pubkey(uint8_t addr, uint8_t *key) {
- uint8_t cmd[6] = {0x00, 0xF5, 0x00, 0x05, 0x01, 0x01};
+ uint8_t cmd[5] = {0x00, 0xF5, 0x00, 0x05, 0x00};
uint16_t resp_len = 64;
return thd89_transmit_ex(addr, cmd, sizeof(cmd), key, &resp_len);
}
secbool se_lock_ecdh_pubkey(uint8_t addr) {
- uint8_t cmd[6] = {0x00, 0xF5, 0x00, 0x05, 0x01, 0x02};
+ uint8_t cmd[5] = {0x00, 0xF5, 0x00, 0x06, 0x00};
return thd89_transmit_ex(addr, cmd, sizeof(cmd), NULL, NULL);
}
@@ -1174,11 +1615,17 @@ secbool se_setPin(const char *pin) {
}
static secbool se_verifyPin_ex(uint8_t addr, uint8_t *session_key,
- const char *pin, pin_type_t pin_type) {
+ const char *pin, pin_type_t pin_type,
+ uint16_t *response_status) {
uint8_t pin_buf[50 + 2] = {0};
uint8_t resp[1] = {0};
uint16_t resp_len = 1;
uint8_t data_len = 0;
+ uint16_t status = 0;
+
+ if (response_status != NULL) {
+ *response_status = 0;
+ }
if (strlen(pin) > PIN_MAX_LEN) {
return secfalse;
@@ -1196,10 +1643,17 @@ static secbool se_verifyPin_ex(uint8_t addr, uint8_t *session_key,
pin_buf[pin_buf[0] + 1] = pin_type;
data_len++;
- if (!se_transmit_mac_ex(addr, session_key, SE_INS_PIN, 0x00, 0x03, pin_buf,
- data_len, resp, &resp_len)) {
+ se_secure_response_result_t transmit_result =
+ se_transmit_mac_result_ex(addr, session_key, SE_INS_PIN, 0x00, 0x03,
+ pin_buf, data_len, resp, &resp_len, &status);
+ if (transmit_result != SE_SECURE_RESPONSE_OK) {
memset(pin_buf, 0, sizeof(pin_buf));
- if (0x6f80 == thd89_last_error()) {
+ if (transmit_result == SE_SECURE_RESPONSE_AUTHENTICATED_ERROR &&
+ response_status != NULL) {
+ *response_status = status;
+ }
+ if (transmit_result == SE_SECURE_RESPONSE_AUTHENTICATED_ERROR &&
+ status == 0x6f80) {
error_reset("You have entered the", "wipe code. All private",
"data has been erased.", NULL);
}
@@ -1220,7 +1674,7 @@ static secbool se_verifyPin_ex(uint8_t addr, uint8_t *session_key,
static secbool se_fp_verifyPin(const char *pin) {
return se_verifyPin_ex(THD89_FINGER_ADDRESS, se_fp_session_key, pin,
- PIN_TYPE_USER);
+ PIN_TYPE_USER, NULL);
}
static void reset_storage_and_restart(void) {
error_pin_max_prompt();
@@ -1231,15 +1685,21 @@ static void reset_storage_and_restart(void) {
restart();
}
secbool se_verifyPin(const char *pin, pin_type_t pin_type) {
- secbool result =
- se_verifyPin_ex(THD89_MASTER_ADDRESS, se_session_key, pin, pin_type);
+ uint16_t response_status = 0;
+ secbool result = se_verifyPin_ex(THD89_MASTER_ADDRESS, se_session_key, pin,
+ pin_type, &response_status);
if (result == sectrue) {
if (pin_type != PIN_TYPE_PASSPHRASE_PIN_CHECK) {
- secbool fp_result = se_verifyPin_ex(THD89_FINGER_ADDRESS,
- se_fp_session_key, pin, pin_type);
+ uint16_t fp_response_status = 0;
+ secbool fp_result =
+ se_verifyPin_ex(THD89_FINGER_ADDRESS, se_fp_session_key, pin,
+ pin_type, &fp_response_status);
if (fp_result == sectrue) {
return sectrue;
}
+ if (fp_response_status == SE_SW_PIN_RETRY_LIMIT_REACHED) {
+ reset_storage_and_restart();
+ }
// else {
// if (se_fp_hasPin()) {
// ensure(se_fp_reset_storage(), "reset fp storage failed");
@@ -1253,6 +1713,9 @@ secbool se_verifyPin(const char *pin, pin_type_t pin_type) {
return sectrue;
}
} else {
+ if (response_status == SE_SW_PIN_RETRY_LIMIT_REACHED) {
+ reset_storage_and_restart();
+ }
uint8_t retry_cnts = 0;
ensure(se_getRetryTimes(&retry_cnts), "get retry times failed");
if (retry_cnts == 0) {
@@ -1367,6 +1830,7 @@ static secbool se_set_pin_passphrase_ex(uint8_t addr, uint8_t *session_key,
uint8_t buf[2 * PIN_MAX_LENGTH + PASSPHRASE_MAX_LENGTH + 3];
uint8_t resp[2];
uint16_t resp_len = 2;
+ uint16_t sw1sw2 = 0;
uint32_t offset = 0;
buf[offset++] = strlen(pin);
@@ -1379,14 +1843,18 @@ static secbool se_set_pin_passphrase_ex(uint8_t addr, uint8_t *session_key,
memcpy(buf + offset, (uint8_t *)passphrase, strlen(passphrase));
offset += strlen(passphrase);
- if (!se_transmit_mac_ex(addr, session_key, SE_INS_PIN, 0x00, 0x09, buf,
- offset, resp, &resp_len)) {
- if (thd89_last_error() == 0x6c00) {
- percent = 0;
- resp[0] = PIN_SUCCESS;
- } else {
- return secfalse;
- }
+ *se_get_pending_operation(addr) = SE_LONG_OPERATION_NONE;
+ se_secure_response_result_t result =
+ se_transmit_mac_result_ex(addr, session_key, SE_INS_PIN, 0x00, 0x09, buf,
+ offset, resp, &resp_len, &sw1sw2);
+ if (result == SE_SECURE_RESPONSE_NO_MAC_6C && (sw1sw2 & 0xff) <= 100) {
+ *se_get_pending_operation(addr) = SE_LONG_OPERATION_SET_PASSPHRASE_PIN;
+ percent = (sw1sw2 & 0xff) == 100 ? 99 : (sw1sw2 & 0xff);
+ resp[0] = PIN_SUCCESS;
+ } else if (result == SE_SECURE_RESPONSE_OK) {
+ percent = 100;
+ } else {
+ return secfalse;
}
if (resp[0] != PIN_SUCCESS) {
pin_passphrase_ret = resp[0];
@@ -1397,16 +1865,18 @@ static secbool se_set_pin_passphrase_ex(uint8_t addr, uint8_t *session_key,
if (ui_callback) {
ui_callback(0, percent * 10, NULL);
}
- if (!session_generate_seed_percent(&percent)) {
+ if (!se_query_progress_percent_ex(addr, &percent)) {
return secfalse;
}
hal_delay(100);
}
resp_len = 1;
- *override = true;
- if (se_transmit_mac(SE_INS_PIN, 0x00, 0x0D, NULL, 0, resp, &resp_len)) {
- *override = resp[0] ? true : false;
+ if (se_transmit_mac_ex(addr, session_key, SE_INS_PIN, 0x00, 0x0D, NULL, 0,
+ resp, &resp_len) != sectrue ||
+ resp_len != 1u) {
+ return secfalse;
}
+ *override = resp[0] ? true : false;
return sectrue;
}
@@ -1570,12 +2040,9 @@ secbool se_getSecsta(void) {
}
secbool se_set_u2f_counter(uint32_t u2fcounter) {
- uint8_t cmd[9] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_SET_COUNTER, 0x04};
- uint16_t recv_len = 0;
-
- memcpy(cmd + 5, &u2fcounter, 4);
-
- if (!thd89_transmit(cmd, sizeof(cmd), NULL, &recv_len)) {
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_SET_COUNTER,
+ (uint8_t *)&u2fcounter, sizeof(u2fcounter), NULL,
+ NULL)) {
return secfalse;
}
@@ -1583,9 +2050,9 @@ secbool se_set_u2f_counter(uint32_t u2fcounter) {
}
secbool se_get_u2f_counter(uint32_t *u2fcounter) {
- uint8_t cmd[5] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_NEXT_COUNTER, 0x00};
uint16_t recv_len = 4;
- if (!thd89_transmit(cmd, sizeof(cmd), (uint8_t *)u2fcounter, &recv_len)) {
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_NEXT_COUNTER, NULL, 0,
+ (uint8_t *)u2fcounter, &recv_len)) {
return secfalse;
}
return sectrue;
@@ -1721,33 +2188,61 @@ secbool se_containsMnemonic(const char *mnemonic) {
return sectrue * (verify == 0x55);
}
-secbool se_exportMnemonic(char *mnemonic, uint16_t dest_size) {
- uint16_t len = dest_size;
+secbool se_exportMnemonic(const uint8_t *pin, uint16_t pin_len, char *mnemonic,
+ uint16_t dest_size) {
+ uint8_t request[1 + PIN_MAX_LEN] = {0};
+ uint16_t response_len = dest_size > 0 ? dest_size - 1U : 0;
- if (!se_transmit_mac(0xE2, 0x00, 0x02, NULL, 0, (uint8_t *)mnemonic, &len)) {
+ if (mnemonic == NULL || dest_size == 0 || pin == NULL ||
+ pin_len < MNEMONIC_EXPORT_PIN_MIN_LEN || pin_len > PIN_MAX_LEN) {
return secfalse;
}
- mnemonic[len] = 0;
+ request[0] = (uint8_t)pin_len;
+ if (pin_len != 0) {
+ memcpy(&request[1], pin, pin_len);
+ }
+ if (!se_transmit_mac(0xE2, 0x00, 0x02, request, pin_len + 1U,
+ (uint8_t *)mnemonic, &response_len) ||
+ response_len >= dest_size) {
+ memzero(request, sizeof(request));
+ memzero(mnemonic, dest_size);
+ return secfalse;
+ }
+ mnemonic[response_len] = '\0';
+ memzero(request, sizeof(request));
return sectrue;
}
-secbool se_set_needs_backup(bool needs_backup) {
- if (!se_transmit_mac(0xE2, 0x00, 0x03, (uint8_t *)&needs_backup, 1, NULL,
- NULL)) {
+secbool se_set_mnemonic_export_enabled(bool enabled, const uint8_t *pin,
+ uint16_t pin_len) {
+ uint8_t request[2 + PIN_MAX_LEN] = {enabled ? 0x55 : 0x00,
+ (uint8_t)pin_len};
+
+ if (pin == NULL || pin_len < MNEMONIC_EXPORT_PIN_MIN_LEN ||
+ pin_len > PIN_MAX_LEN) {
return secfalse;
}
-
+ if (pin_len != 0) {
+ memcpy(&request[2], pin, pin_len);
+ }
+ if (!se_transmit_mac(0xE2, 0x00, 0x03, request, pin_len + 2U, NULL, NULL)) {
+ memzero(request, sizeof(request));
+ return secfalse;
+ }
+ memzero(request, sizeof(request));
return sectrue;
}
-secbool se_get_needs_backup(bool *needs_backup) {
- uint16_t len = 1;
- uint8_t needs_backup_buf = 0xff;
- if (!se_transmit_mac(0xE2, 0x00, 0x04, NULL, 0, &needs_backup_buf, &len)) {
+secbool se_get_mnemonic_export_enabled(bool *enabled) {
+ uint8_t response = 0;
+ uint16_t response_len = sizeof(response);
+
+ if (enabled == NULL ||
+ !se_transmit_mac(0xE2, 0x00, 0x04, NULL, 0, &response, &response_len) ||
+ response_len != 1 || (response != 0x00 && response != 0x55)) {
return secfalse;
}
- *needs_backup = needs_backup_buf == 0 ? false : true;
-
+ *enabled = response == 0x55;
return sectrue;
}
@@ -1937,12 +2432,18 @@ secbool se_session_is_open() {
}
secbool session_generate_master_seed(const char *passphrase, uint8_t *percent) {
- if (!se_transmit_mac(SE_INS_SESSION, 0x00, 0x05, (uint8_t *)passphrase,
- strlen(passphrase), NULL, NULL)) {
- if (thd89_last_error() == 0x6c00) {
- *percent = 0;
- return sectrue;
- }
+ uint16_t sw1sw2 = 0;
+ se_pending_operation = SE_LONG_OPERATION_NONE;
+ se_secure_response_result_t result = se_transmit_mac_result_ex(
+ THD89_MASTER_ADDRESS, se_session_key, SE_INS_SESSION, 0x00, 0x05,
+ (uint8_t *)passphrase, strlen(passphrase), NULL, NULL, &sw1sw2);
+
+ if (result == SE_SECURE_RESPONSE_NO_MAC_6C && (sw1sw2 & 0xff) <= 100) {
+ se_pending_operation = SE_LONG_OPERATION_SESSION_SEED;
+ *percent = (sw1sw2 & 0xff) == 100 ? 99 : (sw1sw2 & 0xff);
+ return sectrue;
+ }
+ if (result != SE_SECURE_RESPONSE_OK) {
return secfalse;
}
*percent = 100;
@@ -1951,34 +2452,60 @@ secbool session_generate_master_seed(const char *passphrase, uint8_t *percent) {
secbool session_generate_cardano_seed(const char *passphrase,
uint8_t *percent) {
- if (!se_transmit_mac(SE_INS_SESSION, 0x00, 0x06, (uint8_t *)passphrase,
- strlen(passphrase), NULL, NULL)) {
- if (thd89_last_error() == 0x6c00) {
- *percent = 0;
- return sectrue;
- }
+ uint16_t sw1sw2 = 0;
+ se_pending_operation = SE_LONG_OPERATION_NONE;
+ se_secure_response_result_t result = se_transmit_mac_result_ex(
+ THD89_MASTER_ADDRESS, se_session_key, SE_INS_SESSION, 0x00, 0x06,
+ (uint8_t *)passphrase, strlen(passphrase), NULL, NULL, &sw1sw2);
+
+ if (result == SE_SECURE_RESPONSE_NO_MAC_6C && (sw1sw2 & 0xff) <= 100) {
+ se_pending_operation = SE_LONG_OPERATION_CARDANO_SEED;
+ *percent = (sw1sw2 & 0xff) == 100 ? 99 : (sw1sw2 & 0xff);
+ return sectrue;
+ }
+ if (result != SE_SECURE_RESPONSE_OK) {
return secfalse;
}
*percent = 100;
return sectrue;
}
-secbool session_generate_seed_percent(uint8_t *percent) {
- uint8_t cmd[5] = {0x80, SE_INS_SESSION, 0x00, 0x08, 0x00};
- uint16_t recv_len;
- uint16_t sw1sw2;
+static secbool se_query_progress_percent_ex(uint8_t addr, uint8_t *percent) {
+ uint8_t cmd[5] = {0x80, SE_INS_GET_STATE, 0x00, 0x08, 0x00};
+ uint16_t recv_len = 0;
+ uint16_t sw1sw2 = 0;
+ se_long_operation_t *pending_operation = se_get_pending_operation(addr);
- if (!thd89_transmit(cmd, sizeof(cmd), percent, &recv_len)) {
- sw1sw2 = thd89_last_error();
- if ((sw1sw2 & 0xff00) == 0x6c00) {
- *percent = sw1sw2 & 0xff;
- *percent = *percent == 100 ? 99 : *percent;
- return sectrue;
- }
+ if (percent == NULL || *pending_operation == SE_LONG_OPERATION_NONE) {
return secfalse;
}
- *percent = 100;
- return sectrue;
+
+ if (thd89_transmit_raw_ex(addr, cmd, sizeof(cmd), NULL, &recv_len, &sw1sw2) !=
+ sectrue) {
+ *pending_operation = SE_LONG_OPERATION_NONE;
+ return secfalse;
+ }
+ se_handle_status(sw1sw2);
+ if (recv_len != 0) {
+ *pending_operation = SE_LONG_OPERATION_NONE;
+ return secfalse;
+ }
+ if (sw1sw2 == 0x9000) {
+ *percent = 100;
+ *pending_operation = SE_LONG_OPERATION_NONE;
+ return sectrue;
+ }
+ if ((sw1sw2 & 0xff00) == 0x6c00 && (sw1sw2 & 0xff) <= 100) {
+ *percent = (sw1sw2 & 0xff) == 100 ? 99 : (sw1sw2 & 0xff);
+ return sectrue;
+ }
+
+ *pending_operation = SE_LONG_OPERATION_NONE;
+ return secfalse;
+}
+
+secbool se_query_progress_percent(uint8_t *percent) {
+ return se_query_progress_percent_ex(THD89_MASTER_ADDRESS, percent);
}
uint8_t *se_session_startSession(const uint8_t *received_session_id) {
@@ -2056,7 +2583,7 @@ secbool se_gen_session_seed(const char *passphrase, bool cardano) {
if (ui_callback) {
ui_callback(0, percent * 10, NULL);
}
- if (!session_generate_seed_percent(&percent)) {
+ if (!se_query_progress_percent(&percent)) {
return secfalse;
}
hal_delay(100);
@@ -2078,7 +2605,7 @@ secbool se_gen_session_seed(const char *passphrase, bool cardano) {
if (ui_callback) {
ui_callback(0, percent * 10, NULL);
}
- if (!session_generate_seed_percent(&percent)) {
+ if (!se_query_progress_percent(&percent)) {
return secfalse;
}
hal_delay(100);
@@ -2271,22 +2798,54 @@ int se_nem_aes256_decrypt(const uint8_t *ed25519_pubkey, const uint8_t *iv,
return 0;
}
-int se_slip21_node(uint8_t *data) {
- uint16_t resp_len = 64;
+secbool se_slip21_ownership_id(const uint8_t *script_pubkey,
+ uint16_t script_pubkey_len, uint8_t out[32]) {
+ uint16_t resp_len = 32;
- if (!se_transmit_mac(0xEB, 0x00, 0x00, NULL, 0, data, &resp_len)) {
- return -1;
+ if (script_pubkey == NULL || script_pubkey_len == 0 ||
+ script_pubkey_len > SE_DATA_MAX_LEN || out == NULL) {
+ return secfalse;
}
- return 0;
+ if (!se_transmit_mac(0xEB, 0x01, 0x00, (uint8_t *)script_pubkey,
+ script_pubkey_len, out, &resp_len) ||
+ resp_len != 32) {
+ return secfalse;
+ }
+ return sectrue;
}
-// seed without passphrase
-int se_slip21_fido_node(uint8_t *data) {
- uint16_t resp_len = 64;
- if (!se_transmit_mac(0xEB, 0x00, 0x01, NULL, 0, data, &resp_len)) {
- return -1;
+secbool se_slip21_address_mac(uint32_t slip44, const uint8_t *address,
+ uint16_t address_len, uint8_t out[32]) {
+ uint16_t resp_len = 32;
+
+ if (address == NULL || address_len == 0 ||
+ address_len > SE_DATA_MAX_LEN - 6U || out == NULL) {
+ return secfalse;
}
- return 0;
+ APDU_DATA[0] = (uint8_t)slip44;
+ APDU_DATA[1] = (uint8_t)(slip44 >> 8);
+ APDU_DATA[2] = (uint8_t)(slip44 >> 16);
+ APDU_DATA[3] = (uint8_t)(slip44 >> 24);
+ APDU_DATA[4] = (uint8_t)(address_len >> 8);
+ APDU_DATA[5] = (uint8_t)address_len;
+ memcpy(APDU_DATA + 6, address, address_len);
+ if (!se_transmit_mac(0xEB, 0x01, 0x01, APDU_DATA, address_len + 6U, out,
+ &resp_len) ||
+ resp_len != 32) {
+ return secfalse;
+ }
+ return sectrue;
+}
+
+secbool se_slip21_slip25_mac(uint8_t out[32]) {
+ uint16_t resp_len = 32;
+
+ if (out == NULL ||
+ !se_transmit_mac(0xEB, 0x01, 0x02, NULL, 0, out, &resp_len) ||
+ resp_len != 32) {
+ return secfalse;
+ }
+ return sectrue;
}
secbool se_authorization_set(const uint32_t authorization_type,
@@ -2502,36 +3061,49 @@ secbool se_fp_read(uint32_t offset, void *val_dest, uint32_t len, uint8_t index,
}
secbool se_gen_fido_seed(uint8_t *percent) {
- uint8_t cmd[5] = {0x00, 0xf9, 0x00, 0x00, 0x00};
- uint16_t recv_len = 0;
- uint16_t sw1sw2;
+ if (percent == NULL) {
+ return secfalse;
+ }
- if (!thd89_transmit(cmd, sizeof(cmd), NULL, &recv_len)) {
- sw1sw2 = thd89_last_error();
- if ((sw1sw2 & 0xff00) == 0x6c00) {
- *percent = sw1sw2 & 0xff;
- if (ui_callback) {
- ui_callback(0, *percent * 10, NULL);
- }
+ if (se_pending_operation != SE_LONG_OPERATION_FIDO_SEED) {
+ if (se_pending_operation != SE_LONG_OPERATION_NONE) {
+ return secfalse;
+ }
+ uint16_t sw1sw2 = 0;
+ se_secure_response_result_t result = se_transmit_mac_result_ex(
+ THD89_MASTER_ADDRESS, se_session_key, SE_INS_FIDO, 0x00,
+ SE_FIDO_GEN_SEED, NULL, 0, NULL, NULL, &sw1sw2);
+ if (result == SE_SECURE_RESPONSE_OK) {
+ *percent = 100;
return sectrue;
}
+ if (result != SE_SECURE_RESPONSE_NO_MAC_6C || (sw1sw2 & 0xff) > 100) {
+ return secfalse;
+ }
+ se_pending_operation = SE_LONG_OPERATION_FIDO_SEED;
+ *percent = (sw1sw2 & 0xff) == 100 ? 99 : (sw1sw2 & 0xff);
+ }
+
+ if (!se_query_progress_percent(percent)) {
return secfalse;
}
- *percent = 100;
+ if (ui_callback) {
+ ui_callback(0, *percent * 10, NULL);
+ }
return sectrue;
}
secbool se_u2f_register(const uint8_t app_id[32], const uint8_t challenge[32],
uint8_t key_handle[64], uint8_t pub_key[65],
uint8_t sign[64]) {
- uint8_t cmd[128] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_U2F_REGISTER};
+ uint8_t data[64];
uint8_t recv[256];
uint16_t recv_len = sizeof(recv);
- memcpy(cmd + 5, app_id, 32);
- memcpy(cmd + 5 + 32, challenge, 32);
+ memcpy(data, app_id, 32);
+ memcpy(data + 32, challenge, 32);
- cmd[4] = 64;
- if (!thd89_transmit(cmd, 5 + 64, (uint8_t *)recv, &recv_len)) {
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_U2F_REGISTER, data,
+ sizeof(data), (uint8_t *)recv, &recv_len)) {
return secfalse;
}
@@ -2547,13 +3119,11 @@ secbool se_u2f_register(const uint8_t app_id[32], const uint8_t challenge[32],
secbool se_u2f_gen_handle_and_node(const uint8_t app_id[32],
uint8_t key_handle[64], HDNode *out) {
- uint8_t cmd[128] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_U2F_GEN_HANDLE};
uint8_t recv[256];
uint16_t recv_len = sizeof(recv);
- memcpy(cmd + 5, app_id, 32);
- cmd[4] = 32;
- if (!thd89_transmit(cmd, 5 + 32, (uint8_t *)recv, &recv_len)) {
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_U2F_GEN_HANDLE,
+ (uint8_t *)app_id, 32, (uint8_t *)recv, &recv_len)) {
return secfalse;
}
@@ -2569,13 +3139,13 @@ secbool se_u2f_gen_handle_and_node(const uint8_t app_id[32],
secbool se_u2f_validate_handle(const uint8_t app_id[32],
const uint8_t key_handle[64]) {
- uint8_t cmd[128] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_U2F_VALIDATE_HANDLE};
+ uint8_t data[96];
- memcpy(cmd + 5, app_id, 32);
- memcpy(cmd + 5 + 32, key_handle, 64);
+ memcpy(data, app_id, 32);
+ memcpy(data + 32, key_handle, 64);
- cmd[4] = 32 + 64;
- if (!thd89_transmit(cmd, 5 + 96, NULL, NULL)) {
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_U2F_VALIDATE_HANDLE, data,
+ sizeof(data), NULL, NULL)) {
return secfalse;
}
return sectrue;
@@ -2585,15 +3155,15 @@ secbool se_u2f_authenticate(const uint8_t app_id[32],
const uint8_t key_handle[64],
const uint8_t challenge[32], uint8_t *u2f_counter,
uint8_t sign[64]) {
- uint8_t cmd[256] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_U2F_AUTHENTICATE};
+ uint8_t data[128];
uint8_t recv[128];
uint16_t recv_len = sizeof(recv);
- memcpy(cmd + 5, app_id, 32);
- memcpy(cmd + 5 + 32, key_handle, 64);
- memcpy(cmd + 5 + 32 + 64, challenge, 32);
+ memcpy(data, app_id, 32);
+ memcpy(data + 32, key_handle, 64);
+ memcpy(data + 32 + 64, challenge, 32);
- cmd[4] = 128;
- if (!thd89_transmit(cmd, 5 + 128, (uint8_t *)recv, &recv_len)) {
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_U2F_AUTHENTICATE, data,
+ sizeof(data), (uint8_t *)recv, &recv_len)) {
return secfalse;
}
@@ -2609,21 +3179,19 @@ secbool se_u2f_authenticate(const uint8_t app_id[32],
secbool se_derive_fido_keys(HDNode *out, const char *curve,
const uint32_t *address_n, size_t address_n_count,
uint32_t *fingerprint) {
- uint8_t cmd[128] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_DERIVE_NODE};
uint8_t resp[256];
uint16_t resp_len = sizeof(resp);
uint8_t len = strlen(curve);
- cmd[5] = len;
- memcpy(cmd + 6, curve, len);
+ APDU_DATA[0] = len;
+ memcpy(APDU_DATA + 1, curve, len);
len += 1;
- memcpy(cmd + 5 + len, (uint8_t *)address_n, address_n_count * 4);
+ memcpy(APDU_DATA + len, (uint8_t *)address_n, address_n_count * 4);
len += address_n_count * 4;
- cmd[4] = len;
-
- if (!thd89_transmit(cmd, 5 + len, (uint8_t *)resp, &resp_len)) {
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_DERIVE_NODE, APDU_DATA, len,
+ (uint8_t *)resp, &resp_len)) {
return secfalse;
}
out->curve = get_curve_by_name(curve);
@@ -2636,13 +3204,11 @@ secbool se_derive_fido_keys(HDNode *out, const char *curve,
}
secbool se_fido_hdnode_sign_digest(const uint8_t *hash, uint8_t *sig) {
- uint8_t cmd[37] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_NODE_SIGN, 0x20};
uint8_t resp[64];
uint16_t resp_len = sizeof(resp);
- memcpy(cmd + 5, hash, 32);
-
- if (!thd89_transmit(cmd, 37, (uint8_t *)resp, &resp_len)) {
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_NODE_SIGN, (uint8_t *)hash,
+ 32, (uint8_t *)resp, &resp_len)) {
return secfalse;
}
memcpy(sig, resp, resp_len);
@@ -2650,93 +3216,334 @@ secbool se_fido_hdnode_sign_digest(const uint8_t *hash, uint8_t *sig) {
}
secbool se_fido_att_sign_digest(const uint8_t *hash, uint8_t *sig) {
- uint8_t cmd[37] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_ATT_SIGN, 0x20};
uint8_t resp[64];
uint16_t resp_len = sizeof(resp);
- memcpy(cmd + 5, hash, 32);
-
- if (!thd89_transmit(cmd, 37, (uint8_t *)resp, &resp_len)) {
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_ATT_SIGN, (uint8_t *)hash, 32,
+ (uint8_t *)resp, &resp_len)) {
return secfalse;
}
memcpy(sig, resp, resp_len);
return sectrue;
}
-secbool se_get_fido2_data(uint16_t offset, uint8_t *dest, uint16_t len) {
- uint8_t cmd[4] = {0};
- uint16_t recv_len = len;
- cmd[0] = (offset >> 8) & 0xFF;
- cmd[1] = offset & 0xFF;
- cmd[2] = (len >> 8) & 0xFF;
- cmd[3] = len & 0xFF;
- if (!se_transmit_mac(SE_INS_READ_DATA, 0x00, 0x03, cmd, sizeof(cmd), dest,
- &recv_len)) {
+secbool se_fido_hmac_secret(const uint8_t *credential_id,
+ uint16_t credential_id_len, const uint8_t *salt,
+ uint16_t salt_len, uint8_t *out) {
+ uint16_t resp_len = salt_len;
+
+ if (credential_id == NULL ||
+ credential_id_len < SE_FIDO_CREDENTIAL_ID_MIN_LEN ||
+ credential_id_len > SE_FIDO_CREDENTIAL_ID_MAX_LEN || salt == NULL ||
+ (salt_len != 32 && salt_len != 64) || out == NULL) {
+ return secfalse;
+ }
+ APDU_DATA[0] = (uint8_t)(credential_id_len >> 8);
+ APDU_DATA[1] = (uint8_t)credential_id_len;
+ memcpy(APDU_DATA + 2, credential_id, credential_id_len);
+ APDU_DATA[2 + credential_id_len] = (uint8_t)salt_len;
+ memcpy(APDU_DATA + 3 + credential_id_len, salt, salt_len);
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_SLIP21_HMAC_SECRET, APDU_DATA,
+ credential_id_len + salt_len + 3U, out, &resp_len) ||
+ resp_len != salt_len) {
return secfalse;
}
return sectrue;
}
-secbool se_set_fido2_data(uint16_t offset, const uint8_t *src, uint16_t len) {
- uint8_t cmd[4] = {0};
- cmd[0] = (offset >> 8) & 0xFF;
- cmd[1] = offset & 0xFF;
- cmd[2] = (len >> 8) & 0xFF;
- cmd[3] = len & 0xFF;
- memcpy(APDU_DATA, cmd, 4);
- memcpy(APDU_DATA + 4, src, len);
- if (!se_transmit_mac(SE_INS_WRITE_DATA, 0x00, 0x03, APDU_DATA, 4 + len, NULL,
- NULL)) {
- return secfalse;
+secbool se_fido_credential_create(const uint8_t *plaintext,
+ uint16_t plaintext_len, uint8_t resident,
+ uint8_t *response, uint16_t *response_len) {
+ uint16_t response_capacity = 0;
+ uint16_t response_received = 0;
+ uint16_t credential_id_len = 0;
+ uint16_t expected_len = 0;
+
+ if (response_len != NULL) {
+ response_capacity = *response_len;
+ *response_len = 0;
+ }
+ if (plaintext == NULL || plaintext_len == 0 ||
+ plaintext_len > SE_FIDO_CREDENTIAL_PLAINTEXT_MAX_LEN || resident > 1 ||
+ response == NULL || response_len == NULL ||
+ (resident != 0 &&
+ plaintext_len > SE_FIDO_RESIDENT_CREDENTIAL_PLAINTEXT_MAX_LEN)) {
+ goto cleanup;
+ }
+
+ credential_id_len = plaintext_len + 32U;
+ expected_len = credential_id_len + 4U;
+ if (response_capacity < expected_len) {
+ goto cleanup;
+ }
+
+ APDU_DATA[0] = resident;
+ memcpy(APDU_DATA + 1, plaintext, plaintext_len);
+ response_received = response_capacity;
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_CREATE_CREDENTIAL,
+ APDU_DATA, plaintext_len + 1U, response,
+ &response_received) ||
+ response_received != expected_len ||
+ (((uint16_t)response[0] << 8) | response[1]) != credential_id_len) {
+ goto cleanup;
+ }
+
+ if ((resident == 0 &&
+ (response[expected_len - 2] != 0xff || response[expected_len - 1] != 0)) ||
+ (resident != 0 &&
+ (response[expected_len - 2] >= FIDO2_RESIDENT_CREDENTIALS_COUNT ||
+ (response[expected_len - 1] != 1 && response[expected_len - 1] != 2)))) {
+ goto cleanup;
}
+
+ *response_len = response_received;
return sectrue;
+
+cleanup:
+ if (response != NULL) {
+ memzero(response, response_capacity);
+ }
+ return secfalse;
}
-secbool se_get_fido2_resident_credentials(uint32_t index, uint8_t *dest,
- uint16_t *dst_len) {
- if (index >= FIDO2_RESIDENT_CREDENTIALS_COUNT) return secfalse;
- uint8_t buffer[FIDO2_RESIDENT_CREDENTIALS_SIZE];
- CTAP_credential_id_storage *cred_id = (CTAP_credential_id_storage *)buffer;
- if (!se_get_fido2_data(index * FIDO2_RESIDENT_CREDENTIALS_SIZE, buffer, 6)) {
- return secfalse;
+secbool se_fido_credential_validate(const uint8_t rp_id_hash[32],
+ const uint8_t *credential_id,
+ uint16_t credential_id_len,
+ uint8_t *plaintext,
+ uint16_t *plaintext_len) {
+ uint16_t plaintext_capacity = 0;
+ uint16_t plaintext_received = 0;
+ uint16_t expected_len = 0;
+ uint16_t request_len = 0;
+
+ if (plaintext_len != NULL) {
+ plaintext_capacity = *plaintext_len;
+ *plaintext_len = 0;
}
- if (memcmp(cred_id->credential_id_flag, FIDO2_RESIDENT_CREDENTIALS_FLAGS,
- 4) != 0) {
- return secfalse;
+ if (credential_id == NULL ||
+ credential_id_len < SE_FIDO_CREDENTIAL_ID_MIN_LEN ||
+ credential_id_len > SE_FIDO_CREDENTIAL_ID_MAX_LEN || plaintext == NULL ||
+ plaintext_len == NULL) {
+ goto cleanup;
+ }
+
+ expected_len = credential_id_len - 32U;
+ if (plaintext_capacity < expected_len) {
+ goto cleanup;
+ }
+
+ APDU_DATA[0] = rp_id_hash != NULL ? 1 : 0;
+ request_len = 1;
+ if (rp_id_hash != NULL) {
+ memcpy(APDU_DATA + request_len, rp_id_hash, 32);
+ request_len += 32;
+ }
+ memcpy(APDU_DATA + request_len, credential_id, credential_id_len);
+ request_len += credential_id_len;
+ plaintext_received = plaintext_capacity;
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_VALIDATE_CREDENTIAL,
+ APDU_DATA, request_len, plaintext, &plaintext_received) ||
+ plaintext_received != expected_len) {
+ goto cleanup;
+ }
+
+ *plaintext_len = plaintext_received;
+ return sectrue;
+
+cleanup:
+ if (plaintext != NULL) {
+ memzero(plaintext, plaintext_capacity);
+ }
+ return secfalse;
+}
+
+secbool se_fido_resident_credentials_list(uint8_t *indexes, uint16_t *count) {
+ uint8_t response[FIDO2_RESIDENT_CREDENTIALS_COUNT + 1] = {0};
+ uint16_t count_capacity = 0;
+ uint16_t response_len = sizeof(response);
+ uint8_t response_count = 0;
+ uint8_t previous = 0;
+
+ if (count != NULL) {
+ count_capacity = *count;
+ *count = 0;
+ }
+ if (indexes == NULL || count == NULL ||
+ !se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_LIST_RESIDENT_CREDENTIALS,
+ NULL, 0, response, &response_len) || response_len == 0) {
+ goto cleanup;
+ }
+
+ response_count = response[0];
+ if (response_count > FIDO2_RESIDENT_CREDENTIALS_COUNT ||
+ response_len != (uint16_t)response_count + 1U ||
+ count_capacity < response_count) {
+ goto cleanup;
+ }
+ for (uint8_t i = 0; i < response_count; i++) {
+ if (response[i + 1] >= FIDO2_RESIDENT_CREDENTIALS_COUNT ||
+ (i != 0 && response[i + 1] <= previous)) {
+ goto cleanup;
+ }
+ previous = response[i + 1];
}
- if (*dst_len < cred_id->credential_length) {
+
+ memcpy(indexes, response + 1, response_count);
+ *count = response_count;
+ memzero(response, sizeof(response));
+ return sectrue;
+
+cleanup:
+ memzero(response, sizeof(response));
+ if (indexes != NULL) {
+ memzero(indexes, count_capacity);
+ }
+ return secfalse;
+}
+
+secbool se_fido_resident_credential_read(uint8_t index, uint8_t *packed,
+ uint16_t *packed_len) {
+ uint16_t packed_capacity = 0;
+ uint16_t packed_received = 0;
+ uint16_t credential_id_len = 0;
+ uint16_t plaintext_len = 0;
+ uint16_t expected_len = 0;
+
+ if (packed_len != NULL) {
+ packed_capacity = *packed_len;
+ *packed_len = 0;
+ }
+ if (index >= FIDO2_RESIDENT_CREDENTIALS_COUNT || packed == NULL ||
+ packed_len == NULL) {
+ goto cleanup;
+ }
+
+ packed_received = packed_capacity;
+ if (!se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_READ_RESIDENT_CREDENTIAL,
+ &index, 1, packed, &packed_received) ||
+ packed_received < 5U) {
+ goto cleanup;
+ }
+ credential_id_len = ((uint16_t)packed[0] << 8) | packed[1];
+ if (credential_id_len < SE_FIDO_CREDENTIAL_ID_MIN_LEN ||
+ credential_id_len > SE_FIDO_RESIDENT_CREDENTIAL_ID_MAX_LEN ||
+ packed_received < credential_id_len + 4U) {
+ goto cleanup;
+ }
+ plaintext_len = ((uint16_t)packed[credential_id_len + 2] << 8) |
+ packed[credential_id_len + 3];
+ expected_len = credential_id_len + plaintext_len + 4U;
+ if (plaintext_len == 0 ||
+ plaintext_len > SE_FIDO_RESIDENT_CREDENTIAL_PLAINTEXT_MAX_LEN ||
+ plaintext_len != credential_id_len - 32U || packed_received != expected_len ||
+ packed_received > SE_FIDO_RESIDENT_CREDENTIAL_READ_MAX_LEN) {
+ goto cleanup;
+ }
+
+ *packed_len = packed_received;
+ return sectrue;
+
+cleanup:
+ if (packed != NULL) {
+ memzero(packed, packed_capacity);
+ }
+ return secfalse;
+}
+
+secbool se_fido_resident_credential_import(const uint8_t *credential_id,
+ uint16_t credential_id_len,
+ uint8_t *slot, uint8_t *action) {
+ uint8_t response[2] = {0};
+ uint16_t response_len = sizeof(response);
+
+ if (slot != NULL) {
+ *slot = 0;
+ }
+ if (action != NULL) {
+ *action = 0;
+ }
+ if (credential_id == NULL ||
+ credential_id_len < SE_FIDO_CREDENTIAL_ID_MIN_LEN ||
+ credential_id_len > SE_FIDO_RESIDENT_CREDENTIAL_ID_MAX_LEN || slot == NULL ||
+ action == NULL ||
+ !se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_IMPORT_RESIDENT_CREDENTIAL,
+ (uint8_t *)credential_id, credential_id_len, response,
+ &response_len) ||
+ response_len != sizeof(response) ||
+ response[0] >= FIDO2_RESIDENT_CREDENTIALS_COUNT ||
+ (response[1] != 1 && response[1] != 2)) {
+ goto cleanup;
+ }
+
+ *slot = response[0];
+ *action = response[1];
+ memzero(response, sizeof(response));
+ return sectrue;
+
+cleanup:
+ memzero(response, sizeof(response));
+ return secfalse;
+}
+
+secbool se_fido_resident_credential_delete(uint8_t index) {
+ uint16_t response_len = 0;
+
+ if (index >= FIDO2_RESIDENT_CREDENTIALS_COUNT) {
return secfalse;
}
- if (!se_get_fido2_data(index * FIDO2_RESIDENT_CREDENTIALS_SIZE + 6,
- buffer + 6, cred_id->credential_length)) {
+ if (se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_DELETE_RESIDENT_CREDENTIAL,
+ &index, 1, NULL, &response_len) != sectrue ||
+ response_len != 0) {
return secfalse;
}
- *dst_len = cred_id->credential_length;
- memcpy(dest, cred_id->rp_id_hash, *dst_len);
return sectrue;
}
-secbool se_set_fido2_resident_credentials(uint32_t index, const uint8_t *src,
- uint16_t len) {
- if (index >= FIDO2_RESIDENT_CREDENTIALS_COUNT) return secfalse;
- if (len > (FIDO2_RESIDENT_CREDENTIALS_SIZE - 6)) return secfalse;
- CTAP_credential_id_storage cred_id = {0};
- memcpy(cred_id.credential_id_flag, FIDO2_RESIDENT_CREDENTIALS_FLAGS, 4);
- cred_id.credential_length = len;
- memcpy(cred_id.rp_id_hash, src, len);
- return se_set_fido2_data(index * FIDO2_RESIDENT_CREDENTIALS_SIZE,
- (uint8_t *)&cred_id, 6 + len);
-}
+secbool se_fido_resident_credentials_clear(void) {
+ uint16_t response_len = 0;
-secbool se_delete_fido2_resident_credentials(uint32_t index) {
- uint8_t buffer[FIDO2_RESIDENT_CREDENTIALS_HEADER_LEN] = {0xff};
- return se_set_fido2_data(index * FIDO2_RESIDENT_CREDENTIALS_SIZE, buffer,
- FIDO2_RESIDENT_CREDENTIALS_HEADER_LEN);
+ if (se_transmit_mac(SE_INS_FIDO, 0x00, SE_FIDO_CLEAR_RESIDENT_CREDENTIALS,
+ NULL, 0, NULL, &response_len) != sectrue ||
+ response_len != 0) {
+ return secfalse;
+ }
+ return sectrue;
}
-secbool se_delete_all_fido2_credentials(void) {
- if (!se_transmit_mac(SE_INS_WRITE_DATA, 0x00, 0x04, NULL, 0, NULL, NULL)) {
+secbool se_get_component_version(uint8_t slot, uint32_t *version) {
+ uint8_t resp[4] = {0};
+ uint16_t resp_len = sizeof(resp);
+
+ if (slot >= SE_COMPONENT_VERSION_SLOT_COUNT || version == NULL) {
+ return secfalse;
+ }
+
+ if (!se_transmit_mac(SE_INS_COMPONENT_VERSION, 0x00, 0x00, &slot, 1, resp,
+ &resp_len)) {
return secfalse;
}
+ if (resp_len != sizeof(resp)) {
+ return secfalse;
+ }
+
+ *version = (uint32_t)resp[0] | ((uint32_t)resp[1] << 8) |
+ ((uint32_t)resp[2] << 16) | ((uint32_t)resp[3] << 24);
return sectrue;
}
+
+secbool se_set_component_version(uint8_t slot, uint32_t version) {
+ uint8_t data[5] = {0};
+
+ if (slot >= SE_COMPONENT_VERSION_SLOT_COUNT) {
+ return secfalse;
+ }
+
+ data[0] = slot;
+ data[1] = version & 0xFF;
+ data[2] = (version >> 8) & 0xFF;
+ data[3] = (version >> 16) & 0xFF;
+ data[4] = (version >> 24) & 0xFF;
+
+ return se_transmit_mac(SE_INS_COMPONENT_VERSION, 0x00, 0x01, data,
+ sizeof(data), NULL, NULL);
+}
diff --git a/core/embed/trezorhal/se_thd89.h b/core/embed/trezorhal/se_thd89.h
index 3c53c96694..90ee1781c5 100644
--- a/core/embed/trezorhal/se_thd89.h
+++ b/core/embed/trezorhal/se_thd89.h
@@ -44,23 +44,12 @@ typedef enum {
PIN_TYPE_MAX
} pin_type_t;
-#define FIDO2_RESIDENT_CREDENTIALS_SIZE (512)
#define FIDO2_RESIDENT_CREDENTIALS_COUNT (60)
-#define FIDO2_RESIDENT_CREDENTIALS_FLAGS "\x66\x69\x64\x6F" // "fido"
-#define FIDO2_RESIDENT_CREDENTIALS_HEADER_LEN (6)
-typedef struct {
- uint8_t credential_id_flag[4];
- uint16_t credential_length;
- uint8_t rp_id_hash[32];
- uint8_t credential_id[474];
-} __attribute__((packed)) CTAP_credential_id_storage;
-_Static_assert(sizeof(CTAP_credential_id_storage) ==
- FIDO2_RESIDENT_CREDENTIALS_SIZE,
- "CTAP_credential_id_storage size must be flash page size");
typedef secbool (*UI_WAIT_CALLBACK)(uint32_t wait, uint32_t progress,
const char *message);
void se_set_ui_callback(UI_WAIT_CALLBACK callback);
+void se_handle_status(uint16_t sw1sw2);
secbool se_transmit_mac(uint8_t ins, uint8_t p1, uint8_t p2, uint8_t *data,
uint16_t data_len, uint8_t *recv, uint16_t *recv_len);
@@ -78,6 +67,9 @@ secbool se_reset_storage(void);
secbool se_set_sn(const char *serial, uint8_t len);
secbool se_get_sn(char **serial);
int se_get_version(uint8_t addr, char *ver, uint16_t in_len);
+secbool se_all_versions_at_least(uint8_t required_major,
+ uint8_t required_minor,
+ uint8_t required_patch);
int se_get_build_id(uint8_t addr, char *build_id, uint16_t in_len);
int se_get_hash(uint8_t addr, uint8_t *hash, uint16_t in_len);
int se_get_boot_version(uint8_t addr, char *ver, uint16_t in_len);
@@ -162,9 +154,11 @@ secbool se_set_session_key_ex(uint8_t addr, const uint8_t *session_key);
secbool se_set_session_key(const uint8_t *session_key);
secbool se_containsMnemonic(const char *mnemonic);
-secbool se_exportMnemonic(char *mnemonic, uint16_t dest_size);
-secbool se_set_needs_backup(bool needs_backup);
-secbool se_get_needs_backup(bool *needs_backup);
+secbool se_exportMnemonic(const uint8_t *pin, uint16_t pin_len, char *mnemonic,
+ uint16_t dest_size);
+secbool se_set_mnemonic_export_enabled(bool enabled, const uint8_t *pin,
+ uint16_t pin_len);
+secbool se_get_mnemonic_export_enabled(bool *enabled);
secbool se_hasWipeCode(void);
secbool se_changeWipeCode(const char *pin, const char *wipe_code);
@@ -176,10 +170,21 @@ secbool se_derive_keys(HDNode *out, const char *curve,
secbool se_derive_xmr_key(const char *curve, const uint32_t *address_n,
size_t address_n_count, uint8_t *pubkey,
uint8_t *prikey_hash);
-secbool se_derive_xmr_private_key(const uint8_t *pubkey, const uint32_t index,
- uint8_t *prikey);
secbool se_xmr_get_tx_key(const uint8_t *rand, const uint8_t *hash,
uint8_t *out);
+secbool se_xmr_generate_key_image(const uint8_t recv_deriv[32],
+ uint32_t real_idx,
+ const uint8_t subaddr_sk[32],
+ const uint8_t out_key[32],
+ uint8_t key_image[32]);
+secbool se_xmr_secret_nonce_begin(const uint8_t recv_deriv[32],
+ uint32_t real_idx,
+ const uint8_t subaddr_sk[32],
+ const uint8_t out_key[32], uint8_t out[97]);
+secbool se_xmr_secret_response_finish(uint8_t session_id, const uint8_t c[32],
+ const uint8_t mu_p[32],
+ const uint8_t mu_c[32],
+ const uint8_t z[32], uint8_t s[32]);
secbool se_node_sign_digest(const uint8_t *hash, uint8_t *sig, uint8_t *by);
int se_ecdsa_sign_digest(const uint8_t curve, const uint8_t canonical,
const uint8_t *digest, uint8_t *sig, uint8_t *pby);
@@ -210,8 +215,11 @@ int se_nem_aes256_encrypt(const uint8_t *ed25519_public_key, const uint8_t *iv,
int se_nem_aes256_decrypt(const uint8_t *ed25519_public_key, const uint8_t *iv,
const uint8_t *salt, uint8_t *payload, uint16_t size,
uint8_t *out);
-int se_slip21_node(uint8_t *data);
-int se_slip21_fido_node(uint8_t *data);
+secbool se_slip21_ownership_id(const uint8_t *script_pubkey,
+ uint16_t script_pubkey_len, uint8_t out[32]);
+secbool se_slip21_address_mac(uint32_t slip44, const uint8_t *address,
+ uint16_t address_len, uint8_t out[32]);
+secbool se_slip21_slip25_mac(uint8_t out[32]);
secbool se_authorization_set(const uint32_t authorization_type,
const uint8_t *authorization,
@@ -249,12 +257,29 @@ secbool se_derive_fido_keys(HDNode *out, const char *curve,
uint32_t *fingerprint);
secbool se_fido_hdnode_sign_digest(const uint8_t *hash, uint8_t *sig);
secbool se_fido_att_sign_digest(const uint8_t *hash, uint8_t *sig);
-secbool se_get_fido2_resident_credentials(uint32_t index, uint8_t *dest,
- uint16_t *dst_len);
-secbool se_set_fido2_resident_credentials(uint32_t index, const uint8_t *src,
- uint16_t len);
-secbool se_delete_fido2_resident_credentials(uint32_t index);
-secbool se_delete_all_fido2_credentials(void);
-
-secbool session_generate_seed_percent(uint8_t *percent);
+secbool se_fido_credential_create(const uint8_t *plaintext,
+ uint16_t plaintext_len, uint8_t resident,
+ uint8_t *response, uint16_t *response_len);
+secbool se_fido_credential_validate(const uint8_t rp_id_hash[32],
+ const uint8_t *credential_id,
+ uint16_t credential_id_len,
+ uint8_t *plaintext,
+ uint16_t *plaintext_len);
+secbool se_fido_hmac_secret(const uint8_t *credential_id,
+ uint16_t credential_id_len, const uint8_t *salt,
+ uint16_t salt_len, uint8_t *out);
+secbool se_fido_resident_credentials_list(uint8_t *indexes, uint16_t *count);
+secbool se_fido_resident_credential_read(uint8_t index, uint8_t *packed,
+ uint16_t *packed_len);
+secbool se_fido_resident_credential_import(const uint8_t *credential_id,
+ uint16_t credential_id_len,
+ uint8_t *slot, uint8_t *action);
+secbool se_fido_resident_credential_delete(uint8_t index);
+secbool se_fido_resident_credentials_clear(void);
+
+secbool se_query_progress_percent(uint8_t *percent);
+
+secbool se_get_component_version(uint8_t slot, uint32_t *version);
+secbool se_set_component_version(uint8_t slot, uint32_t version);
+
#endif
diff --git a/core/embed/trezorhal/se_thd89_v2.c b/core/embed/trezorhal/se_thd89_v2.c
new file mode 100644
index 0000000000..99ea4a82a1
--- /dev/null
+++ b/core/embed/trezorhal/se_thd89_v2.c
@@ -0,0 +1,174 @@
+#include "se_thd89_v2.h"
+
+#include
+
+#include "aes/aes.h"
+#include "hmac.h"
+#include "memzero.h"
+#include "sha2.h"
+
+static const uint8_t SESSION_ENC_LABEL[] = "THD89 SESSION ENC";
+static const uint8_t SESSION_MAC_LABEL[] = "THD89 SESSION MAC";
+static const uint8_t SESSION_CONFIRM_LABEL[] = "THD89 SESSION CONFIRM";
+static const uint8_t SESSION_RESPONSE_LABEL[] = "THD89 SESSION RESPONSE V2";
+static const uint8_t SESSION_APDU_HEADER[5] = {0x00, 0xfa, 0x01, 0x00, 0x60};
+static const uint8_t RESPONSE_MAC_DOMAIN[16] = "THD89-RSP-MAC-V1";
+
+static void derive_session_digest(const uint8_t shared_point[64],
+ const uint8_t se_random[16],
+ const uint8_t mcu_random[16],
+ const uint8_t *label, size_t label_len,
+ uint8_t digest[32]) {
+ SHA256_CTX ctx = {0};
+
+ sha256_Init(&ctx);
+ sha256_Update(&ctx, shared_point, 64);
+ sha256_Update(&ctx, se_random, 16);
+ sha256_Update(&ctx, mcu_random, 16);
+ sha256_Update(&ctx, label, label_len);
+ sha256_Final(&ctx, digest);
+ memzero(&ctx, sizeof(ctx));
+}
+
+void thd89_v2_derive_session_keys(const uint8_t shared_point[64],
+ const uint8_t se_random[16],
+ const uint8_t mcu_random[16],
+ uint8_t enc_key[16], uint8_t mac_key[16],
+ uint8_t confirm_key[32]) {
+ uint8_t digest[32] = {0};
+
+ derive_session_digest(shared_point, se_random, mcu_random, SESSION_ENC_LABEL,
+ sizeof(SESSION_ENC_LABEL) - 1, digest);
+ memcpy(enc_key, digest, 16);
+
+ derive_session_digest(shared_point, se_random, mcu_random, SESSION_MAC_LABEL,
+ sizeof(SESSION_MAC_LABEL) - 1, digest);
+ memcpy(mac_key, digest, 16);
+
+ derive_session_digest(shared_point, se_random, mcu_random,
+ SESSION_CONFIRM_LABEL,
+ sizeof(SESSION_CONFIRM_LABEL) - 1, confirm_key);
+ memzero(digest, sizeof(digest));
+}
+
+void thd89_v2_calculate_confirmation(const uint8_t confirm_key[32],
+ const uint8_t se_random[16],
+ const uint8_t request_data[96],
+ uint8_t confirmation[32]) {
+ HMAC_SHA256_CTX ctx = {0};
+
+ hmac_sha256_Init(&ctx, confirm_key, 32);
+ hmac_sha256_Update(&ctx, SESSION_RESPONSE_LABEL,
+ sizeof(SESSION_RESPONSE_LABEL) - 1);
+ hmac_sha256_Update(&ctx, SESSION_APDU_HEADER, sizeof(SESSION_APDU_HEADER));
+ hmac_sha256_Update(&ctx, se_random, 16);
+ hmac_sha256_Update(&ctx, request_data, 96);
+ hmac_sha256_Final(&ctx, confirmation);
+ memzero(&ctx, sizeof(ctx));
+}
+
+static void response_mac_block(const aes_encrypt_ctx *ctx, uint8_t state[16],
+ const uint8_t block[16]) {
+ uint8_t input[16] = {0};
+ uint8_t output[16] = {0};
+
+ for (size_t i = 0; i < sizeof(input); i++) {
+ input[i] = state[i] ^ block[i];
+ }
+ if (aes_ecb_encrypt(input, output, sizeof(output), ctx) == EXIT_SUCCESS) {
+ memcpy(state, output, sizeof(output));
+ } else {
+ memzero(state, 16);
+ }
+ memzero(input, sizeof(input));
+ memzero(output, sizeof(output));
+}
+
+void thd89_v2_calculate_response_mac(const uint8_t mac_key[16],
+ const uint8_t header[4],
+ const uint8_t transaction[16],
+ const uint8_t *ciphertext,
+ uint16_t ciphertext_len, uint16_t sw1sw2,
+ uint8_t response_mac[4]) {
+ aes_encrypt_ctx ctx = {0};
+ uint8_t state[16] = {0};
+ uint8_t context[16] = {0};
+ uint8_t status_block[16] = {0};
+
+ context[0] = header[0];
+ context[1] = header[1];
+ context[2] = header[2];
+ context[3] = header[3];
+ context[4] = (uint8_t)(ciphertext_len >> 8);
+ context[5] = (uint8_t)ciphertext_len;
+ context[6] = 0x80;
+
+ status_block[0] = (uint8_t)(sw1sw2 >> 8);
+ status_block[1] = (uint8_t)sw1sw2;
+ status_block[2] = 0x80;
+
+ if (aes_encrypt_key128(mac_key, &ctx) != EXIT_SUCCESS) {
+ memzero(response_mac, 4);
+ goto cleanup;
+ }
+
+ response_mac_block(&ctx, state, RESPONSE_MAC_DOMAIN);
+ response_mac_block(&ctx, state, transaction);
+ response_mac_block(&ctx, state, context);
+ for (uint16_t offset = 0; offset < ciphertext_len; offset += 16) {
+ response_mac_block(&ctx, state, ciphertext + offset);
+ }
+ response_mac_block(&ctx, state, status_block);
+ memcpy(response_mac, state, 4);
+
+cleanup:
+ memzero(&ctx, sizeof(ctx));
+ memzero(state, sizeof(state));
+ memzero(context, sizeof(context));
+ memzero(status_block, sizeof(status_block));
+}
+
+thd89_v2_response_shape_t thd89_v2_classify_response(uint16_t response_data_len,
+ uint16_t sw1sw2) {
+ if ((sw1sw2 >> 8) == 0x6c) {
+ return response_data_len == 0 ? THD89_V2_RESPONSE_NO_MAC_6C
+ : THD89_V2_RESPONSE_INVALID;
+ }
+ if (response_data_len < 4 || ((response_data_len - 4) % 16) != 0) {
+ return THD89_V2_RESPONSE_INVALID;
+ }
+ return THD89_V2_RESPONSE_MAC_REQUIRED;
+}
+
+bool thd89_v2_constant_time_equal(const uint8_t *left, const uint8_t *right,
+ size_t len) {
+ uint8_t diff = 0;
+
+ if ((left == NULL || right == NULL) && len != 0) {
+ return false;
+ }
+ for (size_t i = 0; i < len; i++) {
+ diff |= left[i] ^ right[i];
+ }
+ return diff == 0;
+}
+
+bool thd89_v2_unpad_iso7816_4(const uint8_t *padded, uint16_t padded_len,
+ uint16_t *plaintext_len) {
+ if (padded == NULL || plaintext_len == NULL || padded_len < 16 ||
+ (padded_len % 16) != 0) {
+ return false;
+ }
+
+ for (uint16_t offset = 0; offset < 16; offset++) {
+ uint8_t value = padded[padded_len - 1 - offset];
+ if (value == 0x80) {
+ *plaintext_len = padded_len - 1 - offset;
+ return true;
+ }
+ if (value != 0x00) {
+ return false;
+ }
+ }
+ return false;
+}
diff --git a/core/embed/trezorhal/se_thd89_v2.h b/core/embed/trezorhal/se_thd89_v2.h
new file mode 100644
index 0000000000..38f82f1524
--- /dev/null
+++ b/core/embed/trezorhal/se_thd89_v2.h
@@ -0,0 +1,41 @@
+#ifndef _TREZORHAL_SE_THD89_V2_H_
+#define _TREZORHAL_SE_THD89_V2_H_
+
+#include
+#include
+#include
+
+typedef enum {
+ THD89_V2_RESPONSE_MAC_REQUIRED = 0,
+ THD89_V2_RESPONSE_NO_MAC_6C,
+ THD89_V2_RESPONSE_INVALID,
+} thd89_v2_response_shape_t;
+
+void thd89_v2_derive_session_keys(const uint8_t shared_point[64],
+ const uint8_t se_random[16],
+ const uint8_t mcu_random[16],
+ uint8_t enc_key[16], uint8_t mac_key[16],
+ uint8_t confirm_key[32]);
+
+void thd89_v2_calculate_confirmation(const uint8_t confirm_key[32],
+ const uint8_t se_random[16],
+ const uint8_t request_data[96],
+ uint8_t confirmation[32]);
+
+void thd89_v2_calculate_response_mac(const uint8_t mac_key[16],
+ const uint8_t header[4],
+ const uint8_t transaction[16],
+ const uint8_t *ciphertext,
+ uint16_t ciphertext_len, uint16_t sw1sw2,
+ uint8_t response_mac[4]);
+
+thd89_v2_response_shape_t thd89_v2_classify_response(uint16_t response_data_len,
+ uint16_t sw1sw2);
+
+bool thd89_v2_constant_time_equal(const uint8_t *left, const uint8_t *right,
+ size_t len);
+
+bool thd89_v2_unpad_iso7816_4(const uint8_t *padded, uint16_t padded_len,
+ uint16_t *plaintext_len);
+
+#endif
diff --git a/core/embed/trezorhal/thd89.c b/core/embed/trezorhal/thd89.c
index 1b1087d2ac..bf91fb5fe5 100644
--- a/core/embed/trezorhal/thd89.c
+++ b/core/embed/trezorhal/thd89.c
@@ -407,14 +407,16 @@ HAL_StatusTypeDef i2c_master_send(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
#define I2C_RECV_BUFFER_TOO_SMALL (0x80)
#define I2C_RECV_TIMEOUT (5 * 1000) // 5s
+#define I2C_RECV_MAX_FRAME_LEN (2u + 1024u + 64u)
int i2c_master_recive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
uint8_t *pData, uint16_t *Size, uint32_t Timeout) {
// uint32_t tickstart, tickstart1;
uint8_t data[4];
- uint16_t temp_len, data_len;
+ uint16_t frame_len, temp_len, data_len;
uint8_t *data_ptr = pData;
uint8_t xor = 0x00;
+ bool buffer_too_small = false;
sw1 = sw2 = 0;
if (hi2c->State == HAL_I2C_STATE_READY) {
@@ -468,12 +470,19 @@ int i2c_master_recive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
}
data[1] = (uint8_t)hi2c->Instance->RXDR;
- temp_len = (data[0] << 8) + data[1] - 2;
+ frame_len = ((uint16_t)data[0] << 8) | data[1];
+ if (frame_len < 2u || frame_len > I2C_RECV_MAX_FRAME_LEN) {
+ *Size = 0;
+ SET_BIT(hi2c->Instance->CR2, I2C_CR2_STOP);
+ hi2c->State = HAL_I2C_STATE_READY;
+ hi2c->Mode = HAL_I2C_MODE_NONE;
+ __HAL_UNLOCK(hi2c);
+ return HAL_ERROR;
+ }
+ temp_len = frame_len - 2u;
data_len = temp_len;
- if (data_len > *Size) {
- return I2C_RECV_BUFFER_TOO_SMALL;
- }
+ buffer_too_small = data_len > *Size;
xor = xor_check(0, data, 2);
while (temp_len > 0) {
@@ -485,7 +494,11 @@ int i2c_master_recive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
if (I2C_WaitOnRXNEFlagUntilTimeout(hi2c, Timeout, 0) != HAL_OK) {
return HAL_ERROR;
}
- *data_ptr++ = (uint8_t)hi2c->Instance->RXDR;
+ uint8_t received = (uint8_t)hi2c->Instance->RXDR;
+ xor ^= received;
+ if (!buffer_too_small) {
+ *data_ptr++ = received;
+ }
}
temp_len -= data_len_tmp;
}
@@ -521,7 +534,6 @@ int i2c_master_recive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
- xor = xor_check(xor, pData, data_len);
xor = xor_check(xor, data, 2);
if (xor != data[2]) {
*Size = 0;
@@ -532,14 +544,15 @@ int i2c_master_recive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
*Size = data_len;
- return HAL_OK;
+ return buffer_too_small ? I2C_RECV_BUFFER_TOO_SMALL : HAL_OK;
} else {
return HAL_BUSY;
}
}
-static secbool _thd89_transmit_ex(uint8_t addr, uint8_t *cmd, uint16_t len,
- uint8_t *resp, uint16_t *resp_len) {
+static secbool _thd89_transmit_raw_ex(uint8_t addr, uint8_t *cmd, uint16_t len,
+ uint8_t *resp, uint16_t *resp_len,
+ uint16_t *sw1sw2) {
int ret = 0;
char err_info[64] = {0};
uint32_t irq = disable_irq();
@@ -569,27 +582,50 @@ static secbool _thd89_transmit_ex(uint8_t addr, uint8_t *cmd, uint16_t len,
}
return secfalse;
}
- if ((0x90 != sw1) || (0x00 != sw2)) {
- return secfalse;
+ if (sw1sw2 != NULL) {
+ *sw1sw2 = ((uint16_t)sw1 << 8) | sw2;
}
-
return sectrue;
}
int thd89_irq_nest = 0;
-secbool thd89_transmit_ex(uint8_t addr, uint8_t *cmd, uint16_t len,
- uint8_t *resp, uint16_t *resp_len) {
+secbool thd89_transmit_raw_ex(uint8_t addr, uint8_t *cmd, uint16_t len,
+ uint8_t *resp, uint16_t *resp_len,
+ uint16_t *sw1sw2) {
+ uint16_t empty_resp_len = 0;
+ uint16_t *io_resp_len = resp_len;
+
+ if (resp == NULL) {
+ io_resp_len = &empty_resp_len;
+ } else if (resp_len == NULL) {
+ return secfalse;
+ }
+
uint32_t irq = disable_irq();
thd89_irq_nest++;
- secbool result = _thd89_transmit_ex(addr, cmd, len, resp, resp_len);
+ secbool result =
+ _thd89_transmit_raw_ex(addr, cmd, len, resp, io_resp_len, sw1sw2);
thd89_irq_nest--;
if (thd89_irq_nest == 0) {
enable_irq(irq);
}
+ if (resp == NULL && resp_len != NULL) {
+ *resp_len = empty_resp_len;
+ }
return result;
}
+secbool thd89_transmit_ex(uint8_t addr, uint8_t *cmd, uint16_t len,
+ uint8_t *resp, uint16_t *resp_len) {
+ uint16_t sw1sw2 = 0;
+ if (thd89_transmit_raw_ex(addr, cmd, len, resp, resp_len, &sw1sw2) !=
+ sectrue) {
+ return secfalse;
+ }
+ return sectrue * (sw1sw2 == 0x9000);
+}
+
secbool thd89_transmit(uint8_t *cmd, uint16_t len, uint8_t *resp,
uint16_t *resp_len) {
return thd89_transmit_ex(THD89_MASTER_ADDRESS, cmd, len, resp, resp_len);
diff --git a/core/embed/trezorhal/thd89.h b/core/embed/trezorhal/thd89.h
index a1b991cba9..275a8babae 100644
--- a/core/embed/trezorhal/thd89.h
+++ b/core/embed/trezorhal/thd89.h
@@ -23,6 +23,9 @@ secbool thd89_fp_transmit(uint8_t *cmd, uint16_t len, uint8_t *resp,
uint16_t *resp_len);
secbool thd89_transmit_ex(uint8_t addr, uint8_t *cmd, uint16_t len,
uint8_t *resp, uint16_t *resp_len);
+secbool thd89_transmit_raw_ex(uint8_t addr, uint8_t *cmd, uint16_t len,
+ uint8_t *resp, uint16_t *resp_len,
+ uint16_t *sw1sw2);
uint16_t thd89_last_error();
#endif
diff --git a/core/mocks/generated/trezorconfig.pyi b/core/mocks/generated/trezorconfig.pyi
index a51d18a5e1..5d82271d7a 100644
--- a/core/mocks/generated/trezorconfig.pyi
+++ b/core/mocks/generated/trezorconfig.pyi
@@ -102,16 +102,16 @@ def change_wipe_code(
# extmod/modtrezorconfig/modtrezorconfig.c
-def get_needs_backup() -> bool:
+def get_mnemonic_export_enabled() -> bool:
"""
- Returns needs_backup.
+ Returns whether mnemonic export is enabled in the SE.
"""
# extmod/modtrezorconfig/modtrezorconfig.c
-def set_needs_backup(needs_backup: bool = False) -> bool:
+def set_mnemonic_export_enabled(enabled: bool, pin: str) -> bool:
"""
- Set needs_backup.
+ Enable or disable mnemonic export in the SE.
"""
@@ -191,7 +191,7 @@ None, iteration_exponent: int | None) -> bool:
# extmod/modtrezorconfig/modtrezorconfig.c
-def se_export_mnemonic() -> bytes:
+def se_export_mnemonic(pin: str) -> bytes:
"""
Export mnemonic from SE.
"""
diff --git a/core/mocks/generated/trezorcrypto/se_thd89.pyi b/core/mocks/generated/trezorcrypto/se_thd89.pyi
index 8b69011666..41c6d078b3 100644
--- a/core/mocks/generated/trezorcrypto/se_thd89.pyi
+++ b/core/mocks/generated/trezorcrypto/se_thd89.pyi
@@ -152,17 +152,18 @@ bytes:
# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
-def slip21_node() -> bytes:
- """
- Returns slip21 node.
- """
+def slip21_ownership_id(script_pubkey: bytes) -> bytes:
+ """Return the SLIP-0019 ownership identifier for script_pubkey."""
# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
-def slip21_fido_node() -> bytes:
- """
- Returns slip21 fido node, seed without passphrase.
- """
+def slip21_address_mac(slip44: int, address: bytes) -> bytes:
+ """Return the SLIP-0024 address MAC."""
+
+
+# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+def slip21_slip25_mac() -> bytes:
+ """Return the SLIP-0025 keychain authorization MAC."""
# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
@@ -222,9 +223,9 @@ def derive_xmr(
# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
-def derive_xmr_privare(
- deriv: bytes
- index: int,
+def xmr_get_tx_key(
+ rand: bytes
+ hash: bytes,
) -> bytes:
"""
base + H_s(derivation || varint(output_index))
@@ -232,13 +233,34 @@ def derive_xmr_privare(
# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
-def xmr_get_tx_key(
- rand: bytes
- hash: bytes,
+def xmr_generate_key_image(
+ recv_deriv: bytes,
+ real_idx: int,
+ subaddr_sk: bytes,
+ out_key: bytes,
) -> bytes:
- """
- base + H_s(derivation || varint(output_index))
- """
+ """Generates a key image without exporting the one-time spend key."""
+
+
+# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+def xmr_secret_nonce_begin(
+ recv_deriv: bytes,
+ real_idx: int,
+ subaddr_sk: bytes,
+ out_key: bytes,
+) -> tuple[bytes, bytes, bytes, int]:
+ """Starts a one-time XMR secret-response session."""
+
+
+# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+def xmr_secret_response_finish(
+ session_id: int,
+ c: bytes,
+ mu_p: bytes,
+ mu_c: bytes,
+ z: bytes,
+) -> bytes:
+ """Finishes a one-time XMR secret-response session."""
# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
@@ -307,10 +329,45 @@ def fido_att_sign_digest(
# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
-def fido_delete_all_credentials() -> None:
- """
- Delete all FIDO2 credentials.
- """
+def fido_credential_create(plaintext: bytes, resident: bool) -> tuple[bytes, int, int]:
+ """Create a FIDO credential ID in the secure element."""
+
+
+# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+def fido_credential_validate(
+ credential_id: bytes, rp_id_hash: bytes | None
+) -> bytes:
+ """Authenticate a credential ID and return its CBOR plaintext."""
+
+
+# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+def fido_resident_credentials_list() -> tuple[int, ...]:
+ """Return occupied resident credential slot indexes."""
+
+
+# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+def fido_resident_credential_read(index: int) -> tuple[bytes, bytes]:
+ """Read one authenticated resident credential."""
+
+
+# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+def fido_resident_credential_import(credential_id: bytes) -> tuple[int, int]:
+ """Store an authenticated external credential ID."""
+
+
+# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+def fido_resident_credential_delete(index: int) -> None:
+ """Delete one resident credential slot."""
+
+
+# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+def fido_resident_credentials_clear() -> None:
+ """Clear every resident credential slot."""
+
+
+# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
+def fido_hmac_secret(credential_id: bytes, salt: bytes) -> bytes:
+ """Return the purpose-bound hmac-secret output for one or two salts."""
# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h
diff --git a/core/src/apps/base.py b/core/src/apps/base.py
index 46f490fced..8fc2501075 100644
--- a/core/src/apps/base.py
+++ b/core/src/apps/base.py
@@ -168,6 +168,7 @@ def get_features() -> Features:
f.sd_card_present = sdcard.is_present()
f.initialized = storage.device.is_initialized()
+ f.needs_backup = False
current_space = se_thd89.get_pin_passphrase_space()
if current_space < 30:
@@ -185,7 +186,6 @@ def get_features() -> Features:
f.passphrase_protection = False
else:
f.passphrase_protection = storage.device.is_passphrase_enabled()
- f.needs_backup = storage.device.needs_backup()
f.unfinished_backup = storage.device.unfinished_backup()
f.no_backup = storage.device.no_backup()
f.flags = storage.device.get_flags()
@@ -404,14 +404,9 @@ async def handle_DoPreauthorized(
async def handle_UnlockPath(ctx: wire.Context, msg: UnlockPath) -> protobuf.MessageType:
- from trezor.crypto import hmac
from trezor.messages import UnlockedPathRequest
from trezor.ui.layouts import confirm_action
from apps.common.paths import SLIP25_PURPOSE
- from apps.common.seed import Slip21Node, get_seed
- from apps.common.writers import write_uint32_le
-
- _KEYCHAIN_MAC_KEY_PATH = [b"TREZOR", b"Keychain MAC key"]
# UnlockPath is relevant only for SLIP-25 paths.
# Note: Currently we only allow unlocking the entire SLIP-25 purpose subtree instead of
@@ -419,13 +414,24 @@ async def handle_UnlockPath(ctx: wire.Context, msg: UnlockPath) -> protobuf.Mess
if msg.address_n != [SLIP25_PURPOSE]:
raise wire.DataError("Invalid path")
- seed = await get_seed(ctx)
- node = Slip21Node(seed)
- node.derive_path(_KEYCHAIN_MAC_KEY_PATH)
- mac = utils.HashWriter(hmac(hmac.SHA256, node.key()))
- for i in msg.address_n:
- write_uint32_le(mac, i)
- expected_mac = mac.get_digest()
+ if utils.USE_THD89:
+ from trezor.crypto import se_thd89
+ from apps.common.seed import get_seed
+
+ await get_seed(ctx)
+ expected_mac = se_thd89.slip21_slip25_mac()
+ else:
+ from trezor.crypto import hmac
+ from apps.common.seed import Slip21Node, get_seed
+ from apps.common.writers import write_uint32_le
+
+ seed = await get_seed(ctx)
+ node = Slip21Node(seed)
+ node.derive_path([b"TREZOR", b"Keychain MAC key"])
+ mac = utils.HashWriter(hmac(hmac.SHA256, node.key()))
+ for i in msg.address_n:
+ write_uint32_le(mac, i)
+ expected_mac = mac.get_digest()
# Require confirmation to access SLIP25 paths unless already authorized.
if msg.mac:
@@ -563,8 +569,6 @@ def get_state() -> str | None:
dev_state = _(i18n_keys.MSG__SEEDLESS)
elif storage.device.unfinished_backup():
dev_state = _(i18n_keys.MSG__BACKUP_FAILED)
- elif storage.device.needs_backup():
- dev_state = _(i18n_keys.MSG__NEEDS_BACKUP)
elif not config.has_pin():
dev_state = _(i18n_keys.MSG__PIN_NOT_SET)
elif storage.device.get_experimental_features():
diff --git a/core/src/apps/bitcoin/ownership.py b/core/src/apps/bitcoin/ownership.py
index c98a1ad479..3a6ff27963 100644
--- a/core/src/apps/bitcoin/ownership.py
+++ b/core/src/apps/bitcoin/ownership.py
@@ -144,6 +144,11 @@ def read_scriptsig_witness(ownership_proof: bytes) -> tuple[memoryview, memoryvi
def get_identifier(script_pubkey: bytes, keychain: Keychain) -> bytes:
+ if utils.USE_THD89:
+ from trezor.crypto import se_thd89
+
+ return se_thd89.slip21_ownership_id(script_pubkey)
+
# k = Key(m/"SLIP-0019"/"Ownership identification key")
node = keychain.derive_slip21(_OWNERSHIP_ID_KEY_PATH)
diff --git a/core/src/apps/common/address_mac.py b/core/src/apps/common/address_mac.py
index 496395365b..35a9ff2cde 100644
--- a/core/src/apps/common/address_mac.py
+++ b/core/src/apps/common/address_mac.py
@@ -20,6 +20,11 @@ def check_address_mac(
def get_address_mac(address: str, slip44: int, keychain: Keychain) -> bytes:
+ if utils.USE_THD89:
+ from trezor.crypto import se_thd89
+
+ return se_thd89.slip21_address_mac(slip44, address.encode())
+
# k = Key(m/"SLIP-0024"/"Address MAC key")
node = keychain.derive_slip21(_ADDRESS_MAC_KEY_PATH)
diff --git a/core/src/apps/common/keychain.py b/core/src/apps/common/keychain.py
index e7467755b2..02e8a5173a 100644
--- a/core/src/apps/common/keychain.py
+++ b/core/src/apps/common/keychain.py
@@ -169,9 +169,7 @@ def derive_slip21(self, path: paths.Slip21Path) -> Slip21Node:
):
raise FORBIDDEN_KEY_PATH
if utils.USE_THD89:
- node = Slip21Node(seed=b"\x00" * 32)
- node.derive_path(path)
- return node
+ raise FORBIDDEN_KEY_PATH
else:
return self._derive_with_cache(
prefix_len=1,
diff --git a/core/src/apps/common/seed.py b/core/src/apps/common/seed.py
index 1df6783db5..876ad0edd8 100644
--- a/core/src/apps/common/seed.py
+++ b/core/src/apps/common/seed.py
@@ -23,9 +23,7 @@ def __init__(self, seed: bytes | None = None, data: bytes | None = None) -> None
if data is not None:
self.data = data
else:
- from trezor.crypto import se_thd89
-
- self.data = se_thd89.slip21_node()
+ raise ValueError("THD89 SLIP21 roots are not exportable")
else:
if seed is not None and data is not None:
raise ValueError("Specify exactly one of: seed, data")
@@ -199,9 +197,7 @@ def derive_fido_node_with_se(
def derive_slip21_node_without_passphrase(path: Slip21Path) -> Slip21Node:
if utils.USE_THD89:
- from trezor.crypto import se_thd89
-
- node = Slip21Node(data=se_thd89.slip21_fido_node())
+ raise ValueError("THD89 SLIP21 roots are not exportable")
else:
seed = _get_seed_without_passphrase()
node = Slip21Node(seed)
diff --git a/core/src/apps/debug/load_device.py b/core/src/apps/debug/load_device.py
index b72407bf62..21fcce54c8 100644
--- a/core/src/apps/debug/load_device.py
+++ b/core/src/apps/debug/load_device.py
@@ -38,7 +38,6 @@ async def load_device(ctx: wire.Context, msg: LoadDevice) -> Success:
storage.device.store_mnemonic_secret(
secret,
backup_type,
- needs_backup=msg.needs_backup is True,
no_backup=msg.no_backup is True,
identifier=identifier,
iteration_exponent=iteration_exponent,
diff --git a/core/src/apps/homescreen/homescreen.py b/core/src/apps/homescreen/homescreen.py
index b98ab6cc5a..8766196a93 100644
--- a/core/src/apps/homescreen/homescreen.py
+++ b/core/src/apps/homescreen/homescreen.py
@@ -52,8 +52,6 @@ def do_render(self) -> None:
ui.header_error("SEEDLESS")
elif storage.device.is_initialized() and storage.device.unfinished_backup():
ui.header_error("BACKUP FAILED!")
- elif storage.device.is_initialized() and storage.device.needs_backup():
- ui.header_warning("NEEDS BACKUP!")
elif storage.device.is_initialized() and not config.has_pin():
ui.header_warning("PIN NOT SET!")
elif storage.device.get_experimental_features():
diff --git a/core/src/apps/management/backup_device.py b/core/src/apps/management/backup_device.py
index 8c96ba8db6..ddb38b7b5b 100644
--- a/core/src/apps/management/backup_device.py
+++ b/core/src/apps/management/backup_device.py
@@ -1,14 +1,9 @@
from typing import TYPE_CHECKING
-import storage
import storage.device
from trezor import wire
from trezor.messages import Success
-from apps.common import mnemonic
-
-from .reset_device import backup_seed, layout
-
if TYPE_CHECKING:
from trezor.messages import BackupDevice
@@ -16,20 +11,4 @@
async def backup_device(ctx: wire.Context, msg: BackupDevice) -> Success:
if not storage.device.is_initialized():
raise wire.NotInitialized("Device is not initialized")
- if not storage.device.needs_backup():
- raise wire.ProcessError("Seed already backed up")
-
- mnemonic_secret, mnemonic_type = mnemonic.get()
- if mnemonic_secret is None:
- raise RuntimeError
-
- storage.device.set_unfinished_backup(True)
- storage.device.set_backed_up(False)
-
- await backup_seed(ctx, mnemonic_type, mnemonic_secret)
-
- storage.device.set_unfinished_backup(False)
-
- await layout.show_backup_success(ctx)
-
- return Success(message="Seed successfully backed up")
+ raise wire.ProcessError("Seed already backed up")
diff --git a/core/src/apps/management/recovery_device/homescreen.py b/core/src/apps/management/recovery_device/homescreen.py
index d2ec5dfa99..a70be8bfd8 100644
--- a/core/src/apps/management/recovery_device/homescreen.py
+++ b/core/src/apps/management/recovery_device/homescreen.py
@@ -258,7 +258,6 @@ async def _finish_recovery(
storage_device.store_mnemonic_secret(
secret,
backup_type,
- needs_backup=False,
no_backup=False,
identifier=identifier,
iteration_exponent=exponent,
diff --git a/core/src/apps/management/reset_device/__init__.py b/core/src/apps/management/reset_device/__init__.py
index 9604da8820..545762f4bc 100644
--- a/core/src/apps/management/reset_device/__init__.py
+++ b/core/src/apps/management/reset_device/__init__.py
@@ -91,7 +91,6 @@ async def reset_device(
storage_device.store_mnemonic_secret(
secret, # for SLIP-39, this is the EMS
backup_type,
- needs_backup=not perform_backup,
no_backup=bool(msg.no_backup),
identifier=storage_device.get_slip39_identifier(),
iteration_exponent=storage_device.get_slip39_iteration_exponent(),
diff --git a/core/src/apps/monero/live_refresh.py b/core/src/apps/monero/live_refresh.py
index 92bc9e9549..911bbb8f45 100644
--- a/core/src/apps/monero/live_refresh.py
+++ b/core/src/apps/monero/live_refresh.py
@@ -2,7 +2,7 @@
from typing import TYPE_CHECKING
import storage.cache
-from trezor import log
+from trezor import log, utils
from trezor.enums import MessageType
from trezor.messages import (
MoneroLiveRefreshFinalAck,
@@ -83,21 +83,53 @@ async def _refresh_step(
if __debug__:
log.debug(__name__, "refresh, step i: %d", s.current_output)
- # Compute spending secret key and the key image
- # spend_priv = Hs(recv_deriv || real_out_idx) + spend_key_private
- # If subaddr:
- # spend_priv += Hs("SubAddr" || view_key_private || major || minor)
- # out_key = spend_priv * G, KI: spend_priv * Hp(out_key)
out_key = crypto_helpers.decodepoint(msg.out_key)
recv_deriv = crypto_helpers.decodepoint(msg.recv_deriv)
received_index = msg.sub_addr_major, msg.sub_addr_minor
- spend_priv, ki = monero.generate_tx_spend_and_key_image(
- s.creds, out_key, recv_deriv, msg.real_out_idx, received_index
- )
- ki_enc = crypto_helpers.encodepoint(ki)
- sig = key_image.generate_ring_signature(ki_enc, ki, [out_key], spend_priv, 0, False)
- del spend_priv # spend_priv never leaves the device
+ if utils.USE_THD89:
+ from trezor.crypto import se_thd89
+
+ aG, aH, ki_enc, session_id = se_thd89.xmr_secret_nonce_begin(
+ msg.recv_deriv,
+ msg.real_out_idx,
+ misc.xmr_subaddress_secret_key(
+ s.creds.view_key_private, received_index
+ ),
+ msg.out_key,
+ )
+ ki = crypto_helpers.decodepoint(ki_enc)
+
+ def se_response(c: crypto.Scalar) -> bytes:
+ return se_thd89.xmr_secret_response_finish(
+ session_id,
+ crypto_helpers.encodeint(c),
+ crypto_helpers.encodeint(crypto.Scalar(1)),
+ crypto_helpers.encodeint(crypto.Scalar(0)),
+ crypto_helpers.encodeint(crypto.Scalar(0)),
+ )
+
+ sig = key_image.generate_ring_signature(
+ ki_enc,
+ ki,
+ [out_key],
+ crypto.Scalar(),
+ 0,
+ False,
+ (aG, aH, se_response),
+ )
+ else:
+ # Compute spending secret key and the key image.
+ spend_priv, ki = monero.generate_tx_spend_and_key_image(
+ s.creds, out_key, recv_deriv, msg.real_out_idx, received_index
+ )
+ if spend_priv is None:
+ raise RuntimeError("XMR spend key missing")
+ ki_enc = crypto_helpers.encodepoint(ki)
+ sig = key_image.generate_ring_signature(
+ ki_enc, ki, [out_key], spend_priv, 0, False
+ )
+ del spend_priv
# Serialize into buff
buff[0:32] = ki_enc
diff --git a/core/src/apps/monero/misc.py b/core/src/apps/monero/misc.py
index 751981e783..d992e62889 100644
--- a/core/src/apps/monero/misc.py
+++ b/core/src/apps/monero/misc.py
@@ -9,6 +9,79 @@
from .xmr.crypto import Scalar
from .xmr.credentials import AccountCreds
+_XMR_SE_INPUT_TOKEN_MAGIC = b"XMRSE01"
+_XMR_SE_INPUT_TOKEN_LEN = len(_XMR_SE_INPUT_TOKEN_MAGIC) + 32 + 4 + 32 + 32
+
+
+def encode_xmr_se_input_token(
+ recv_deriv: bytes, real_idx: int, subaddr_sk: bytes, out_key: bytes
+) -> bytearray:
+ if len(recv_deriv) != 32 or len(subaddr_sk) != 32 or len(out_key) != 32:
+ raise ValueError("Invalid XMR SE token key length")
+ if real_idx < 0 or real_idx > 0xFFFFFFFF:
+ raise ValueError("Invalid XMR output index")
+
+ token = bytearray(_XMR_SE_INPUT_TOKEN_LEN)
+ offset = 0
+ token[offset : offset + len(_XMR_SE_INPUT_TOKEN_MAGIC)] = (
+ _XMR_SE_INPUT_TOKEN_MAGIC
+ )
+ offset += len(_XMR_SE_INPUT_TOKEN_MAGIC)
+ token[offset : offset + 32] = recv_deriv
+ offset += 32
+ token[offset : offset + 4] = bytes(
+ (
+ real_idx & 0xFF,
+ (real_idx >> 8) & 0xFF,
+ (real_idx >> 16) & 0xFF,
+ (real_idx >> 24) & 0xFF,
+ )
+ )
+ offset += 4
+ token[offset : offset + 32] = subaddr_sk
+ offset += 32
+ token[offset : offset + 32] = out_key
+ return token
+
+
+def decode_xmr_se_input_token(token: bytes) -> tuple[bytes, int, bytes, bytes]:
+ if len(token) != _XMR_SE_INPUT_TOKEN_LEN:
+ raise ValueError("Invalid XMR SE token length")
+ if token[: len(_XMR_SE_INPUT_TOKEN_MAGIC)] != _XMR_SE_INPUT_TOKEN_MAGIC:
+ raise ValueError("Invalid XMR SE token magic")
+
+ offset = len(_XMR_SE_INPUT_TOKEN_MAGIC)
+ recv_deriv = token[offset : offset + 32]
+ offset += 32
+ real_idx = (
+ token[offset]
+ | (token[offset + 1] << 8)
+ | (token[offset + 2] << 16)
+ | (token[offset + 3] << 24)
+ )
+ offset += 4
+ subaddr_sk = token[offset : offset + 32]
+ offset += 32
+ out_key = token[offset : offset + 32]
+ return recv_deriv, real_idx, subaddr_sk, out_key
+
+
+def xmr_subaddress_secret_key(
+ view_key_private: Scalar, received_index: tuple[int, int]
+) -> bytes:
+ if received_index == (0, 0):
+ return b"\x00" * 32
+
+ from apps.monero.xmr import crypto_helpers, monero
+
+ return crypto_helpers.encodeint(
+ monero.get_subaddress_secret_key(
+ view_key_private,
+ major=received_index[0],
+ minor=received_index[1],
+ )
+ )
+
def get_creds(
keychain: Keychain, address_n: Bip32Path, network_type: MoneroNetworkType
diff --git a/core/src/apps/monero/signing/step_02_set_input.py b/core/src/apps/monero/signing/step_02_set_input.py
index 1f5c9a554e..8cc4277956 100644
--- a/core/src/apps/monero/signing/step_02_set_input.py
+++ b/core/src/apps/monero/signing/step_02_set_input.py
@@ -13,7 +13,7 @@
"""
from typing import TYPE_CHECKING
-from apps.monero import layout
+from apps.monero import layout, misc
from apps.monero.xmr import crypto, crypto_helpers, monero, serialize
from .state import State
@@ -57,15 +57,17 @@ async def set_input(
# Calculates `derivation = Ra`, private spend key `x = H(Ra||i) + b` to be able
# to spend the UTXO; and key image `I = x*H(P||i)`
- xi, ki, _di = monero.generate_tx_spend_and_key_image_and_derivation(
- state.creds,
- state.subaddresses,
- out_key,
- tx_key,
- additional_tx_pub_key,
- src_entr.real_output_in_tx_index,
- state.account_idx,
- src_entr.subaddr_minor,
+ xi, ki, recv_derivation, received_index = (
+ monero.generate_tx_spend_and_key_image_and_derivation(
+ state.creds,
+ state.subaddresses,
+ out_key,
+ tx_key,
+ additional_tx_pub_key,
+ src_entr.real_output_in_tx_index,
+ state.account_idx,
+ src_entr.subaddr_minor,
+ )
)
state.mem_trace(1, True)
@@ -106,10 +108,30 @@ async def set_input(
crypto_helpers.encodeint(alpha),
)
- spend_enc = chacha_poly.encrypt_pack(
- offloading_keys.enc_key_spend(state.key_enc, state.current_input_index),
- crypto_helpers.encodeint(xi),
- )
+ if xi is None:
+ spend_plain = misc.encode_xmr_se_input_token(
+ crypto_helpers.encodepoint(recv_derivation),
+ src_entr.real_output_in_tx_index,
+ misc.xmr_subaddress_secret_key(
+ state.creds.view_key_private, received_index
+ ),
+ crypto_helpers.encodepoint(out_key),
+ )
+ spend_enc = chacha_poly.encrypt_pack(
+ offloading_keys.enc_key_spend(
+ state.key_enc, state.current_input_index
+ ),
+ spend_plain,
+ )
+ for i in range(len(spend_plain)):
+ spend_plain[i] = 0
+ else:
+ spend_enc = chacha_poly.encrypt_pack(
+ offloading_keys.enc_key_spend(
+ state.key_enc, state.current_input_index
+ ),
+ crypto_helpers.encodeint(xi),
+ )
state.last_step = state.STEP_INP
if state.current_input_index + 1 == state.input_count:
diff --git a/core/src/apps/monero/signing/step_09_sign_input.py b/core/src/apps/monero/signing/step_09_sign_input.py
index 4f401b4bfe..c2e3d05358 100644
--- a/core/src/apps/monero/signing/step_09_sign_input.py
+++ b/core/src/apps/monero/signing/step_09_sign_input.py
@@ -15,7 +15,7 @@
from trezor import utils
-from apps.monero import layout
+from apps.monero import layout, misc
from apps.monero.xmr import crypto, crypto_helpers
from .state import State
@@ -79,6 +79,7 @@ async def sign_input(
raise ValueError("Key image order invalid")
state.last_ki = cur_ki if state.current_input_index < state.input_count else None
+ expected_ki = cur_ki
del (cur_ki, vini_bin, vini_hmac, vini_hmac_comp)
gc.collect()
@@ -122,13 +123,17 @@ async def sign_input(
state.mem_trace(2, True)
- # Spending secret
- spend_key = crypto_helpers.decodeint(
- chacha_poly.decrypt_pack(
- offloading_keys.enc_key_spend(state.key_enc, input_position),
- bytes(spend_enc),
- )
+ spend_plain = chacha_poly.decrypt_pack(
+ offloading_keys.enc_key_spend(state.key_enc, input_position),
+ bytes(spend_enc),
)
+ if utils.USE_THD89:
+ recv_deriv, real_out_idx, subaddr_sk, se_out_key = (
+ misc.decode_xmr_se_input_token(spend_plain)
+ )
+ spend_key = crypto.Scalar()
+ else:
+ spend_key = crypto_helpers.decodeint(spend_plain)
del (
offloading_keys,
@@ -147,14 +152,21 @@ async def sign_input(
index = src_entr.real_output
input_secret_key = CtKey(spend_key, crypto_helpers.decodeint(src_entr.mask))
- # Private key correctness test
- utils.ensure(
- crypto.point_eq(
- crypto_helpers.decodepoint(src_entr.outputs[src_entr.real_output].key.dest),
- crypto.scalarmult_base_into(None, input_secret_key.dest),
- ),
- "Real source entry's destination does not equal spend key's",
- )
+ real_out_key = src_entr.outputs[src_entr.real_output].key.dest
+ if utils.USE_THD89:
+ utils.ensure(
+ crypto.ct_equals(real_out_key, se_out_key),
+ "Real source entry's destination does not equal SE token's",
+ )
+ else:
+ # Private key correctness test
+ utils.ensure(
+ crypto.point_eq(
+ crypto_helpers.decodepoint(real_out_key),
+ crypto.scalarmult_base_into(None, input_secret_key.dest),
+ ),
+ "Real source entry's destination does not equal spend key's",
+ )
utils.ensure(
crypto.point_eq(
crypto_helpers.decodepoint(
@@ -167,6 +179,34 @@ async def sign_input(
state.mem_trace(4, True)
+ se_secret = None
+ if utils.USE_THD89:
+ from trezor.crypto import se_thd89
+
+ aG, aH, key_image, session_id = se_thd89.xmr_secret_nonce_begin(
+ recv_deriv, real_out_idx, subaddr_sk, se_out_key
+ )
+ utils.ensure(
+ crypto.ct_equals(key_image, expected_ki),
+ "SE key image does not equal vini's",
+ )
+
+ def se_response(
+ c: crypto.Scalar,
+ mu_p: crypto.Scalar,
+ mu_c: crypto.Scalar,
+ z: crypto.Scalar,
+ ) -> bytes:
+ return se_thd89.xmr_secret_response_finish(
+ session_id,
+ crypto_helpers.encodeint(c),
+ crypto_helpers.encodeint(mu_p),
+ crypto_helpers.encodeint(mu_c),
+ crypto_helpers.encodeint(z),
+ )
+
+ se_secret = (aG, aH, key_image, se_response)
+
from apps.monero.xmr import clsag
mg_buffer = []
@@ -187,6 +227,7 @@ async def sign_input(
pseudo_out_c,
index,
mg_buffer,
+ se_secret,
)
del (CtKey, input_secret_key, pseudo_out_alpha, clsag, ring_pubkeys)
diff --git a/core/src/apps/monero/xmr/clsag.py b/core/src/apps/monero/xmr/clsag.py
index c2daa86b3f..2ea98c8bf5 100644
--- a/core/src/apps/monero/xmr/clsag.py
+++ b/core/src/apps/monero/xmr/clsag.py
@@ -49,7 +49,7 @@
from apps.monero.xmr.serialize import int_serialize
if TYPE_CHECKING:
- from typing import Any, TypeGuard, TypeVar
+ from typing import Any, Callable, TypeGuard, TypeVar
from .serialize_messages.tx_ct_key import CtKey
from trezor.messages import MoneroRctKeyPublic
@@ -59,6 +59,10 @@
def list_of_type(lst: list[Any], typ: type[T]) -> TypeGuard[list[T]]:
...
+ SecretResponse = Callable[
+ [crypto.Scalar, crypto.Scalar, crypto.Scalar, crypto.Scalar], bytes
+ ]
+
_HASH_KEY_CLSAG_ROUND = b"CLSAG_round\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
_HASH_KEY_CLSAG_AGG_0 = b"CLSAG_agg_0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
@@ -73,6 +77,7 @@ def generate_clsag_simple(
cout: crypto.Point,
index: int,
mg_buff: list[bytearray],
+ se_secret: tuple[bytes, bytes, bytes, "SecretResponse"] | None = None,
) -> list[bytes]:
"""
CLSAG for RctType.Simple
@@ -105,7 +110,9 @@ def generate_clsag_simple(
del pubs
gc.collect()
- return _generate_clsag(message, P, p, C_nonzero, z, cout, index, mg_buff)
+ return _generate_clsag(
+ message, P, p, C_nonzero, z, cout, index, mg_buff, se_secret
+ )
def _generate_clsag(
@@ -117,11 +124,12 @@ def _generate_clsag(
Cout: crypto.Point,
index: int,
mg_buff: list[bytearray],
+ se_secret: tuple[bytes, bytes, bytes, "SecretResponse"] | None = None,
) -> list[bytes]:
sI = crypto.Point() # sig.I
sD = crypto.Point() # sig.D
sc1 = crypto.Scalar() # sig.c1
- a = crypto.random_scalar()
+ a = crypto.random_scalar() if se_secret is None else crypto.Scalar()
H = crypto.Point()
D = crypto.Point()
Cout_bf = crypto_helpers.encodepoint(Cout)
@@ -131,7 +139,10 @@ def _generate_clsag(
tmp_bf = bytearray(32)
crypto.hash_to_point_into(H, P[index])
- crypto.scalarmult_into(sI, H, p) # I = p*H
+ if se_secret is None:
+ crypto.scalarmult_into(sI, H, p) # I = p*H
+ else:
+ crypto.decodepoint_into(sI, se_secret[2])
crypto.scalarmult_into(D, H, z) # D = z*H
crypto.sc_mul_into(tmp_sc, z, crypto_helpers.INV_EIGHT_SC) # 1/8*z
crypto.scalarmult_into(sD, H, tmp_sc) # sig.D = 1/8*z*H
@@ -170,10 +181,14 @@ def hsh_PC(x):
c_to_hash.update(message)
chasher = c_to_hash.copy()
- crypto.scalarmult_base_into(tmp, a)
- chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aG
- crypto.scalarmult_into(tmp, H, a)
- chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aH
+ if se_secret is None:
+ crypto.scalarmult_base_into(tmp, a)
+ chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aG
+ crypto.scalarmult_into(tmp, H, a)
+ chasher.update(crypto.encodepoint_into(tmp_bf, tmp)) # aH
+ else:
+ chasher.update(se_secret[0])
+ chasher.update(se_secret[1])
c = crypto_helpers.decodeint(chasher.digest())
del (chasher, H)
@@ -224,10 +239,13 @@ def hsh_PC(x):
gc.collect()
# Final scalar = a - c * (mu_P * p + mu_c * Z)
- crypto.sc_mul_into(tmp_sc, mu_P, p)
- crypto.sc_muladd_into(tmp_sc, mu_C, z, tmp_sc)
- crypto.sc_mulsub_into(tmp_sc, c, tmp_sc, a)
- crypto.encodeint_into(mg_buff[index + 1], tmp_sc)
+ if se_secret is None:
+ crypto.sc_mul_into(tmp_sc, mu_P, p)
+ crypto.sc_muladd_into(tmp_sc, mu_C, z, tmp_sc)
+ crypto.sc_mulsub_into(tmp_sc, c, tmp_sc, a)
+ crypto.encodeint_into(mg_buff[index + 1], tmp_sc)
+ else:
+ mg_buff[index + 1][:] = se_secret[3](c, mu_P, mu_C, z)
if TYPE_CHECKING:
assert list_of_type(mg_buff, bytes)
diff --git a/core/src/apps/monero/xmr/key_image.py b/core/src/apps/monero/xmr/key_image.py
index 305b6c7150..028b60be80 100644
--- a/core/src/apps/monero/xmr/key_image.py
+++ b/core/src/apps/monero/xmr/key_image.py
@@ -4,11 +4,14 @@
from apps.monero.xmr.serialize.int_serialize import dump_uvarint_b
if TYPE_CHECKING:
+ from typing import Callable
+
from apps.monero.xmr.credentials import AccountCreds
from trezor.messages import MoneroTransferDetails
Subaddresses = dict[bytes, tuple[int, int]]
Sig = list[list[crypto.Scalar]]
+ SecretResponse = Callable[[crypto.Scalar], bytes]
def compute_hash(rr: MoneroTransferDetails) -> bytes:
@@ -66,6 +69,67 @@ def _export_key_image(
"""
Generates key image for the TXO + signature for the key image
"""
+ from trezor import utils
+
+ if utils.USE_THD89:
+ from apps.monero import misc
+ from trezor.crypto import se_thd89
+
+ recv_derivation = crypto_helpers.generate_key_derivation(
+ tx_pub_key, creds.view_key_private
+ )
+ additional_recv_derivation = (
+ crypto_helpers.generate_key_derivation(
+ additional_tx_pub_key, creds.view_key_private
+ )
+ if additional_tx_pub_key
+ else None
+ )
+ subaddr_recv_info = monero.is_out_to_account(
+ subaddresses,
+ pkey,
+ recv_derivation,
+ additional_recv_derivation,
+ out_idx,
+ creds,
+ sub_addr_major,
+ sub_addr_minor,
+ )
+ if subaddr_recv_info is None:
+ raise monero.XmrNoSuchAddressException("No such addr")
+
+ received_index, derivation = subaddr_recv_info
+ out_key = crypto_helpers.encodepoint(pkey)
+ aG, aH, key_image, session_id = se_thd89.xmr_secret_nonce_begin(
+ crypto_helpers.encodepoint(derivation),
+ out_idx,
+ misc.xmr_subaddress_secret_key(
+ creds.view_key_private, received_index
+ ),
+ out_key,
+ )
+ ki = crypto_helpers.decodepoint(key_image)
+
+ def se_response(c: crypto.Scalar) -> bytes:
+ return se_thd89.xmr_secret_response_finish(
+ session_id,
+ crypto_helpers.encodeint(c),
+ crypto_helpers.encodeint(crypto.Scalar(1)),
+ crypto_helpers.encodeint(crypto.Scalar(0)),
+ crypto_helpers.encodeint(crypto.Scalar(0)),
+ )
+
+ sig = generate_ring_signature(
+ key_image,
+ ki,
+ [pkey],
+ crypto.Scalar(),
+ 0,
+ test,
+ (aG, aH, se_response),
+ )
+ return ki, sig
+
r = monero.generate_tx_spend_and_key_image_and_derivation(
creds,
subaddresses,
@@ -77,6 +141,8 @@ def _export_key_image(
sub_addr_minor,
)
xi, ki, _ = r[:3]
+ if xi is None:
+ raise RuntimeError("XMR spend key missing")
phash = crypto_helpers.encodepoint(ki)
sig = generate_ring_signature(phash, ki, [pkey], xi, 0, test)
@@ -91,6 +157,7 @@ def generate_ring_signature(
sec: crypto.Scalar,
sec_idx: int,
test: bool = False,
+ se_secret: tuple[bytes, bytes, "SecretResponse"] | None = None,
) -> Sig:
"""
Generates ring signature with key image.
@@ -99,15 +166,18 @@ def generate_ring_signature(
from trezor.utils import memcpy
if test:
- t = crypto.scalarmult_base_into(None, sec)
- if not crypto.point_eq(t, pubs[sec_idx]):
- raise ValueError("Invalid sec key")
-
- k_i = monero.generate_key_image(crypto_helpers.encodepoint(pubs[sec_idx]), sec)
- if not crypto.point_eq(k_i, image):
- raise ValueError("Key image invalid")
for k in pubs:
crypto.ge25519_check(k)
+ if se_secret is None:
+ t = crypto.scalarmult_base_into(None, sec)
+ if not crypto.point_eq(t, pubs[sec_idx]):
+ raise ValueError("Invalid sec key")
+
+ k_i = monero.generate_key_image(
+ crypto_helpers.encodepoint(pubs[sec_idx]), sec
+ )
+ if not crypto.point_eq(k_i, image):
+ raise ValueError("Key image invalid")
buff_off = len(prefix_hash)
buff = bytearray(buff_off + 2 * 32 * len(pubs))
@@ -123,14 +193,22 @@ def generate_ring_signature(
for i in range(len(pubs)):
if i == sec_idx:
- k = crypto.random_scalar()
- tmp3 = crypto.scalarmult_base_into(None, k)
- crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp3)
+ if se_secret is None:
+ k = crypto.random_scalar()
+ tmp3 = crypto.scalarmult_base_into(None, k)
+ crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp3)
+ else:
+ mvbuff[buff_off : buff_off + 32] = se_secret[0]
buff_off += 32
- tmp3 = crypto.hash_to_point_into(None, crypto_helpers.encodepoint(pubs[i]))
- tmp2 = crypto.scalarmult_into(None, tmp3, k)
- crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp2)
+ if se_secret is None:
+ tmp3 = crypto.hash_to_point_into(
+ None, crypto_helpers.encodepoint(pubs[i])
+ )
+ tmp2 = crypto.scalarmult_into(None, tmp3, k)
+ crypto.encodepoint_into(mvbuff[buff_off : buff_off + 32], tmp2)
+ else:
+ mvbuff[buff_off : buff_off + 32] = se_secret[1]
buff_off += 32
else:
@@ -151,5 +229,8 @@ def generate_ring_signature(
h = crypto.hash_to_scalar_into(None, buff)
sig[sec_idx][0] = crypto.sc_sub_into(None, h, sum)
- sig[sec_idx][1] = crypto.sc_mulsub_into(None, sig[sec_idx][0], sec, k)
+ if se_secret is None:
+ sig[sec_idx][1] = crypto.sc_mulsub_into(None, sig[sec_idx][0], sec, k)
+ else:
+ sig[sec_idx][1] = crypto_helpers.decodeint(se_secret[2](sig[sec_idx][0]))
return sig
diff --git a/core/src/apps/monero/xmr/monero.py b/core/src/apps/monero/xmr/monero.py
index f50446db62..4bfaa0d399 100644
--- a/core/src/apps/monero/xmr/monero.py
+++ b/core/src/apps/monero/xmr/monero.py
@@ -131,7 +131,7 @@ def generate_tx_spend_and_key_image(
recv_derivation: crypto.Point,
real_output_index: int,
received_index: tuple[int, int],
-) -> tuple[crypto.Scalar, crypto.Point]:
+) -> tuple[crypto.Scalar | None, crypto.Point]:
"""
Generates UTXO spending key and key image.
Corresponds to generate_key_image_helper_precomp() in the Monero codebase.
@@ -147,12 +147,18 @@ def generate_tx_spend_and_key_image(
from trezor import utils
if utils.USE_THD89:
+ from apps.monero import misc
from trezor.crypto import se_thd89
- recv_derivation = crypto_helpers.encodepoint(recv_derivation)
- # derive secret key with subaddress - step 1: original CN derivation
- scalar_step1 = se_thd89.derive_xmr_private(recv_derivation, real_output_index)
- scalar_step1 = crypto_helpers.decodeint(scalar_step1)
+ key_image = se_thd89.xmr_generate_key_image(
+ crypto_helpers.encodepoint(recv_derivation),
+ real_output_index,
+ misc.xmr_subaddress_secret_key(
+ ack.view_key_private, received_index
+ ),
+ crypto_helpers.encodepoint(out_key),
+ )
+ return None, crypto_helpers.decodepoint(key_image)
else:
if crypto.sc_iszero(ack.spend_key_private):
raise ValueError("Watch-only wallet not supported")
@@ -206,9 +212,11 @@ def generate_tx_spend_and_key_image_and_derivation(
real_output_index: int | None,
sub_addr_major: int | None,
sub_addr_minor: int | None,
-) -> tuple[crypto.Scalar, crypto.Point, crypto.Point]:
+) -> tuple[
+ crypto.Scalar | None, crypto.Point, crypto.Point, tuple[int, int]
+]:
"""
- Generates UTXO spending key and key image and corresponding derivation.
+ Generates a UTXO spending key, key image, derivation and received index.
Supports subaddresses.
Corresponds to generate_key_image_helper() in the Monero codebase.
@@ -250,7 +258,7 @@ def generate_tx_spend_and_key_image_and_derivation(
xi, ki = generate_tx_spend_and_key_image(
creds, out_key, subaddr_recv_info[1], real_output_index, subaddr_recv_info[0]
)
- return xi, ki, recv_derivation
+ return xi, ki, subaddr_recv_info[1], subaddr_recv_info[0]
def compute_subaddresses(
diff --git a/core/src/apps/webauthn/add_resident_credential.py b/core/src/apps/webauthn/add_resident_credential.py
index 9141218418..c961d38ade 100644
--- a/core/src/apps/webauthn/add_resident_credential.py
+++ b/core/src/apps/webauthn/add_resident_credential.py
@@ -1,5 +1,5 @@
import storage.device
-from trezor import wire
+from trezor import utils, wire
from trezor.lvglui.i18n import gettext as _, keys as i18n_keys
from trezor.messages import Success, WebAuthnAddResidentCredential
from trezor.ui.components.common.webauthn import ConfirmInfo
@@ -7,6 +7,7 @@
from trezor.ui.layouts.lvgl.webauthn import confirm_webauthn
from .credential import Fido2Credential
+from .fido_seed import ensure_fido_seed_ready
from .resident_credentials import store_resident_credential
@@ -35,6 +36,8 @@ async def add_resident_credential(
raise wire.ProcessError("Missing credential ID parameter.")
try:
+ if utils.USE_THD89:
+ ensure_fido_seed_ready()
cred = Fido2Credential.from_cred_id(bytes(msg.credential_id), None)
except Exception:
await show_error_and_raise(
diff --git a/core/src/apps/webauthn/credential.py b/core/src/apps/webauthn/credential.py
index 2265e0ea27..42ebb48c06 100644
--- a/core/src/apps/webauthn/credential.py
+++ b/core/src/apps/webauthn/credential.py
@@ -98,6 +98,9 @@ def bogus_signature(self) -> bytes:
def hmac_secret_key(self) -> bytes | None:
return None
+ def hmac_secret_output(self, salt: bytes) -> bytes | None:
+ return None
+
def next_signature_counter(self) -> int:
return storage.device.next_u2f_counter() or 0
@@ -159,15 +162,21 @@ def generate_id(self) -> None:
data[_CRED_ID_ALGORITHM] = self.algorithm
data[_CRED_ID_CURVE] = self.curve
- key = seed.derive_slip21_node_without_passphrase(
- [b"SLIP-0022", _CRED_ID_VERSION, b"Encryption key"]
- ).key()
- iv = random.bytes(12)
- ctx = chacha20poly1305(key, iv)
- ctx.auth(self.rp_id_hash)
- ciphertext = ctx.encrypt(cbor.encode(data))
- tag = ctx.finish()
- self.id = _CRED_ID_VERSION + iv + ciphertext + tag
+ plaintext = cbor.encode(data)
+ if utils.USE_THD89:
+ self.id, slot, action = se_thd89.fido_credential_create(plaintext, False)
+ if slot != 0xFF or action != 0:
+ raise AssertionError
+ else:
+ key = seed.derive_slip21_node_without_passphrase(
+ [b"SLIP-0022", _CRED_ID_VERSION, b"Encryption key"]
+ ).key()
+ iv = random.bytes(12)
+ ctx = chacha20poly1305(key, iv)
+ ctx.auth(self.rp_id_hash)
+ ciphertext = ctx.encrypt(plaintext)
+ tag = ctx.finish()
+ self.id = _CRED_ID_VERSION + iv + ciphertext + tag
if len(self.id) > CRED_ID_MAX_LENGTH:
raise AssertionError
@@ -178,31 +187,40 @@ def from_cred_id(
) -> "Fido2Credential":
if len(cred_id) < CRED_ID_MIN_LENGTH or cred_id[0:4] != _CRED_ID_VERSION:
raise ValueError # invalid length or version
- key = seed.derive_slip21_node_without_passphrase(
- [b"SLIP-0022", cred_id[0:4], b"Encryption key"]
- ).key()
- iv = cred_id[4:16]
- ciphertext = cred_id[16:-16]
- tag = cred_id[-16:]
+ if utils.USE_THD89:
+ data = se_thd89.fido_credential_validate(cred_id, rp_id_hash)
+ else:
+ key = seed.derive_slip21_node_without_passphrase(
+ [b"SLIP-0022", cred_id[0:4], b"Encryption key"]
+ ).key()
+ iv = cred_id[4:16]
+ ciphertext = cred_id[16:-16]
+ tag = cred_id[-16:]
+
+ if rp_id_hash is None:
+ ctx = chacha20poly1305(key, iv)
+ candidate_data = ctx.decrypt(ciphertext)
+ try:
+ rp_id = cbor.decode(candidate_data)[_CRED_ID_RP_ID]
+ except Exception as e:
+ raise ValueError from e # CBOR decoding failed
+ rp_id_hash = hashlib.sha256(rp_id).digest()
- if rp_id_hash is None:
ctx = chacha20poly1305(key, iv)
+ ctx.auth(rp_id_hash)
data = ctx.decrypt(ciphertext)
- try:
- rp_id = cbor.decode(data)[_CRED_ID_RP_ID]
- except Exception as e:
- raise ValueError from e # CBOR decoding failed
- rp_id_hash = hashlib.sha256(rp_id).digest()
- ctx = chacha20poly1305(key, iv)
- ctx.auth(rp_id_hash)
- data = ctx.decrypt(ciphertext)
+ if not utils.consteq(ctx.finish(), tag):
+ raise ValueError # inauthentic ciphertext
- if not utils.consteq(ctx.finish(), tag):
- raise ValueError # inauthentic ciphertext
+ return cls.from_authenticated_plaintext(cred_id, data, rp_id_hash)
+ @classmethod
+ def from_authenticated_plaintext(
+ cls, cred_id: bytes, plaintext: bytes, rp_id_hash: bytes | None
+ ) -> "Fido2Credential":
try:
- data = cbor.decode(data)
+ data = cbor.decode(plaintext)
except Exception as e:
raise ValueError from e # CBOR decoding failed
@@ -211,7 +229,6 @@ def from_cred_id(
cred = cls()
cred.rp_id = data.get(_CRED_ID_RP_ID, None)
- cred.rp_id_hash = rp_id_hash
cred.rp_name = data.get(_CRED_ID_RP_NAME, None)
cred.user_id = data.get(_CRED_ID_USER_ID, None)
cred.user_name = data.get(_CRED_ID_USER_NAME, None)
@@ -227,10 +244,16 @@ def from_cred_id(
(_CRED_ID_ALGORITHM in data) != (_CRED_ID_CURVE in data)
or not cred.check_required_fields()
or not cred.check_data_types()
- or hashlib.sha256(cred.rp_id).digest() != rp_id_hash
):
raise ValueError # data consistency check failed
+ calculated_rp_id_hash = hashlib.sha256(cred.rp_id).digest()
+ if rp_id_hash is None:
+ rp_id_hash = calculated_rp_id_hash
+ elif calculated_rp_id_hash != rp_id_hash:
+ raise ValueError # data consistency check failed
+ cred.rp_id_hash = rp_id_hash
+
return cred
def truncate_names(self) -> None:
@@ -368,7 +391,7 @@ def bogus_signature(self) -> bytes:
def hmac_secret_key(self) -> bytes | None:
# Returns the symmetric key for the hmac-secret extension also known as CredRandom.
- if not self.hmac_secret:
+ if not self.hmac_secret or utils.USE_THD89:
return None
node = seed.derive_slip21_node_without_passphrase(
@@ -377,6 +400,20 @@ def hmac_secret_key(self) -> bytes | None:
return node.key()
+ def hmac_secret_output(self, salt: bytes) -> bytes | None:
+ if not self.hmac_secret:
+ return None
+ if utils.USE_THD89:
+ return se_thd89.fido_hmac_secret(self.id, salt)
+
+ cred_random = self.hmac_secret_key()
+ if cred_random is None:
+ return None
+ output = hmac(hmac.SHA256, cred_random, salt[:32]).digest()
+ if len(salt) == 64:
+ output += hmac(hmac.SHA256, cred_random, salt[32:]).digest()
+ return output
+
def next_signature_counter(self) -> int:
if not self.use_sign_count:
return 0
diff --git a/core/src/apps/webauthn/fido2.py b/core/src/apps/webauthn/fido2.py
index 69d98381b3..88271c4c8a 100644
--- a/core/src/apps/webauthn/fido2.py
+++ b/core/src/apps/webauthn/fido2.py
@@ -213,7 +213,9 @@
# FIDO2 configuration.
_ALLOW_FIDO2 = True
-_ALLOW_RESIDENT_CREDENTIALS = storage.device.get_se01_version() >= "1.1.5"
+_ALLOW_RESIDENT_CREDENTIALS = storage.device.get_se01_version() >= (
+ "1.3.2" if utils.USE_THD89 else "1.1.5"
+)
_ALLOW_WINK = False
# The default value of the use_sign_count flag for newly created credentials.
@@ -2073,17 +2075,12 @@ def cbor_get_assertion_hmac_secret(cred: Credential, hmac_secret: dict) -> bytes
raise CborError(_ERR_EXTENSION_FIRST)
salt = aes(aes.CBC, shared_secret).decrypt(salt_enc)
- # Get cred_random - a constant symmetric key associated with the credential.
- cred_random = cred.hmac_secret_key()
- if cred_random is None:
+ # Compute hmac-secret without exposing CredRandom outside its key domain.
+ output = cred.hmac_secret_output(salt)
+ if output is None:
# The credential does not have the hmac-secret extension enabled.
return None
- # Compute the hmac-secret output.
- output = hmac(hmac.SHA256, cred_random, salt[:32]).digest()
- if len(salt) == 64:
- output += hmac(hmac.SHA256, cred_random, salt[32:]).digest()
-
# Encrypt the hmac-secret output.
return aes(aes.CBC, shared_secret).encrypt(output)
diff --git a/core/src/apps/webauthn/fido_seed.py b/core/src/apps/webauthn/fido_seed.py
index 19f5fcb7cc..f435c472e7 100644
--- a/core/src/apps/webauthn/fido_seed.py
+++ b/core/src/apps/webauthn/fido_seed.py
@@ -1,18 +1,20 @@
+def ensure_fido_seed_ready() -> None:
+ from trezor.crypto import se_thd89
+ from utime import sleep_ms
+
+ while True:
+ try:
+ ret = se_thd89.fido_seed()
+ if ret:
+ return
+ sleep_ms(100)
+ except Exception:
+ raise Exception("Failed to generate seed.")
+
+
def ensure_fido_seed(func):
def wrapper(*args, **kwargs):
- from trezor.crypto import se_thd89
- from utime import sleep_ms
-
- while True:
- try:
- ret = se_thd89.fido_seed()
- if ret:
- break
- else:
- sleep_ms(100)
- continue
- except Exception:
- raise Exception("Failed to generate seed.")
+ ensure_fido_seed_ready()
return func(*args, **kwargs)
return wrapper
diff --git a/core/src/apps/webauthn/resident_credentials.py b/core/src/apps/webauthn/resident_credentials.py
index 116ff46f80..922c689659 100644
--- a/core/src/apps/webauthn/resident_credentials.py
+++ b/core/src/apps/webauthn/resident_credentials.py
@@ -4,12 +4,24 @@
import storage
import storage.resident_credentials
from storage.resident_credentials import MAX_RESIDENT_CREDENTIALS
+from trezor import utils
+from trezor.crypto import se_thd89
from .credential import Fido2Credential
from .fido_seed import ensure_fido_seed
RP_ID_HASH_LENGTH = const(32)
-_ALLOW_RESIDENT_CREDENTIALS = storage.device.get_se01_version() >= "1.1.5"
+_ALLOW_RESIDENT_CREDENTIALS = storage.device.get_se01_version() >= (
+ "1.3.2" if utils.USE_THD89 else "1.1.5"
+)
+
+
+def _credential_from_se(
+ index: int, cred_id: bytes, plaintext: bytes
+) -> Fido2Credential:
+ cred = Fido2Credential.from_authenticated_plaintext(cred_id, plaintext, None)
+ cred.index = index
+ return cred
def _credential_from_data(index: int, data: bytes) -> Fido2Credential:
@@ -24,6 +36,12 @@ def _credential_from_data(index: int, data: bytes) -> Fido2Credential:
def find_all() -> Iterator[Fido2Credential]:
if not _ALLOW_RESIDENT_CREDENTIALS:
return
+ if utils.USE_THD89:
+ for index in se_thd89.fido_resident_credentials_list():
+ cred_id, plaintext = se_thd89.fido_resident_credential_read(index)
+ yield _credential_from_se(index, cred_id, plaintext)
+ return
+
registered_count = storage.resident_credentials.get_fido2_counter()
if registered_count == 0:
return
@@ -41,6 +59,14 @@ def find_all() -> Iterator[Fido2Credential]:
def find_by_rp_id_hash(rp_id_hash: bytes) -> Iterator[Fido2Credential]:
if not _ALLOW_RESIDENT_CREDENTIALS:
return
+ if utils.USE_THD89:
+ for index in se_thd89.fido_resident_credentials_list():
+ cred_id, plaintext = se_thd89.fido_resident_credential_read(index)
+ cred = _credential_from_se(index, cred_id, plaintext)
+ if cred.rp_id_hash == rp_id_hash:
+ yield cred
+ return
+
for index in range(MAX_RESIDENT_CREDENTIALS):
data = storage.resident_credentials.get(index)
@@ -62,10 +88,15 @@ def get_resident_credential(index: int) -> Fido2Credential | None:
if not 0 <= index < MAX_RESIDENT_CREDENTIALS:
return None
+ if utils.USE_THD89:
+ if index not in se_thd89.fido_resident_credentials_list():
+ return None
+ cred_id, plaintext = se_thd89.fido_resident_credential_read(index)
+ return _credential_from_se(index, cred_id, plaintext)
+
data = storage.resident_credentials.get(index)
if data is None:
return None
-
return _credential_from_data(index, data)
@@ -73,6 +104,16 @@ def get_resident_credential(index: int) -> Fido2Credential | None:
def store_resident_credential(cred: Fido2Credential) -> bool:
if not _ALLOW_RESIDENT_CREDENTIALS:
return False
+ if utils.USE_THD89:
+ try:
+ slot, action = se_thd89.fido_resident_credential_import(cred.id)
+ except ValueError:
+ return False
+ if not 0 <= slot < MAX_RESIDENT_CREDENTIALS or action not in (1, 2):
+ return False
+ cred.index = slot
+ return True
+
if storage.resident_credentials.get_fido2_counter() >= MAX_RESIDENT_CREDENTIALS:
return False
diff --git a/core/src/storage/device.py b/core/src/storage/device.py
index 2805a25766..29145a3e7b 100644
--- a/core/src/storage/device.py
+++ b/core/src/storage/device.py
@@ -87,7 +87,6 @@
_SERIAL_NUMBER_VALUE: str | None = None
_DEVICE_ID_VALUE: str | None = None
_TREZOR_COMPATIBLE_VALUE: bool | None = None
-_NEEDS_BACKUP_VALUE: bool | None = None
_FIDO_SEED_GEN = False
_FIDO2_COUNTER_VALUE: int | None = None
_FIDO_ENABLED_VALUE: bool | None = None
@@ -865,6 +864,10 @@ def get_wp_cnts() -> int:
def get_fido2_counter() -> int:
global _FIDO2_COUNTER_VALUE
+ if utils.USE_THD89:
+ from trezor.crypto import se_thd89
+
+ return len(se_thd89.fido_resident_credentials_list())
if _FIDO2_COUNTER_VALUE is None:
counter = common.get(_NAMESPACE, _FIDO2_COUNTER, public=True)
if counter is None:
@@ -884,6 +887,8 @@ def get_fido2_counter() -> int:
def set_fido2_counter(value: int) -> None:
global _FIDO2_COUNTER_VALUE
+ if utils.USE_THD89:
+ raise RuntimeError("FIDO credentials are managed by the secure element")
from .resident_credentials import MAX_RESIDENT_CREDENTIALS
assert (
@@ -1001,8 +1006,7 @@ def set_language(lang: str) -> None:
def get_mnemonic_secret() -> bytes | None:
if utils.EMULATOR:
return common.get(_NAMESPACE, _MNEMONIC_SECRET)
- else:
- return config.se_export_mnemonic()
+ return None
def get_backup_type() -> BackupType:
@@ -1115,7 +1119,6 @@ def set_appdrawer_background(full_path: str) -> None:
def store_mnemonic_secret(
secret: bytes,
backup_type: BackupType | int,
- needs_backup: bool = False,
no_backup: bool = False,
identifier: int | None = None,
iteration_exponent: int | None = None,
@@ -1123,7 +1126,6 @@ def store_mnemonic_secret(
from trezor.enums import BackupType
global _NO_BACKUP_VALUE
- global _NEEDS_BACKUP_VALUE
global _INITIALIZED_VALUE
set_version(common.STORAGE_VERSION_CURRENT)
if utils.EMULATOR:
@@ -1136,31 +1138,10 @@ def store_mnemonic_secret(
config.se_import_slip39(secret, backup_type, identifier, iteration_exponent)
common.set_uint8(_NAMESPACE, _BACKUP_TYPE, backup_type)
common.set_true_or_delete(_NAMESPACE, _NO_BACKUP, no_backup)
- if not no_backup:
- set_backed_up(needs_backup)
- _NEEDS_BACKUP_VALUE = needs_backup
_NO_BACKUP_VALUE = no_backup
_INITIALIZED_VALUE = True
-def needs_backup() -> bool:
- if utils.EMULATOR:
- return common.get_bool(_NAMESPACE, _NEEDS_BACKUP)
- global _NEEDS_BACKUP_VALUE
- if _NEEDS_BACKUP_VALUE is None:
- _NEEDS_BACKUP_VALUE = config.get_needs_backup()
- return _NEEDS_BACKUP_VALUE or False
-
-
-def set_backed_up(stat: bool) -> None:
- if utils.EMULATOR:
- return common.delete(_NAMESPACE, _NEEDS_BACKUP)
- global _NEEDS_BACKUP_VALUE
- config.set_needs_backup(stat)
- _NEEDS_BACKUP_VALUE = stat
- return None
-
-
def unfinished_backup() -> bool:
global _UNFINISHED_BACKUP_VALUE
if _UNFINISHED_BACKUP_VALUE is None:
@@ -1672,7 +1653,6 @@ def clear_global_cache() -> None:
global _FLAGS_VALUE
global _UNFINISHED_BACKUP_VALUE
global _NO_BACKUP_VALUE
- global _NEEDS_BACKUP_VALUE
global _BACKUP_TYPE_VALUE
global _SAFETY_CHECK_LEVEL_VALUE
global _AIRGAP_MODE_VALUE
@@ -1713,7 +1693,6 @@ def clear_global_cache() -> None:
_FLAGS_VALUE = None
_UNFINISHED_BACKUP_VALUE = None
_NO_BACKUP_VALUE = None
- _NEEDS_BACKUP_VALUE = None
_BACKUP_TYPE_VALUE = None
_SAFETY_CHECK_LEVEL_VALUE = None
_AIRGAP_MODE_VALUE = None
diff --git a/core/src/storage/resident_credentials.py b/core/src/storage/resident_credentials.py
index c01b98443d..7898c78aab 100644
--- a/core/src/storage/resident_credentials.py
+++ b/core/src/storage/resident_credentials.py
@@ -13,6 +13,8 @@
def get(index: int) -> bytes | None:
if not 0 <= index < MAX_RESIDENT_CREDENTIALS:
raise ValueError # invalid credential index
+ if utils.USE_THD89:
+ raise RuntimeError("FIDO credentials are managed by the secure element")
return common.get(common.APP_WEBAUTHN, index + _RESIDENT_CREDENTIAL_START_KEY)
@@ -20,6 +22,8 @@ def get(index: int) -> bytes | None:
def set(index: int, data: bytes, is_overwritten: bool = False) -> None:
if not 0 <= index < MAX_RESIDENT_CREDENTIALS:
raise ValueError # invalid credential index
+ if utils.USE_THD89:
+ raise RuntimeError("FIDO credentials are managed by the secure element")
common.set(common.APP_WEBAUTHN, index + _RESIDENT_CREDENTIAL_START_KEY, data)
if not is_overwritten:
@@ -29,23 +33,28 @@ def set(index: int, data: bytes, is_overwritten: bool = False) -> None:
def delete(index: int) -> None:
if not 0 <= index < MAX_RESIDENT_CREDENTIALS:
raise ValueError # invalid credential index
+ if utils.USE_THD89:
+ se_thd89.fido_resident_credential_delete(index)
+ return
common.delete(common.APP_WEBAUTHN, index + _RESIDENT_CREDENTIAL_START_KEY)
_decrement_fido2_counter()
def delete_all() -> None:
- if device.get_fido2_counter() == 0:
- return
if utils.USE_THD89:
- se_thd89.fido_delete_all_credentials()
+ se_thd89.fido_resident_credentials_clear()
else:
+ if device.get_fido2_counter() == 0:
+ return
for i in range(MAX_RESIDENT_CREDENTIALS):
common.delete(common.APP_WEBAUTHN, i + _RESIDENT_CREDENTIAL_START_KEY)
- _reset_fido2_counter()
+ _reset_fido2_counter()
def get_fido2_counter() -> int:
+ if utils.USE_THD89:
+ return len(se_thd89.fido_resident_credentials_list())
return device.get_fido2_counter()