diff --git a/core/SConscript.unix b/core/SConscript.unix index 0d8161185..ca3c7fe75 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -422,6 +422,7 @@ SOURCE_UNIX = [ 'embed/trezorhal/unix/common.c', 'embed/trezorhal/unix/flash_otp.c', 'embed/trezorhal/unix/flash.c', + 'embed/trezorhal/unix/fwutils.c', 'embed/trezorhal/unix/random_delays.c', 'embed/trezorhal/unix/rng.c', 'embed/trezorhal/unix/systick.c', diff --git a/core/embed/extmod/modtrezorutils/modtrezorutils.c b/core/embed/extmod/modtrezorutils/modtrezorutils.c index 5babd7301..c77ed2837 100644 --- a/core/embed/extmod/modtrezorutils/modtrezorutils.c +++ b/core/embed/extmod/modtrezorutils/modtrezorutils.c @@ -31,25 +31,20 @@ #include #include "blake2s.h" #include "bootutils.h" -#include "common.h" -#include "flash.h" +#include "error_handling.h" +#include "fwutils.h" #include "unit_variant.h" #include "usb.h" #include TREZOR_BOARD #include "model.h" -#ifndef TREZOR_EMULATOR -#include "image.h" -#endif - #if USE_OPTIGA && !defined(TREZOR_EMULATOR) #include "secret.h" #endif -#define FW_HASHING_CHUNK_SIZE 1024 +static void ui_progress(void *context, uint32_t current, uint32_t total) { + mp_obj_t ui_wait_callback = (mp_obj_t)context; -static void ui_progress(mp_obj_t ui_wait_callback, uint32_t current, - uint32_t total) { if (mp_obj_is_callable(ui_wait_callback)) { mp_call_function_2_protected(ui_wait_callback, mp_obj_new_int(current), mp_obj_new_int(total)); @@ -153,51 +148,23 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorutils_halt_obj, 0, 1, /// """ STATIC mp_obj_t mod_trezorutils_firmware_hash(size_t n_args, const mp_obj_t *args) { - BLAKE2S_CTX ctx; mp_buffer_info_t chal = {0}; if (n_args > 0 && args[0] != mp_const_none) { mp_get_buffer_raise(args[0], &chal, MP_BUFFER_READ); } - if (chal.len != 0) { - if (blake2s_InitKey(&ctx, BLAKE2S_DIGEST_LENGTH, chal.buf, chal.len) != 0) { - mp_raise_msg(&mp_type_ValueError, "Invalid challenge."); - } - } else { - blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH); - } - mp_obj_t ui_wait_callback = mp_const_none; if (n_args > 1 && args[1] != mp_const_none) { ui_wait_callback = args[1]; } - uint32_t firmware_size = flash_area_get_size(&FIRMWARE_AREA); - uint32_t chunks = firmware_size / FW_HASHING_CHUNK_SIZE; - - ensure((firmware_size % FW_HASHING_CHUNK_SIZE == 0) * sectrue, - "Cannot compute FW hash."); - - ui_progress(ui_wait_callback, 0, chunks); - for (int i = 0; i < chunks; i++) { - const void *data = flash_area_get_address( - &FIRMWARE_AREA, i * FW_HASHING_CHUNK_SIZE, FW_HASHING_CHUNK_SIZE); - if (data == NULL) { - mp_raise_msg(&mp_type_RuntimeError, "Failed to read firmware."); - } - blake2s_Update(&ctx, data, FW_HASHING_CHUNK_SIZE); - if (i % 128 == 0) { - ui_progress(ui_wait_callback, i + 1, chunks); - } - } - - ui_progress(ui_wait_callback, chunks, chunks); - vstr_t vstr = {0}; vstr_init_len(&vstr, BLAKE2S_DIGEST_LENGTH); - if (blake2s_Final(&ctx, vstr.buf, vstr.len) != 0) { + + if (sectrue != firmware_calc_hash(chal.buf, chal.len, (uint8_t *)vstr.buf, + vstr.len, ui_progress, ui_wait_callback)) { vstr_clear(&vstr); - mp_raise_msg(&mp_type_RuntimeError, "Failed to finalize firmware hash."); + mp_raise_msg(&mp_type_RuntimeError, "Failed to calculate firmware hash."); } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); @@ -213,13 +180,11 @@ STATIC mp_obj_t mod_trezorutils_firmware_vendor(void) { #ifdef TREZOR_EMULATOR return mp_obj_new_str_copy(&mp_type_str, (const uint8_t *)"EMULATOR", 8); #else - vendor_header vhdr = {0}; - const void *data = flash_area_get_address(&FIRMWARE_AREA, 0, 0); - if (data == NULL || sectrue != read_vendor_header(data, &vhdr)) { + char vendor[64] = {0}; + if (sectrue != firmware_get_vendor(vendor, sizeof(vendor))) { 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, - vhdr.vstr_len); + return mp_obj_new_str_copy(&mp_type_str, (byte *)vendor, strlen(vendor)); #endif } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_firmware_vendor_obj, diff --git a/core/embed/prodtest/main.c b/core/embed/prodtest/main.c index 66114bfe7..05f0c931d 100644 --- a/core/embed/prodtest/main.c +++ b/core/embed/prodtest/main.c @@ -622,7 +622,7 @@ static void test_boardloader_version(const boardloader_version_t *version) { } static void test_wipe(void) { - invalidate_firmware(); + firmware_invalidate_header(); display_clear(); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY / 2 + 10, "WIPED", -1, FONT_BOLD, COLOR_WHITE, COLOR_BLACK); diff --git a/core/embed/trezorhal/fwutils.h b/core/embed/trezorhal/fwutils.h index 26e484c8f..b4c13626f 100644 --- a/core/embed/trezorhal/fwutils.h +++ b/core/embed/trezorhal/fwutils.h @@ -20,9 +20,43 @@ #ifndef TREZORHAL_FWUTILS_H #define TREZORHAL_FWUTILS_H +#include + +#include "secbool.h" + +// Callback function for firmware hash calculation. +typedef void (*firmware_hash_callback_t)(void* context, uint32_t progress, + uint32_t total); + +// Calculates hash of the firmware area. +// +// `challenge` is a optional pointer to the challenge data. +// `challenge_len` is the length of the challenge data (1..32). +// `hash` is a pointer to a buffer where the hash will be stored. +// `hash_len` is size of the buffer (must be at least 32). +// `callback` is an optional callback function that will be called during the +// hash calculation. +// `callback_context` is a pointer that will be passed to the callback function. +// +// Returns `sectrue` if the hash was calculated successfully, `secfalse` +// otherwise. +secbool firmware_calc_hash(const uint8_t* challenge, size_t challenge_len, + uint8_t* hash, size_t hash_len, + firmware_hash_callback_t callback, + void* callback_context); + +// Reads the firmware vendor string from the header in the firmware area. +// +// `buff` is a pointer to a buffer where the vendor string will be stored. +// `buff_size` is the length of the buffer (reserve at least 64 bytes). +// +// Returns `sectrue` if the vendor string was read successfully, `secfalse` +// otherwise. +secbool firmware_get_vendor(char* buff, size_t buff_size); + // Invalidates the firmware by erasing the first 1KB of the firmware area. // // Note: only works when write access to firmware area is enabled by MPU -void invalidate_firmware(void); +void firmware_invalidate_header(void); #endif // TREZORHAL_FWUTILS_H diff --git a/core/embed/trezorhal/stm32f4/fwutils.c b/core/embed/trezorhal/stm32f4/fwutils.c index 1d5990e11..e583203d7 100644 --- a/core/embed/trezorhal/stm32f4/fwutils.c +++ b/core/embed/trezorhal/stm32f4/fwutils.c @@ -17,15 +17,84 @@ * along with this program. If not, see . */ -#include STM32_HAL_H +#include -#include "fwutils.h" +#include "blake2s.h" #include "error_handling.h" #include "flash.h" #include "flash_area.h" +#include "fwutils.h" +#include "image.h" #include "model.h" -void invalidate_firmware(void) { +#define FW_HASHING_CHUNK_SIZE 1024 + +secbool firmware_calc_hash(const uint8_t* challenge, size_t challenge_len, + uint8_t* hash, size_t hash_len, + firmware_hash_callback_t callback, + void* callback_context) { + BLAKE2S_CTX ctx; + + if (challenge_len != 0) { + if (blake2s_InitKey(&ctx, BLAKE2S_DIGEST_LENGTH, challenge, + challenge_len) != 0) { + return secfalse; + } + } else { + blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH); + } + + uint32_t firmware_size = flash_area_get_size(&FIRMWARE_AREA); + uint32_t chunks = firmware_size / FW_HASHING_CHUNK_SIZE; + + ensure((firmware_size % FW_HASHING_CHUNK_SIZE == 0) * sectrue, + "Cannot compute FW hash."); + + for (int i = 0; i < chunks; i++) { + if (callback != NULL && (i % 128 == 0)) { + callback(callback_context, i, chunks); + } + + const void* data = flash_area_get_address( + &FIRMWARE_AREA, i * FW_HASHING_CHUNK_SIZE, FW_HASHING_CHUNK_SIZE); + + if (data == NULL) { + return secfalse; + } + + blake2s_Update(&ctx, data, FW_HASHING_CHUNK_SIZE); + } + + if (callback != NULL) { + callback(callback_context, chunks, chunks); + } + + if (blake2s_Final(&ctx, hash, hash_len) != 0) { + return secfalse; + } + + return sectrue; +} + +secbool firmware_get_vendor(char* buff, size_t buff_size) { + const void* data = flash_area_get_address(&FIRMWARE_AREA, 0, 0); + + vendor_header vhdr = {0}; + + if (data == NULL || sectrue != read_vendor_header(data, &vhdr)) { + return secfalse; + } + + if (buff == NULL || buff_size < vhdr.vstr_len + 1) { + return secfalse; + } + + strncpy(buff, vhdr.vstr, buff_size); + + return sectrue; +} + +void firmware_invalidate_header(void) { #ifdef STM32U5 // on stm32u5, we need to disable the instruction cache before erasing the // firmware - otherwise, the write check will fail diff --git a/core/embed/trezorhal/unix/fwutils.c b/core/embed/trezorhal/unix/fwutils.c new file mode 120000 index 000000000..8968a5a02 --- /dev/null +++ b/core/embed/trezorhal/unix/fwutils.c @@ -0,0 +1 @@ +../stm32f4/fwutils.c \ No newline at end of file