refactor(core): move image module to sec layer
What changed, and why it matters
This commit is a large but straightforward code reorganization: it moves the firmware image handling module from a general utility directory (embed/util/image) into a security-focused directory (embed/sec/image) and updates all include paths and build scripts accordingly. The actual code logic appears unchanged except for the path/namespace adjustments. There is no indication this fixes or introduces a security vulnerability.
No security action required. Treat as normal maintenance refactor. Reviewers may want to confirm that no functional changes were accidentally introduced during the move, but the diff shows no such changes.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change is a pure refactor: files such as boot_header.c, boot_image.c, boot_image_embdata.c, boot_ucb.c, image.c and their headers are relocated from core/embed/util/image to core/embed/sec/image. All SConscript build files and C source files are updated to reference the new include path
Changed components
core/embed/sec/imagecore/embed/util/imagecore/SConscript.*core/embed/projects/boardloadercore/embed/projects/bootloadercore/embed/projects/bootloader_cicore/embed/projects/firmwarecore/embed/projects/kernelcore/embed/projects/prodtestcore/embed/projects/secmoncore/embed/sec/fwutilscore/embed/sec/storagecore/embed/sys/smcallcore/embed/sys/syscallcore/embed/upymod/modtrezorutilsInspect captured patch +2033 / −2034
diff --git a/core/SConscript.boardloader b/core/SConscript.boardloader
index 77f96b73..a885a847 100644
--- a/core/SConscript.boardloader
+++ b/core/SConscript.boardloader
@@ -93,12 +93,12 @@ SOURCE_MOD += [
'embed/gfx/gfx_draw.c',
'embed/gfx/terminal.c',
'embed/io/display/display_utils.c',
- 'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
'embed/util/rsod/rsod_special.c',
'embed/rtl/error_handling.c',
'embed/rtl/scm_revision.c',
'embed/rtl/strutils.c',
+ 'embed/sec/image/image.c',
'embed/sys/flash/flash_utils.c',
]
@@ -153,8 +153,8 @@ env.Replace(
'embed/io/haptic/inc',
'embed/io/touch/inc',
'embed/io/usb/inc',
+ 'embed/sec/image/inc',
'embed/sys/bsp/inc',
- 'embed/util/image/inc',
'embed/util/rsod/inc',
'vendor/micropython/lib/cmsis/inc',
] + CPPPATH_MOD + PATH_HAL,
diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader
index 72562658..9b09aa68 100644
--- a/core/SConscript.bootloader
+++ b/core/SConscript.bootloader
@@ -118,12 +118,12 @@ SOURCE_MOD += [
'embed/gfx/gfx_draw.c',
'embed/gfx/terminal.c',
'embed/io/display/display_utils.c',
- 'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
'embed/util/rsod/rsod_special.c',
'embed/rtl/error_handling.c',
'embed/rtl/scm_revision.c',
'embed/rtl/strutils.c',
+ 'embed/sec/image/image.c',
'embed/sys/flash/flash_utils.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
@@ -208,8 +208,8 @@ ALLPATHS = [
'embed/models',
'embed/sys/bsp/inc',
'embed/gfx/inc',
+ 'embed/sec/image/inc',
'embed/util/translations/inc',
- 'embed/util/image/inc',
'embed/util/rsod/inc',
'embed/upymod/modtrezorui',
'vendor/nanopb',
diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci
index ebbd14b1..0817a925 100644
--- a/core/SConscript.bootloader_ci
+++ b/core/SConscript.bootloader_ci
@@ -97,12 +97,12 @@ SOURCE_MOD += [
'embed/gfx/gfx_draw.c',
'embed/gfx/terminal.c',
'embed/io/display/display_utils.c',
- 'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
'embed/util/rsod/rsod_special.c',
'embed/rtl/error_handling.c',
'embed/rtl/scm_revision.c',
'embed/rtl/strutils.c',
+ 'embed/sec/image/image.c',
'embed/sys/flash/flash_utils.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
@@ -168,8 +168,8 @@ env.Replace(
'embed/rtl/inc',
'embed/models',
'embed/gfx/inc',
+ 'embed/sec/image/inc',
'embed/sys/bsp/inc',
- 'embed/util/image/inc',
'embed/util/rsod/inc',
'embed/upymod/modtrezorui',
'vendor/nanopb',
diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu
index 45dac359..9186e5d6 100644
--- a/core/SConscript.bootloader_emu
+++ b/core/SConscript.bootloader_emu
@@ -92,12 +92,12 @@ SOURCE_MOD += [
'embed/gfx/gfx_draw.c',
'embed/gfx/terminal.c',
'embed/io/display/display_utils.c',
- 'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
'embed/util/rsod/rsod_special.c',
'embed/rtl/error_handling.c',
'embed/rtl/scm_revision.c',
'embed/rtl/strutils.c',
+ 'embed/sec/image/image.c',
'embed/sys/flash/flash_utils.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
@@ -181,7 +181,7 @@ ALLPATHS = ['embed/rust',
'embed/projects/unix',
'embed/upymod/modtrezorui',
'embed/gfx/inc',
- 'embed/util/image/inc',
+ 'embed/sec/image/inc',
'embed/util/rsod/inc',
'embed/util/translations/inc',
'vendor/nanopb',
diff --git a/core/SConscript.firmware b/core/SConscript.firmware
index c601d2c5..5e03be1a 100644
--- a/core/SConscript.firmware
+++ b/core/SConscript.firmware
@@ -260,13 +260,13 @@ SOURCE_MOD += [
'embed/gfx/gfx_draw.c',
'embed/gfx/terminal.c',
'embed/io/display/display_utils.c',
- 'embed/util/image/boot_image_embdata.c',
- 'embed/util/image/image.c',
'embed/util/translations/translations.c',
'embed/util/rsod/rsod.c',
'embed/rtl/error_handling.c',
'embed/rtl/scm_revision.c',
'embed/rtl/strutils.c',
+ 'embed/sec/image/boot_image_embdata.c',
+ 'embed/sec/image/image.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
'vendor/micropython/lib/uzlib/tinflate.c',
@@ -490,7 +490,7 @@ ALLPATHS = [
'embed/models',
'embed/gfx/inc',
'embed/sys/bsp/inc',
- 'embed/util/image/inc',
+ 'embed/sec/image/inc',
'embed/util/rsod/inc',
'embed/util/translations/inc',
'embed/upymod/modtrezorui',
diff --git a/core/SConscript.kernel b/core/SConscript.kernel
index 2e2ebe2d..8e6d9de8 100644
--- a/core/SConscript.kernel
+++ b/core/SConscript.kernel
@@ -218,12 +218,12 @@ SOURCE_MOD += [
'embed/gfx/gfx_draw.c',
'embed/gfx/terminal.c',
'embed/util/translations/translations.c',
- 'embed/util/image/boot_image.c',
- 'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
'embed/util/rsod/rsod_special.c',
'embed/rtl/error_handling.c',
'embed/rtl/strutils.c',
+ 'embed/sec/image/boot_image.c',
+ 'embed/sec/image/image.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
'vendor/micropython/lib/uzlib/tinflate.c',
@@ -314,8 +314,8 @@ ALLPATHS = [
'embed/rtl/inc',
'embed/models',
'embed/gfx/inc',
+ 'embed/sec/image/inc',
'embed/sys/bsp/inc',
- 'embed/util/image/inc',
'embed/util/rsod/inc',
'embed/util/translations/inc',
] + CPPPATH_MOD + PATH_HAL
diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest
index 6ecf369a..6c9cb527 100644
--- a/core/SConscript.prodtest
+++ b/core/SConscript.prodtest
@@ -170,8 +170,6 @@ SOURCE_MOD += [
'embed/gfx/gfx_draw.c',
'embed/gfx/terminal.c',
'embed/io/display/display_utils.c',
- 'embed/util/image/boot_image.c',
- 'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
'embed/util/rsod/rsod_special.c',
'embed/rtl/cli.c',
@@ -180,6 +178,8 @@ SOURCE_MOD += [
'embed/rtl/scm_revision.c',
'embed/rtl/strutils.c',
'embed/rtl/unit_test.c',
+ 'embed/sec/image/boot_image.c',
+ 'embed/sec/image/image.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
'vendor/micropython/lib/uzlib/tinflate.c',
@@ -256,8 +256,8 @@ ALLPATHS = [
'embed/models',
'embed/gfx/inc',
'embed/sys/bsp/inc',
+ 'embed/sec/image/inc',
'embed/sec/storage/inc',
- 'embed/util/image/inc',
'embed/util/rsod/inc',
'embed/util/translations/inc',
'embed/upymod/modtrezorui',
diff --git a/core/SConscript.prodtest_emu b/core/SConscript.prodtest_emu
index 3c53a9fe..07aafede 100644
--- a/core/SConscript.prodtest_emu
+++ b/core/SConscript.prodtest_emu
@@ -136,8 +136,6 @@ SOURCE_MOD += [
'embed/gfx/gfx_draw.c',
'embed/gfx/terminal.c',
'embed/io/display/display_utils.c',
- 'embed/util/image/boot_image.c',
- 'embed/util/image/image.c',
'embed/util/rsod/rsod.c',
'embed/util/rsod/rsod_special.c',
'embed/rtl/cli.c',
@@ -146,6 +144,8 @@ SOURCE_MOD += [
'embed/rtl/scm_revision.c',
'embed/rtl/strutils.c',
'embed/rtl/unit_test.c',
+ 'embed/sec/image/boot_image.c',
+ 'embed/sec/image/image.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
'vendor/micropython/lib/uzlib/tinflate.c',
@@ -221,7 +221,7 @@ ALLPATHS = ['embed/rust',
'embed/models',
'embed/projects/unix',
'embed/gfx/inc',
- 'embed/util/image/inc',
+ 'embed/sec/image/inc',
'embed/util/rsod/inc',
'embed/util/translations/inc',
] + CPPPATH_MOD + PATH_HAL
diff --git a/core/SConscript.secmon b/core/SConscript.secmon
index ee99f936..8f63cc81 100644
--- a/core/SConscript.secmon
+++ b/core/SConscript.secmon
@@ -214,11 +214,11 @@ if 'boot_ucb' in FEATURES_AVAILABLE:
SOURCE_MOD += [
'embed/util/translations/translations.c',
- 'embed/util/image/boot_image.c',
- 'embed/util/image/image.c',
'embed/util/rsod/rsod_special.c',
'embed/rtl/error_handling.c',
'embed/rtl/strutils.c',
+ 'embed/sec/image/boot_image.c',
+ 'embed/sec/image/image.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
'vendor/micropython/lib/uzlib/tinflate.c',
@@ -308,8 +308,8 @@ ALLPATHS = [
'embed/rtl/inc',
'embed/models',
'embed/gfx/inc',
+ 'embed/sec/image/inc',
'embed/sys/bsp/inc',
- 'embed/util/image/inc',
'embed/util/rsod/inc',
'embed/util/translations/inc',
] + CPPPATH_MOD + PATH_HAL
diff --git a/core/SConscript.unix b/core/SConscript.unix
index 84917b21..0f9730ef 100644
--- a/core/SConscript.unix
+++ b/core/SConscript.unix
@@ -243,13 +243,13 @@ SOURCE_MOD += [
'embed/gfx/gfx_draw.c',
'embed/gfx/terminal.c',
'embed/io/display/display_utils.c',
- 'embed/util/image/image.c',
'embed/util/translations/translations.c',
'embed/util/rsod/rsod.c',
'embed/util/rsod/rsod_special.c',
'embed/rtl/error_handling.c',
'embed/rtl/strutils.c',
'embed/rtl/scm_revision.c',
+ 'embed/sec/image/image.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
'vendor/micropython/lib/uzlib/tinflate.c',
@@ -498,7 +498,7 @@ ALLPATHS=['.',
'embed/projects/unix',
'embed/upymod/modtrezorui',
'embed/gfx/inc',
- 'embed/util/image/inc',
+ 'embed/sec/image/inc',
'embed/util/rsod/inc',
'embed/util/translations/inc',
'vendor/micropython',
diff --git a/core/embed/projects/boardloader/main.c b/core/embed/projects/boardloader/main.c
index b93f8395..437e1f1b 100644
--- a/core/embed/projects/boardloader/main.c
+++ b/core/embed/projects/boardloader/main.c
@@ -33,10 +33,10 @@
#include <util/rsod.h>
#ifdef USE_BOOT_UCB
-#include <util/boot_header.h>
-#include <util/boot_ucb.h>
+#include <sec/boot_header.h>
+#include <sec/boot_ucb.h>
#else
-#include <util/image.h>
+#include <sec/image.h>
#endif
#ifdef USE_PMIC
diff --git a/core/embed/projects/boardloader/sd_update.c b/core/embed/projects/boardloader/sd_update.c
index 0c403cda..94f81c13 100644
--- a/core/embed/projects/boardloader/sd_update.c
+++ b/core/embed/projects/boardloader/sd_update.c
@@ -5,11 +5,11 @@
#include <gfx/terminal.h>
#include <io/display.h>
+#include <sec/image.h>
#include <sys/bootutils.h>
#include <sys/flash.h>
#include <sys/flash_utils.h>
#include <sys/systick.h>
-#include <util/image.h>
#include "memzero.h"
diff --git a/core/embed/projects/bootloader/bootui.h b/core/embed/projects/bootloader/bootui.h
index 1fa7e073..0cac4448 100644
--- a/core/embed/projects/bootloader/bootui.h
+++ b/core/embed/projects/bootloader/bootui.h
@@ -21,7 +21,7 @@
#include <trezor_types.h>
-#include <util/image.h>
+#include <sec/image.h>
#include "rust_ui_bootloader.h"
diff --git a/core/embed/projects/bootloader/fw_check.c b/core/embed/projects/bootloader/fw_check.c
index 8e1fbef4..34360128 100644
--- a/core/embed/projects/bootloader/fw_check.c
+++ b/core/embed/projects/bootloader/fw_check.c
@@ -20,10 +20,10 @@
#include <trezor_model.h>
#include <trezor_rtl.h>
+#include <sec/image.h>
#include <sys/bootutils.h>
#include <sys/flash.h>
#include <sys/flash_otp.h>
-#include <util/image.h>
#include "fw_check.h"
#include "version_check.h"
diff --git a/core/embed/projects/bootloader/fw_check.h b/core/embed/projects/bootloader/fw_check.h
index f0aa4571..9b13a9ef 100644
--- a/core/embed/projects/bootloader/fw_check.h
+++ b/core/embed/projects/bootloader/fw_check.h
@@ -21,7 +21,7 @@
#include <trezor_types.h>
-#include <util/image.h>
+#include <sec/image.h>
/**
* @brief Firmware information collected by the bootloader when validating
diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c
index be4c29de..4706fd78 100644
--- a/core/embed/projects/bootloader/main.c
+++ b/core/embed/projects/bootloader/main.c
@@ -24,6 +24,7 @@
#include <io/display_utils.h>
#include <io/notify.h>
#include <io/usb_config.h>
+#include <sec/image.h>
#include <sec/random_delays.h>
#include <sec/secret.h>
#include <sec/unit_properties.h>
@@ -33,12 +34,11 @@
#include <sys/system.h>
#include <sys/systick.h>
#include <sys/types.h>
-#include <util/image.h>
#include <util/rsod.h>
#include <util/rsod_special.h>
#ifdef USE_BOOT_UCB
-#include <util/boot_ucb.h>
+#include <sec/boot_ucb.h>
#endif
#ifdef USE_PVD
diff --git a/core/embed/projects/bootloader/protob/protob.c b/core/embed/projects/bootloader/protob/protob.c
index aa43abd4..95635a12 100644
--- a/core/embed/projects/bootloader/protob/protob.c
+++ b/core/embed/projects/bootloader/protob/protob.c
@@ -23,8 +23,8 @@
#include <pb.h>
#include <pb_decode.h>
+#include <sec/image.h>
#include <sec/unit_properties.h>
-#include <util/image.h>
#if LOCKABLE_BOOTLOADER
#include <sec/secret.h>
diff --git a/core/embed/projects/bootloader/protob/protob.h b/core/embed/projects/bootloader/protob/protob.h
index c5c91185..2f0d47f4 100644
--- a/core/embed/projects/bootloader/protob/protob.h
+++ b/core/embed/projects/bootloader/protob/protob.h
@@ -21,7 +21,7 @@
#include <trezor_types.h>
-#include <util/image.h>
+#include <sec/image.h>
#include "pb/messages.pb.h"
diff --git a/core/embed/projects/bootloader/wire/wire_iface_usb.h b/core/embed/projects/bootloader/wire/wire_iface_usb.h
index c1b9035e..ab98377f 100644
--- a/core/embed/projects/bootloader/wire/wire_iface_usb.h
+++ b/core/embed/projects/bootloader/wire/wire_iface_usb.h
@@ -20,7 +20,7 @@
#pragma once
#include "codec_v1.h"
-#include "util/image.h"
+#include "sec/image.h"
wire_iface_t *usb_iface_init(secbool usb21_landing);
diff --git a/core/embed/projects/bootloader/workflow/wf_auto_update.c b/core/embed/projects/bootloader/workflow/wf_auto_update.c
index aea067dd..24af8cb8 100644
--- a/core/embed/projects/bootloader/workflow/wf_auto_update.c
+++ b/core/embed/projects/bootloader/workflow/wf_auto_update.c
@@ -22,7 +22,7 @@
#include <trezor_rtl.h>
#include <io/notify.h>
-#include <util/image.h>
+#include <sec/image.h>
#include "bootui.h"
#include "rust_ui_bootloader.h"
diff --git a/core/embed/projects/bootloader/workflow/wf_bootloader.c b/core/embed/projects/bootloader/workflow/wf_bootloader.c
index b92c682d..12926aff 100644
--- a/core/embed/projects/bootloader/workflow/wf_bootloader.c
+++ b/core/embed/projects/bootloader/workflow/wf_bootloader.c
@@ -21,8 +21,8 @@
#include <trezor_rtl.h>
#include <io/notify.h>
+#include <sec/image.h>
#include <sys/types.h>
-#include <util/image.h>
#ifdef USE_POWER_MANAGER
#include <io/power_manager.h>
diff --git a/core/embed/projects/bootloader/workflow/wf_empty_device.c b/core/embed/projects/bootloader/workflow/wf_empty_device.c
index f8871576..5e06c994 100644
--- a/core/embed/projects/bootloader/workflow/wf_empty_device.c
+++ b/core/embed/projects/bootloader/workflow/wf_empty_device.c
@@ -21,10 +21,10 @@
#include <trezor_rtl.h>
#include <io/notify.h>
+#include <sec/image.h>
#include <sys/flash_utils.h>
#include <sys/systick.h>
#include <sys/types.h>
-#include <util/image.h>
#ifdef USE_STORAGE_HWKEY
#include <sec/secret.h>
diff --git a/core/embed/projects/bootloader/workflow/wf_host_control.c b/core/embed/projects/bootloader/workflow/wf_host_control.c
index 4e95db4e..9fd0bef1 100644
--- a/core/embed/projects/bootloader/workflow/wf_host_control.c
+++ b/core/embed/projects/bootloader/workflow/wf_host_control.c
@@ -24,7 +24,6 @@
#include <sys/systick.h>
#include <sys/types.h>
-#include <util/image.h>
#include "protob/protob.h"
#include "wire/wire_iface_usb.h"
diff --git a/core/embed/projects/bootloader/workflow/workflow.h b/core/embed/projects/bootloader/workflow/workflow.h
index 4f649bdf..15ec4f40 100644
--- a/core/embed/projects/bootloader/workflow/workflow.h
+++ b/core/embed/projects/bootloader/workflow/workflow.h
@@ -21,7 +21,7 @@
#include <trezor_types.h>
-#include <util/image.h>
+#include <sec/image.h>
#include "protob/protob.h"
#include "workflow_common.h"
diff --git a/core/embed/projects/bootloader_ci/main.c b/core/embed/projects/bootloader_ci/main.c
index e385d9c7..169f57cd 100644
--- a/core/embed/projects/bootloader_ci/main.c
+++ b/core/embed/projects/bootloader_ci/main.c
@@ -26,6 +26,7 @@
#include <io/display.h>
#include <io/usb.h>
#include <io/usb_config.h>
+#include <sec/image.h>
#include <sec/random_delays.h>
#include <sys/bootargs.h>
#include <sys/bootutils.h>
@@ -35,7 +36,6 @@
#include <sys/sysevent.h>
#include <sys/system.h>
#include <sys/systick.h>
-#include <util/image.h>
#include <util/rsod.h>
#include "version.h"
diff --git a/core/embed/projects/bootloader_ci/messages.c b/core/embed/projects/bootloader_ci/messages.c
index 2eaad393..006722be 100644
--- a/core/embed/projects/bootloader_ci/messages.c
+++ b/core/embed/projects/bootloader_ci/messages.c
@@ -26,10 +26,10 @@
#include "pb/messages.pb.h"
#include <io/usb.h>
+#include <sec/image.h>
#include <sys/flash.h>
#include <sys/flash_utils.h>
#include <sys/sysevent.h>
-#include <util/image.h>
#include "version.h"
#include "bootui.h"
diff --git a/core/embed/projects/bootloader_ci/messages.h b/core/embed/projects/bootloader_ci/messages.h
index a4b35c49..2ee02ed2 100644
--- a/core/embed/projects/bootloader_ci/messages.h
+++ b/core/embed/projects/bootloader_ci/messages.h
@@ -22,7 +22,7 @@
#include <trezor_types.h>
-#include <util/image.h>
+#include <sec/image.h>
#define USB_TIMEOUT 500
#define USB_PACKET_SIZE 64
diff --git a/core/embed/projects/firmware/main.c b/core/embed/projects/firmware/main.c
index 3e4dcfcc..6ace23ca 100644
--- a/core/embed/projects/firmware/main.c
+++ b/core/embed/projects/firmware/main.c
@@ -35,11 +35,11 @@
#include <io/display.h>
#include <io/notify.h>
+#include <sec/boot_image.h>
#include <sys/linker_utils.h>
#include <sys/logging.h>
#include <sys/systask.h>
#include <sys/system.h>
-#include <util/boot_image.h>
#include <util/rsod.h>
#include "rust_ui_common.h"
diff --git a/core/embed/projects/kernel/main.c b/core/embed/projects/kernel/main.c
index 3e7848c6..369da76d 100644
--- a/core/embed/projects/kernel/main.c
+++ b/core/embed/projects/kernel/main.c
@@ -23,6 +23,7 @@
#include <gfx/gfx_bitblt.h>
#include <io/display.h>
#include <sec/board_capabilities.h>
+#include <sec/boot_image.h>
#include <sec/option_bytes.h>
#include <sec/random_delays.h>
#include <sec/secret.h>
@@ -35,7 +36,6 @@
#include <sys/sysevent.h>
#include <sys/system.h>
#include <sys/systick.h>
-#include <util/boot_image.h>
#include <util/rsod.h>
#ifdef USE_BUTTON
diff --git a/core/embed/projects/prodtest/cmd/prodtest_bootloader.c b/core/embed/projects/prodtest/cmd/prodtest_bootloader.c
index 97713570..1eaef139 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_bootloader.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_bootloader.c
@@ -22,14 +22,14 @@
#include <rtl/cli.h>
#include <sec/board_capabilities.h>
+#include <sec/boot_image.h>
+#include <sec/image.h>
#include <sys/mpu.h>
-#include <util/boot_image.h>
-#include <util/image.h>
#include "common.h"
#ifdef USE_BOOT_UCB
-#include <util/boot_header.h>
+#include <sec/boot_header.h>
#endif
static void prodtest_bootloader_version(cli_t *cli) {
diff --git a/core/embed/projects/secmon/main.c b/core/embed/projects/secmon/main.c
index 82cc3151..009535ce 100644
--- a/core/embed/projects/secmon/main.c
+++ b/core/embed/projects/secmon/main.c
@@ -21,6 +21,7 @@
#include <trezor_model.h>
#include <sec/board_capabilities.h>
+#include <sec/boot_image.h>
#include <sec/option_bytes.h>
#include <sec/random_delays.h>
#include <sec/secure_aes.h>
@@ -30,7 +31,6 @@
#include <sys/system.h>
#include <sys/systick.h>
#include <sys/sysutils.h>
-#include <util/boot_image.h>
#ifdef USE_BACKUP_RAM
#include <sec/backup_ram.h>
diff --git a/core/embed/sec/fwutils/fwutils.c b/core/embed/sec/fwutils/fwutils.c
index e17d5a79..71b646b7 100644
--- a/core/embed/sec/fwutils/fwutils.c
+++ b/core/embed/sec/fwutils/fwutils.c
@@ -24,9 +24,9 @@
#include <trezor_rtl.h>
#include <sec/fwutils.h>
+#include <sec/image.h>
#include <sys/flash.h>
#include <sys/systask.h>
-#include <util/image.h>
#include "blake2s.h"
diff --git a/core/embed/sec/image/boot_header.c b/core/embed/sec/image/boot_header.c
new file mode 100644
index 00000000..72770a32
--- /dev/null
+++ b/core/embed/sec/image/boot_header.c
@@ -0,0 +1,328 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef SECURE_MODE
+
+#include <trezor_model.h>
+#include <trezor_rtl.h>
+
+#include <rtl/sizedefs.h>
+#include <sec/boot_header.h>
+#include <sec/image_hash_conf.h>
+
+#include <../vendor/sphincsplus/ref/api.h>
+#include <ed25519-donna/ed25519.h>
+
+#include <version.h>
+
+#ifdef BOOTLOADER
+extern const uint8_t _bootloader_code_size;
+
+typedef union {
+ boot_header_auth_t hdr;
+ uint8_t raw[BOOT_HEADER_MAXSIZE];
+} boot_header_padded_t;
+
+__attribute__((section(".header")))
+const boot_header_padded_t g_bootloader_header = {
+ .hdr = {
+ .magic = BOOT_HEADER_MAGIC_TRZQ,
+ .hw_model = HW_MODEL,
+ .hw_revision = HW_REVISION,
+ .version =
+ {
+ .major = VERSION_MAJOR,
+ .minor = VERSION_MINOR,
+ .patch = VERSION_PATCH,
+ .build = VERSION_BUILD,
+ },
+ .fix_version =
+ {
+ .major = FIX_VERSION_MAJOR,
+ .minor = FIX_VERSION_MINOR,
+ .patch = FIX_VERSION_PATCH,
+ .build = FIX_VERSION_BUILD,
+ },
+ .min_prev_version =
+ {
+ .major = 0,
+ .minor = 0,
+ .patch = 0,
+ .build = 0,
+ },
+ .monotonic_version = BOOTLOADER_MONOTONIC_VERSION,
+ // The sigmask field is properly initialized later by headertool_pq
+ // (= 0 => no keys used for signature verification; prevents booting)
+ .sigmask = 0,
+ .header_size = BOOT_HEADER_MAXSIZE,
+ // The authenticated part size is calculated for a zero-length Merkle
+ // proof, since the Merkle proof is not known at compile time.
+ // headertool_pq must update this value later when adding the Merkle
+ // proof to the header.
+ .auth_size = BOOT_HEADER_MAXSIZE - sizeof(boot_header_merkle_proof_t) -
+ sizeof(boot_header_unauth_t),
+ .code_size = (uint32_t)&_bootloader_code_size,
+ .storage_address = STORAGE_1_START,
+ }};
+#endif
+
+static const uint8_t * const BOARDLOADER_PQ_KEYS[] = {
+#if !PRODUCTION
+ (const uint8_t*) "\xec\x01\xe6\x02\x63\x02\x4f\x7e\x71\x72\x80\x13\xb7\x31\xf7\xba\x12\x99\xf5\x18\xc2\x7b\xa3\xed\x8f\x4a\x21\x99\x74\x12\x7c\x62",
+ (const uint8_t*) "\x8a\xf8\x87\x80\x85\x94\x6e\xd8\xb1\x16\xbd\x24\xc0\xf2\xaa\xc4\x8b\x7e\x8f\x11\xbf\x06\x87\x25\xcc\xfb\xb1\x52\xab\xf7\xa4\xcd",
+#else
+ MODEL_BOARDLOADER_PQ_KEYS
+#endif
+};
+
+static const uint8_t * const BOARDLOADER_EC_KEYS[] = {
+#if !PRODUCTION
+ (const uint8_t*) "\xdb\x99\x5f\xe2\x51\x69\xd1\x41\xca\xb9\xbb\xba\x92\xba\xa0\x1f\x9f\x2e\x1e\xce\x7d\xf4\xcb\x2a\xc0\x51\x90\xf3\x7f\xcc\x1f\x9d",
+ (const uint8_t*) "\x21\x52\xf8\xd1\x9b\x79\x1d\x24\x45\x32\x42\xe1\x5f\x2e\xab\x6c\xb7\xcf\xfa\x7b\x6a\x5e\xd3\x00\x97\x96\x0e\x06\x98\x81\xdb\x12",
+#else
+ MODEL_BOARDLOADER_EC_KEYS
+#endif
+};
+
+secbool boot_header_check_signature(const boot_header_auth_t* hdr,
+ const merkle_proof_node_t* merkle_root) {
+ // Get the signature indices based on the signature mask
+ _Static_assert(ARRAY_LENGTH(BOARDLOADER_PQ_KEYS) <= 3);
+ _Static_assert(ARRAY_LENGTH(BOARDLOADER_EC_KEYS) ==
+ ARRAY_LENGTH(BOARDLOADER_PQ_KEYS));
+
+ uint8_t sigmask = hdr->sigmask;
+ uint8_t sigmask_inv = 0; // FIH
+
+ const boot_header_unauth_t* sig = boot_header_unauth_get(hdr);
+
+ for (int sig_idx = 0; sig_idx < ARRAY_LENGTH(sig->ec_signature); sig_idx++) {
+ // Get the index of the public key in the signature mask
+ int key_idx = __builtin_ctz(sigmask);
+ if (key_idx >= ARRAY_LENGTH(BOARDLOADER_PQ_KEYS)) {
+ return secfalse;
+ }
+
+ // Hash of the Merkle root and the SLH signature
+ uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
+ IMAGE_HASH_CTX ctx;
+ IMAGE_HASH_INIT(&ctx);
+ IMAGE_HASH_UPDATE(&ctx, merkle_root->bytes, sizeof(merkle_root->bytes));
+ IMAGE_HASH_UPDATE(&ctx, sig->slh_signature[sig_idx],
+ sizeof(sig->slh_signature[sig_idx]));
+ IMAGE_HASH_FINAL(&ctx, hash);
+
+ // Verify EC signature - do it before we verify the PQC signature
+ int ec_result =
+ ed25519_sign_open(hash, sizeof(hash), BOARDLOADER_EC_KEYS[key_idx],
+ sig->ec_signature[sig_idx]);
+
+ if (ec_result != 0) {
+ return secfalse;
+ }
+
+ // Verify the PQC signature
+ int pq_result = crypto_sign_verify(
+ sig->slh_signature[sig_idx], sizeof(sig->slh_signature[sig_idx]),
+ merkle_root->bytes, sizeof(merkle_root->bytes),
+ BOARDLOADER_PQ_KEYS[key_idx]);
+
+ if (pq_result != 0) {
+ return secfalse;
+ }
+
+ // Mark the key as used
+ sigmask &= ~(1 << key_idx);
+ sigmask_inv |= (1 << key_idx);
+ }
+
+ if (sigmask != 0 || sigmask_inv != hdr->sigmask) { // FIH
+ // There were more than BOOT_HEADER_SIGNATURE_COUNT public key bits
+ // set or some of the public keys in the original sigmask were not used.
+ return secfalse;
+ }
+
+ return sectrue;
+}
+
+static size_t boot_header_merkle_proof_size(
+ const boot_header_merkle_proof_t* proof) {
+ return sizeof(boot_header_merkle_proof_t) +
+ proof->node_count * sizeof(proof->nodes[0]);
+}
+
+static const boot_header_merkle_proof_t* boot_header_get_merkle_proof(
+ const boot_header_auth_t* hdr) {
+ // Check if the merkle_proof.path_len field is within the header
+ if (hdr->auth_size + sizeof(boot_header_merkle_proof_t) > hdr->header_size) {
+ return NULL;
+ }
+
+ // Merkle proof is located right after the authenticated part of the header
+ boot_header_merkle_proof_t* proof =
+ (boot_header_merkle_proof_t*)((uintptr_t)hdr + hdr->auth_size);
+
+ // Check if the path length is in reasonable limits
+ if (proof->node_count > BOOT_HEADER_MERKLE_PROOF_MAXLEN) {
+ return NULL;
+ }
+
+ size_t proof_size = boot_header_merkle_proof_size(proof);
+
+ // Check if the Merkle proof is completely within the header
+ if (hdr->auth_size + proof_size > hdr->header_size) {
+ return NULL;
+ }
+
+ return proof;
+}
+
+const boot_header_auth_t* boot_header_auth_get(uint32_t address) {
+ boot_header_auth_t* hdr = (boot_header_auth_t*)address;
+
+ // Check if the header starts with the magic
+ if (hdr->magic != BOOT_HEADER_MAGIC_TRZQ) {
+ return NULL;
+ }
+
+ // Check if the header size (= bootloader code offset) is aligned to 8K
+ // boundary (flash page size)
+ if (!IS_ALIGNED(hdr->header_size, SIZE_8K) || hdr->header_size == 0) {
+ return NULL;
+ }
+
+ // Check if the header size is in reasonable limits
+ if (hdr->header_size >= SIZE_64K) {
+ return NULL;
+ }
+
+ // Check if the authenticated part size is within the header size
+ if (hdr->auth_size >= hdr->header_size) {
+ return NULL;
+ }
+
+ // Check if the size of the authenticated part is at least the size of the
+ // authenticated boot header structure. This condition prevents updating
+ // to an image whose authenticated part is smaller than the current
+ // authenticated boot header structure.
+ if (hdr->auth_size < sizeof(boot_header_auth_t)) {
+ return NULL;
+ }
+
+ // Check if bootloader code size is within reasonable limits
+ if (hdr->code_size < SIZE_8K) {
+ return NULL;
+ }
+
+ // Check if the hardware model and revision match
+ if (hdr->hw_model != HW_MODEL || hdr->hw_revision != HW_REVISION) {
+ return secfalse;
+ }
+
+ // Check if the header contains a valid Merkle proof
+ if (NULL == boot_header_get_merkle_proof(hdr)) {
+ return NULL;
+ }
+
+ // Check if the header contains a valid unauthenticated part
+ if (NULL == boot_header_unauth_get(hdr)) {
+ return NULL;
+ }
+
+ return hdr;
+}
+
+const boot_header_unauth_t* boot_header_unauth_get(
+ const boot_header_auth_t* hdr) {
+ const boot_header_merkle_proof_t* proof = boot_header_get_merkle_proof(hdr);
+
+ if (proof == NULL) {
+ // If the Merkle proof is invalid, the unauthenticated part cannot
+ // be valid either
+ return NULL;
+ }
+
+ size_t proof_size = boot_header_merkle_proof_size(proof);
+
+ // Unauthenticated part is located right after the Merkle proof
+ boot_header_unauth_t* unauth =
+ (boot_header_unauth_t*)((uintptr_t)proof + proof_size);
+
+ // Check if the unauthenticated part is within the header
+ if (hdr->auth_size + proof_size + sizeof(boot_header_unauth_t) >
+ hdr->header_size) {
+ return NULL;
+ }
+
+ return unauth;
+}
+
+void boot_header_calc_merkle_root(const boot_header_auth_t* hdr,
+ uint32_t code_address,
+ merkle_proof_node_t* root) {
+ IMAGE_HASH_CTX ctx;
+
+ static const uint8_t prefix0[] = {0x00};
+ static const uint8_t prefix1[] = {0x01};
+
+ // Hash the bootloader code
+ IMAGE_HASH_INIT(&ctx);
+ IMAGE_HASH_UPDATE(&ctx, (const uint8_t*)code_address, hdr->code_size);
+ IMAGE_HASH_FINAL(&ctx, root->bytes);
+
+ // Hash the authenticated part of the header
+ IMAGE_HASH_INIT(&ctx);
+ IMAGE_HASH_UPDATE(&ctx, prefix0, sizeof(prefix0));
+ IMAGE_HASH_UPDATE(&ctx, (const uint8_t*)hdr, hdr->auth_size);
+ IMAGE_HASH_UPDATE(&ctx, root->bytes, sizeof(root->bytes));
+ IMAGE_HASH_FINAL(&ctx, root->bytes);
+
+ const boot_header_merkle_proof_t* proof = boot_header_get_merkle_proof(hdr);
+
+ // Add the Merkle proof nodes to the hash
+ for (size_t i = 0; i < proof->node_count; i++) {
+ const merkle_proof_node_t* node = &proof->nodes[i];
+ IMAGE_HASH_INIT(&ctx);
+ IMAGE_HASH_UPDATE(&ctx, prefix1, sizeof(prefix1));
+ if (memcmp(node, root->bytes, sizeof(root->bytes)) < 0) {
+ IMAGE_HASH_UPDATE(&ctx, node->bytes, sizeof(node->bytes));
+ IMAGE_HASH_UPDATE(&ctx, root->bytes, sizeof(root->bytes));
+ } else {
+ IMAGE_HASH_UPDATE(&ctx, root->bytes, sizeof(root->bytes));
+ IMAGE_HASH_UPDATE(&ctx, node->bytes, sizeof(node->bytes));
+ }
+ IMAGE_HASH_FINAL(&ctx, root->bytes);
+ }
+}
+
+secbool bootloader_area_needs_update(const boot_header_auth_t* hdr,
+ uint32_t code_address) {
+ boot_header_auth_t* prev_hdr = (boot_header_auth_t*)BOOTLOADER_START;
+ if (hdr->header_size == prev_hdr->header_size &&
+ hdr->code_size == prev_hdr->code_size &&
+ (memcmp(hdr, prev_hdr, hdr->header_size) == 0) &&
+ (memcmp((const uint8_t*)code_address,
+ (const uint8_t*)prev_hdr + prev_hdr->header_size,
+ hdr->code_size) == 0)) {
+ return secfalse;
+ }
+ return sectrue;
+}
+
+#endif // SECURE_MODE
diff --git a/core/embed/sec/image/boot_image.c b/core/embed/sec/image/boot_image.c
new file mode 100644
index 00000000..b3e100f1
--- /dev/null
+++ b/core/embed/sec/image/boot_image.c
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef SECURE_MODE
+
+#include <trezor_model.h>
+#include <trezor_rtl.h>
+
+#include <sec/board_capabilities.h>
+#include <sec/boot_image.h>
+#include <sec/image.h>
+#include <sec/monoctr.h>
+#include <sys/flash.h>
+#include <sys/mpu.h>
+
+#include "blake2s.h"
+#include "memzero.h"
+
+#ifdef USE_BOOT_UCB
+#include <sec/boot_header.h>
+#include <sec/boot_ucb.h>
+#else
+#include "uzlib.h"
+#endif
+
+#ifndef USE_BOOT_UCB
+
+static secbool hash_match(const uint8_t *hash, const uint8_t *hash_00,
+ const uint8_t *hash_FF) {
+ if (0 == memcmp(hash, hash_00, BLAKE2S_DIGEST_LENGTH)) return sectrue;
+ if (0 == memcmp(hash, hash_FF, BLAKE2S_DIGEST_LENGTH)) return sectrue;
+ return secfalse;
+}
+
+#define UZLIB_WINDOW_SIZE (1 << 10)
+_Static_assert(
+ UZLIB_WINDOW_SIZE >= IMAGE_HEADER_SIZE,
+ "UZLIB_WINDOW_SIZE must be at least as large as IMAGE_HEADER_SIZE");
+_Static_assert(
+ BOOTLOADER_MAXSIZE <= IMAGE_CHUNK_SIZE,
+ "BOOTLOADER_MAXSIZE must be less than or equal to IMAGE_CHUNK_SIZE");
+
+static void uzlib_prepare(struct uzlib_uncomp *decomp, uint8_t *window,
+ const void *src, uint32_t srcsize, void *dest,
+ uint32_t destsize) {
+ memzero(decomp, sizeof(struct uzlib_uncomp));
+ if (window) {
+ memzero(window, UZLIB_WINDOW_SIZE);
+ }
+ memzero(dest, destsize);
+ decomp->source = (const uint8_t *)src;
+ decomp->source_limit = decomp->source + srcsize;
+ decomp->dest = (uint8_t *)dest;
+ decomp->dest_limit = decomp->dest + destsize;
+ uzlib_uncompress_init(decomp, window, window ? UZLIB_WINDOW_SIZE : 0);
+}
+
+bool boot_image_check(const boot_image_t *image) {
+ mpu_mode_t mode = mpu_reconfig(MPU_MODE_BOOTLOADER);
+
+ // compute current bootloader hash
+ uint8_t hash[BLAKE2S_DIGEST_LENGTH];
+ const uint32_t bl_len = flash_area_get_size(&BOOTLOADER_AREA);
+ const void *bl_data = flash_area_get_address(&BOOTLOADER_AREA, 0, bl_len);
+ blake2s(bl_data, bl_len, hash, BLAKE2S_DIGEST_LENGTH);
+
+ // don't whitelist the valid bootloaders for now
+ // ensure(known_bootloader(hash, BLAKE2S_DIGEST_LENGTH), "Unknown bootloader
+ // detected");
+
+ // does the bootloader match?
+ if (sectrue == hash_match(hash, image->hash_00, image->hash_FF)) {
+ mpu_reconfig(mode);
+ return false;
+ }
+
+ mpu_reconfig(mode);
+ return true;
+}
+
+void boot_image_replace(const boot_image_t *image) {
+ const uint32_t bl_len = flash_area_get_size(&BOOTLOADER_AREA);
+ const void *bl_data = flash_area_get_address(&BOOTLOADER_AREA, 0, bl_len);
+
+ mpu_mode_t mode = mpu_reconfig(MPU_MODE_BOOTLOADER);
+
+ struct uzlib_uncomp decomp = {0};
+ uint8_t decomp_window[UZLIB_WINDOW_SIZE] = {0};
+ uint32_t decomp_out[IMAGE_HEADER_SIZE / sizeof(uint32_t)] = {0};
+
+ uzlib_prepare(&decomp, decomp_window, image->image_ptr, image->image_size,
+ decomp_out, sizeof(decomp_out));
+
+ ensure((uzlib_uncompress(&decomp) == TINF_OK) ? sectrue : secfalse,
+ "Bootloader header decompression failed");
+
+ const image_header *new_bld_hdr = read_image_header(
+ (uint8_t *)&decomp_out, BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_MAXSIZE);
+
+ ensure(new_bld_hdr == (const image_header *)&decomp_out ? sectrue : secfalse,
+ "Invalid embedded bootloader");
+
+ ensure(check_image_model(new_bld_hdr), "Incompatible embedded bootloader");
+
+ ensure(check_bootloader_header_sig(new_bld_hdr),
+ "Invalid embedded bootloader signature");
+
+ const image_header *current_bld_hdr =
+ read_image_header(bl_data, BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_MAXSIZE);
+
+ uint8_t new_bld_hash[IMAGE_HASH_DIGEST_LENGTH] = {0};
+ uint8_t new_bld_hash_expected[IMAGE_HASH_DIGEST_LENGTH] = {0};
+
+ IMAGE_HASH_CTX ctx;
+ IMAGE_HASH_INIT(&ctx);
+
+ // backup new bld header values, as it will be overwritten by uzlib
+ size_t header_offset = new_bld_hdr->hdrlen;
+ memcpy(new_bld_hash_expected, &new_bld_hdr->hashes[0],
+ IMAGE_HASH_DIGEST_LENGTH);
+ uint32_t new_bld_hw_model = new_bld_hdr->hw_model;
+ uint8_t new_bld_monotonic = new_bld_hdr->monotonic;
+
+ do {
+ uint8_t *p = (uint8_t *)decomp_out + header_offset;
+ uint32_t size = decomp.dest - (uint8_t *)decomp_out - header_offset;
+ IMAGE_HASH_UPDATE(&ctx, p, size);
+ decomp.dest = (uint8_t *)decomp_out;
+ header_offset =
+ 0; // after the first chunk, we don't need to skip the header anymore
+ } while (uzlib_uncompress(&decomp) >= 0);
+
+ IMAGE_HASH_FINAL(&ctx, new_bld_hash);
+
+ if (0 !=
+ memcmp(new_bld_hash, new_bld_hash_expected, IMAGE_HASH_DIGEST_LENGTH)) {
+ // the hash in the header does not match the computed hash
+ error_shutdown("Invalid bootloader contents");
+ }
+
+ memset(&decomp, 0, sizeof(struct uzlib_uncomp));
+
+ // cannot find valid header for current bootloader, something is wrong
+ ensure(current_bld_hdr == (const image_header *)bl_data ? sectrue : secfalse,
+ "Invalid bootloader header");
+
+ ensure(check_image_model(current_bld_hdr), "Incompatible bootloader found");
+
+ if (new_bld_monotonic < current_bld_hdr->monotonic) {
+ error_shutdown("Bootloader downgrade rejected");
+ }
+
+ uint32_t board_name = get_board_name();
+ if (board_name == 0 || strncmp((const char *)&board_name, "T2T1", 4) == 0) {
+ // no board capabilities, assume Model T
+ if ((strncmp((const char *)&new_bld_hw_model, "T2T1", 4) != 0) &&
+ (new_bld_hw_model != 0)) {
+ // reject non-model T bootloader
+ // 0 represents pre-model check bootloader
+ error_shutdown("Incompatible embedded bootloader");
+ }
+ }
+ // at this point, due to the previous check_image_model call, we know that the
+ // new_bld_hdr is
+ // meant for the same model as this firmware, so we can check the board name
+ // against the firmware hw_model.
+ else if (board_name != HW_MODEL) {
+ // reject incompatible bootloader
+ error_shutdown("Incompatible embedded bootloader");
+ }
+
+ ensure(flash_area_erase(&BOOTLOADER_AREA, NULL), NULL);
+ ensure(flash_unlock_write(), NULL);
+
+ uint32_t offset = 0;
+
+ uzlib_prepare(&decomp, decomp_window, image->image_ptr, image->image_size,
+ decomp_out, sizeof(decomp_out));
+
+ ensure((uzlib_uncompress(&decomp) == TINF_OK) ? sectrue : secfalse,
+ "Bootloader decompression failed");
+
+ do {
+ uint32_t *p = decomp_out;
+ uint32_t size = decomp.dest - (uint8_t *)decomp_out;
+ uint32_t size_padded = FLASH_ALIGN(size);
+ ensure(flash_area_write_data_padded(&BOOTLOADER_AREA, offset, p, size, 0,
+ size_padded),
+ NULL);
+ offset += size_padded;
+ decomp.dest = (uint8_t *)decomp_out;
+ } while (uzlib_uncompress(&decomp) >= 0);
+
+ if (offset < bl_len) {
+ // fill the rest of the bootloader area with 0x00
+ ensure(flash_area_write_data_padded(&BOOTLOADER_AREA, offset, NULL, 0, 0,
+ bl_len - offset),
+ NULL);
+ }
+
+ ensure(flash_lock_write(), NULL);
+
+ mpu_reconfig(mode);
+}
+
+#else
+
+bool boot_image_check(const boot_image_t *image) {
+ if (image->image_size < sizeof(boot_header_auth_t)) {
+ // Invalid image size, must be at least the size of the header
+ return false;
+ }
+
+ mpu_mode_t mode = mpu_reconfig(MPU_MODE_BOOTLOADER);
+
+ boot_header_auth_t *cur_hdr = (boot_header_auth_t *)BOOTLOADER_START;
+ boot_header_auth_t *new_hdr = (boot_header_auth_t *)image->image_ptr;
+
+ bool diff = (cur_hdr->header_size != new_hdr->header_size) ||
+ (memcmp(cur_hdr, new_hdr, cur_hdr->header_size) != 0);
+
+ mpu_restore(mode);
+
+ return diff;
+}
+
+void boot_image_replace(const boot_image_t *image) {
+ uint32_t header_address = (uint32_t)image->image_ptr;
+
+ // Check that image is big enough to hold the header at least
+ ensure(sectrue * (image->image_size >= sizeof(boot_header_auth_t)),
+ "Bootloader image too small");
+
+ // Read bootloader header
+ const boot_header_auth_t *hdr = boot_header_auth_get(header_address);
+ ensure((hdr != NULL) * sectrue, "Invalid bootloader header");
+
+ // Check the image is big enough to hold both header and code
+ ensure(sectrue * (hdr->header_size + hdr->code_size <= image->image_size),
+ "Bootloader image too small");
+
+ // Check monotonic version
+
+ mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_BOOTLOADER);
+
+ const boot_header_auth_t *old_hdr = boot_header_auth_get(BOOTLOADER_START);
+
+ ensure((old_hdr != NULL) * sectrue, "Invalid current bootloader header");
+
+ uint8_t min_monotonic_version = old_hdr->monotonic_version;
+
+ mpu_restore(mpu_mode);
+
+ ensure(sectrue * (hdr->monotonic_version >= min_monotonic_version),
+ "Bootloader downgrade rejected");
+
+ uint32_t code_address = (uint32_t)image->image_ptr + hdr->header_size;
+
+ // Calculate the Merkle root from the header and the code
+ merkle_proof_node_t merkle_root;
+ boot_header_calc_merkle_root(hdr, code_address, &merkle_root);
+
+ // Check whether the new bootloader is properly signed
+ ensure(boot_header_check_signature(hdr, &merkle_root),
+ "Invalid bootloader signature");
+
+ // Write to update control block
+ ensure(boot_ucb_write(header_address, code_address),
+ "Failed to write boot UCB");
+}
+
+#endif
+
+#endif
diff --git a/core/embed/sec/image/boot_image_embdata.c b/core/embed/sec/image/boot_image_embdata.c
new file mode 100644
index 00000000..bbc0af73
--- /dev/null
+++ b/core/embed/sec/image/boot_image_embdata.c
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <trezor_model.h>
+
+#include <sec/boot_image.h>
+
+#define CONCAT_NAME_HELPER(prefix, name, suffix) prefix##name##suffix
+#define CONCAT_NAME(name, var) CONCAT_NAME_HELPER(BOOTLOADER_, name, var)
+
+#if BOOTLOADER_QA
+// QA bootloaders
+#define BOOTLOADER_00 CONCAT_NAME(MODEL_INTERNAL_NAME_TOKEN, _QA_00)
+#define BOOTLOADER_FF CONCAT_NAME(MODEL_INTERNAL_NAME_TOKEN, _QA_FF)
+#else
+// normal bootloaders
+#define BOOTLOADER_00 CONCAT_NAME(MODEL_INTERNAL_NAME_TOKEN, _00)
+#define BOOTLOADER_FF CONCAT_NAME(MODEL_INTERNAL_NAME_TOKEN, _FF)
+#endif
+
+// symbols from bootloader.bin => bootloader.o
+extern const void bootloader_start;
+extern const void bootloader_size;
+extern const void bootloader_size;
+
+static const boot_image_t g_bootloader_image = {
+ .image_ptr = (const void *)&bootloader_start,
+ .image_size = (size_t)&bootloader_size,
+#ifndef USE_BOOT_UCB
+ .hash_00 = BOOTLOADER_00,
+ .hash_FF = BOOTLOADER_FF,
+#endif
+};
+
+const boot_image_t *boot_image_get_embdata(void) { return &g_bootloader_image; }
diff --git a/core/embed/sec/image/boot_ucb.c b/core/embed/sec/image/boot_ucb.c
new file mode 100644
index 00000000..f1b75675
--- /dev/null
+++ b/core/embed/sec/image/boot_ucb.c
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef SECURE_MODE
+
+#include <trezor_bsp.h>
+#include <trezor_model.h>
+#include <trezor_rtl.h>
+
+#include <rtl/sizedefs.h>
+#include <sec/boot_header.h>
+#include <sec/boot_ucb.h>
+#include <sec/image_hash_conf.h>
+#include <sys/flash.h>
+#include <sys/mpu.h>
+
+#define BOOT_UCB_MAGIC 0x5A8C7BF3
+
+#if defined(BOOTLOADER) || defined(BOARDLOADER)
+void adjust_to_secure_flash(uint32_t* address) {
+ if (*address < FLASH_BASE_S) {
+ // Address is in the non-secure flash region, adjust it to point to the
+ // secure flash region.
+ *address += FLASH_BASE_S - FLASH_BASE_NS;
+ }
+}
+#endif
+
+secbool boot_ucb_read(boot_ucb_t* ucb) {
+ mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_BOOTUCB);
+ *ucb = *((const boot_ucb_t*)BOOTUCB_START);
+ mpu_restore(mpu_mode);
+
+ if (ucb->magic != BOOT_UCB_MAGIC) {
+ return secfalse;
+ }
+
+#if defined(BOOTLOADER) || defined(BOARDLOADER)
+ // Addresses in the UCB block may reside in both non-secure and secure
+ // flash regions. We need to adjust them to point to the secure flash
+ // region in order to proceed.
+ adjust_to_secure_flash(&ucb->header_address);
+ adjust_to_secure_flash(&ucb->code_address);
+#endif
+
+ // Before reading the boot header fields, we need to ensure that it's
+ // located in the valid address range.
+ uint32_t min_address = NONBOARDLOADER_START;
+ uint32_t max_address = NONBOARDLOADER_START + NONBOARDLOADER_MAXSIZE;
+
+ if (ucb->header_address < min_address ||
+ ucb->header_address > max_address - sizeof(boot_header_auth_t)) {
+ return secfalse;
+ }
+
+ const boot_header_auth_t* hdr = (boot_header_auth_t*)ucb->header_address;
+
+ // Get address range where the header and code can be located.
+ // Both header and code must be inside flash area reserved for the
+ // firmware and must not overlap with the address range where
+ // the new bootloader will be written.
+ min_address = NONBOARDLOADER_START + hdr->header_size + hdr->code_size;
+ max_address = NONBOARDLOADER_START + NONBOARDLOADER_MAXSIZE;
+
+ // Check if the entire boot header is within the valid address range
+ if (ucb->header_address < min_address || hdr->header_size > max_address ||
+ ucb->header_address > max_address - hdr->header_size) {
+ return secfalse;
+ }
+
+ // Check if code (if present) is within the valid address range
+ if (ucb->code_address != 0) {
+ if (ucb->code_address < min_address || hdr->code_size > max_address ||
+ ucb->code_address > max_address - hdr->code_size) {
+ return secfalse;
+ }
+ }
+
+ uint8_t hash[IMAGE_HASH_DIGEST_LENGTH]; // Hash of the header
+
+ IMAGE_HASH_CTX ctx;
+ IMAGE_HASH_INIT(&ctx);
+ IMAGE_HASH_UPDATE(&ctx, (const uint8_t*)hdr, hdr->header_size);
+ IMAGE_HASH_FINAL(&ctx, hash);
+
+ if (memcmp(hash, ucb->hash, sizeof(hash)) != 0) {
+ // Header hash does not match the one stored in UCB
+ // This can happen if the header was modified after the UCB was written.
+ return secfalse;
+ }
+
+ return sectrue;
+}
+
+secbool boot_ucb_write(uint32_t header_address, uint32_t code_address) {
+ boot_ucb_t ucb = {
+ .magic = BOOT_UCB_MAGIC,
+ .header_address = header_address,
+ .code_address = code_address,
+ };
+
+ // Calculate the hash of the header
+ boot_header_auth_t* hdr = (boot_header_auth_t*)header_address;
+
+ IMAGE_HASH_CTX ctx;
+ IMAGE_HASH_INIT(&ctx);
+ IMAGE_HASH_UPDATE(&ctx, (const uint8_t*)hdr, hdr->header_size);
+ IMAGE_HASH_FINAL(&ctx, ucb.hash);
+
+ secbool result = secfalse;
+
+ mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_BOOTUCB);
+
+ // Erase the UCB area
+ if (sectrue != flash_area_erase(&BOOTUCB_AREA, NULL)) {
+ goto cleanup;
+ }
+
+ if (sectrue != flash_unlock_write()) {
+ goto cleanup;
+ }
+
+ // Write the UCB
+ if (sectrue !=
+ flash_area_write_data(&BOOTUCB_AREA, 0, (const void*)&ucb, sizeof(ucb))) {
+ goto cleanup;
+ }
+
+ ensure(flash_lock_write(), NULL);
+
+ result = sectrue;
+
+cleanup:
+
+ mpu_restore(mpu_mode);
+ return result;
+}
+
+secbool boot_ucb_erase(void) {
+ mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_BOOTUCB);
+ secbool result = flash_area_is_erased(&BOOTUCB_AREA);
+ if (sectrue != result) {
+ result = flash_area_erase(&BOOTUCB_AREA, NULL);
+ }
+ mpu_restore(mpu_mode);
+ return result;
+}
+
+#endif // SECURE_MODE
diff --git a/core/embed/sec/image/image.c b/core/embed/sec/image/image.c
new file mode 100644
index 00000000..922b19c2
--- /dev/null
+++ b/core/embed/sec/image/image.c
@@ -0,0 +1,511 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <trezor_model.h>
+#include <trezor_rtl.h>
+
+#include "ed25519-donna/ed25519.h"
+
+#include <sec/image.h>
+#include <sys/bootutils.h>
+#include <sys/flash.h>
+
+#ifdef STM32F4
+_Static_assert(BOOTLOADER_VECTBL_OFFSET == IMAGE_HEADER_SIZE,
+ "BOOTLOADER_VECTBL_OFFSET must match IMAGE_HEADER_SIZE");
+#endif
+
+_Static_assert(VENDOR_HEADER_MAX_SIZE + IMAGE_HEADER_SIZE <= IMAGE_CHUNK_SIZE,
+ "The size of the firmware headers must be less than or equal to "
+ "IMAGE_CHUNK_SIZE");
+
+const uint8_t BOARDLOADER_KEY_M = 2;
+const uint8_t BOARDLOADER_KEY_N = 3;
+static const uint8_t * const BOARDLOADER_KEYS[] = {
+#if !PRODUCTION
+ (const uint8_t *)"\xdb\x99\x5f\xe2\x51\x69\xd1\x41\xca\xb9\xbb\xba\x92\xba\xa0\x1f\x9f\x2e\x1e\xce\x7d\xf4\xcb\x2a\xc0\x51\x90\xf3\x7f\xcc\x1f\x9d",
+ (const uint8_t *)"\x21\x52\xf8\xd1\x9b\x79\x1d\x24\x45\x32\x42\xe1\x5f\x2e\xab\x6c\xb7\xcf\xfa\x7b\x6a\x5e\xd3\x00\x97\x96\x0e\x06\x98\x81\xdb\x12",
+ (const uint8_t *)"\x22\xfc\x29\x77\x92\xf0\xb6\xff\xc0\xbf\xcf\xdb\x7e\xdb\x0c\x0a\xa1\x4e\x02\x5a\x36\x5e\xc0\xe3\x42\xe8\x6e\x38\x29\xcb\x74\xb6",
+#else
+ MODEL_BOARDLOADER_KEYS
+#endif
+};
+
+const uint8_t BOOTLOADER_KEY_M = 2;
+const uint8_t BOOTLOADER_KEY_N = 3;
+static const uint8_t * const BOOTLOADER_KEYS[] = {
+#if !PRODUCTION
+ /*** DEVEL/QA KEYS ***/
+ (const uint8_t *)"\xd7\x59\x79\x3b\xbc\x13\xa2\x81\x9a\x82\x7c\x76\xad\xb6\xfb\xa8\xa4\x9a\xee\x00\x7f\x49\xf2\xd0\x99\x2d\x99\xb8\x25\xad\x2c\x48",
+ (const uint8_t *)"\x63\x55\x69\x1c\x17\x8a\x8f\xf9\x10\x07\xa7\x47\x8a\xfb\x95\x5e\xf7\x35\x2c\x63\xe7\xb2\x57\x03\x98\x4c\xf7\x8b\x26\xe2\x1a\x56",
+ (const uint8_t *)"\xee\x93\xa4\xf6\x6f\x8d\x16\xb8\x19\xbb\x9b\xeb\x9f\xfc\xcd\xfc\xdc\x14\x12\xe8\x7f\xee\x6a\x32\x4c\x2a\x99\xa1\xe0\xe6\x71\x48",
+#else
+ MODEL_BOOTLOADER_KEYS
+#endif
+};
+
+#ifdef USE_SECMON_VERIFICATION
+const uint8_t SECMON_KEY_M = 2;
+const uint8_t SECMON_KEY_N = 3;
+static const uint8_t * const SECMON_KEYS[] = {
+#if !PRODUCTION
+ /*** DEVEL/QA KEYS ***/
+ (const uint8_t *)"\xdb\x99\x5f\xe2\x51\x69\xd1\x41\xca\xb9\xbb\xba\x92\xba\xa0\x1f\x9f\x2e\x1e\xce\x7d\xf4\xcb\x2a\xc0\x51\x90\xf3\x7f\xcc\x1f\x9d",
+ (const uint8_t *)"\x21\x52\xf8\xd1\x9b\x79\x1d\x24\x45\x32\x42\xe1\x5f\x2e\xab\x6c\xb7\xcf\xfa\x7b\x6a\x5e\xd3\x00\x97\x96\x0e\x06\x98\x81\xdb\x12",
+ (const uint8_t *)"\x22\xfc\x29\x77\x92\xf0\xb6\xff\xc0\xbf\xcf\xdb\x7e\xdb\x0c\x0a\xa1\x4e\x02\x5a\x36\x5e\xc0\xe3\x42\xe8\x6e\x38\x29\xcb\x74\xb6",
+#else
+ MODEL_SECMON_KEYS
+#endif
+};
+#endif
+
+static secbool compute_pubkey(uint8_t sig_m, uint8_t sig_n,
+ const uint8_t *const *pub, uint8_t sigmask,
+ ed25519_public_key res) {
+ if (0 == sig_m || 0 == sig_n) return secfalse;
+ if (sig_m > sig_n) return secfalse;
+
+ // discard bits higher than sig_n
+ sigmask &= ((1 << sig_n) - 1);
+
+ // remove if number of set bits in sigmask is not equal to sig_m
+ if (__builtin_popcount(sigmask) != sig_m) return secfalse;
+
+ ed25519_public_key keys[sig_m];
+ int j = 0;
+ for (int i = 0; i < sig_n; i++) {
+ if ((1 << i) & sigmask) {
+ memcpy(keys[j], pub[i], 32);
+ j++;
+ }
+ }
+
+ return sectrue * (0 == ed25519_cosi_combine_publickeys(res, keys, sig_m));
+}
+
+const image_header *read_image_header(const uint8_t *const data,
+ const uint32_t magic,
+ const uint32_t maxsize) {
+ const image_header *hdr = (const image_header *)data;
+
+ if (hdr->magic != magic) {
+ return NULL;
+ }
+ if (hdr->hdrlen != IMAGE_HEADER_SIZE) {
+ return NULL;
+ }
+
+ // TODO: expiry mechanism needs to be ironed out before production or those
+ // devices won't accept expiring bootloaders (due to boardloader write
+ // protection).
+ // lowest bit is used for breaking compatibility between old TT bootloaders
+ // and non TT images
+ // which is evaluated in check_image_model function
+ if ((hdr->expiry & 0xFFFFFFFE) != 0) return NULL;
+
+ if (hdr->codelen > (maxsize - hdr->hdrlen)) return NULL;
+ if ((hdr->hdrlen + hdr->codelen) < 4 * 1024) return NULL;
+ if ((hdr->hdrlen + hdr->codelen) % 512 != 0) return NULL;
+
+ return hdr;
+}
+
+secbool check_image_model(const image_header *const hdr) {
+ // abusing expiry field to break compatibility of non-TT images with existing
+ // bootloaders/boardloaders
+#ifdef TREZOR_MODEL_T2T1
+ if (hdr->expiry == 0 && hdr->hw_model == 0 && hdr->hw_revision == 0) {
+ // images for model TT older than this check
+ return sectrue;
+ }
+#else
+ if ((hdr->expiry & 0x01) == 0) {
+ // for models other than TT, expiry == 0 is unacceptable, as the image will
+ // run on bootloaders older that this check
+ return secfalse;
+ }
+#endif
+
+#ifndef TREZOR_EMULATOR
+ if (hdr->hw_model != HW_MODEL) {
+ return secfalse;
+ }
+ if (hdr->hw_revision != HW_REVISION) {
+ return secfalse;
+ }
+#endif
+
+ return sectrue;
+}
+
+void get_image_fingerprint(const image_header *const hdr, uint8_t *const out) {
+ IMAGE_HASH_CTX ctx;
+ IMAGE_HASH_INIT(&ctx);
+ IMAGE_HASH_UPDATE(&ctx, (uint8_t *)hdr, IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE);
+ for (int i = 0; i < IMAGE_SIG_SIZE; i++) {
+ IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)"\x00", 1);
+ }
+ IMAGE_HASH_FINAL(&ctx, out);
+}
+
+secbool check_image_header_sig(const image_header *const hdr, uint8_t key_m,
+ uint8_t key_n, const uint8_t *const *keys) {
+ // check header signature
+
+ uint8_t fingerprint[32];
+ get_image_fingerprint(hdr, fingerprint);
+
+ ed25519_public_key pub;
+ if (sectrue != compute_pubkey(key_m, key_n, keys, hdr->sigmask, pub))
+ return secfalse;
+
+ return sectrue *
+ (0 == ed25519_sign_open(fingerprint, IMAGE_HASH_DIGEST_LENGTH, pub,
+ *(const ed25519_signature *)hdr->sig));
+}
+
+#ifdef USE_SECMON_VERIFICATION
+const secmon_header_t *read_secmon_header(const uint8_t *const data,
+ const uint32_t maxsize) {
+ const secmon_header_t *hdr = (const secmon_header_t *)data;
+
+ if (hdr->magic != SECMON_IMAGE_MAGIC) {
+ return NULL;
+ }
+ if (hdr->hdrlen != SECMON_HEADER_SIZE) {
+ return NULL;
+ }
+
+ if (hdr->codelen > (maxsize - hdr->hdrlen)) return secfalse;
+ if ((hdr->hdrlen + hdr->codelen) < 4 * 1024) return secfalse;
+ if ((hdr->hdrlen + hdr->codelen) % 512 != 0) return secfalse;
+
+ return hdr;
+}
+
+secbool check_secmon_model(const secmon_header_t *const hdr) {
+#ifndef TREZOR_EMULATOR
+ if (hdr->hw_model != HW_MODEL) {
+ return secfalse;
+ }
+ if (hdr->hw_revision != HW_REVISION) {
+ return secfalse;
+ }
+#endif
+
+ return sectrue;
+}
+
+void get_secmon_fingerprint(const secmon_header_t *const hdr,
+ uint8_t *const out) {
+ IMAGE_HASH_CTX ctx;
+ IMAGE_HASH_INIT(&ctx);
+ IMAGE_HASH_UPDATE(&ctx, (uint8_t *)hdr, SECMON_HEADER_SIZE - IMAGE_SIG_SIZE);
+ for (int i = 0; i < IMAGE_SIG_SIZE; i++) {
+ IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)"\x00", 1);
+ }
+ IMAGE_HASH_FINAL(&ctx, out);
+}
+
+secbool check_secmon_header_sig(const secmon_header_t *const hdr) {
+ // check header signature
+
+ uint8_t fingerprint[32];
+ get_secmon_fingerprint(hdr, fingerprint);
+
+ ed25519_public_key pub;
+ if (sectrue != compute_pubkey(SECMON_KEY_M, SECMON_KEY_N, SECMON_KEYS,
+ hdr->sigmask, pub))
+ return secfalse;
+
+ return sectrue *
+ (0 == ed25519_sign_open(fingerprint, IMAGE_HASH_DIGEST_LENGTH, pub,
+ *(const ed25519_signature *)hdr->sig));
+}
+
+#ifdef SECURE_MODE
+secbool check_secmon_contents(const secmon_header_t *const hdr,
+ size_t code_offset, const flash_area_t *area) {
+ if (0 == area) {
+ return secfalse;
+ }
+
+ // Check the secmon integrity, calculate and compare hash
+ const void *data = flash_area_get_address(
+ area, code_offset + SECMON_HEADER_SIZE, hdr->codelen);
+ if (!data) {
+ return secfalse;
+ }
+
+ if (sectrue != check_single_hash(hdr->hash, data, hdr->codelen)) {
+ return secfalse;
+ }
+
+ return sectrue;
+}
+#endif // SECURE_MODE
+
+#endif // USE_SECMON_VERIFICATION
+
+secbool __wur read_vendor_header(const uint8_t *const data,
+ vendor_header *const vhdr) {
+ memcpy(&vhdr->magic, data, 4);
+ if (vhdr->magic != 0x565A5254) return secfalse; // TRZV
+
+ memcpy(&vhdr->hdrlen, data + 4, 4);
+ if (vhdr->hdrlen > VENDOR_HEADER_MAX_SIZE) return secfalse;
+
+ memcpy(&vhdr->expiry, data + 8, 4);
+ if (vhdr->expiry != 0) return secfalse;
+
+ vhdr->origin = data;
+
+ memcpy(&vhdr->version, data + 12, 2);
+
+ memcpy(&vhdr->vsig_m, data + 14, 1);
+ memcpy(&vhdr->vsig_n, data + 15, 1);
+ memcpy(&vhdr->vtrust, data + 16, 2);
+ memcpy(&vhdr->hw_model, data + 18, 4);
+ memcpy(&vhdr->fw_type, data + 22, 1);
+
+ if (vhdr->vsig_n > MAX_VENDOR_PUBLIC_KEYS) {
+ return secfalse;
+ }
+
+ for (int i = 0; i < vhdr->vsig_n; i++) {
+ vhdr->vpub[i] = data + 32 + i * 32;
+ }
+ for (int i = vhdr->vsig_n; i < MAX_VENDOR_PUBLIC_KEYS; i++) {
+ vhdr->vpub[i] = 0;
+ }
+
+ memcpy(&vhdr->vstr_len, data + 32 + vhdr->vsig_n * 32, 1);
+
+ vhdr->vstr = (const char *)(data + 32 + vhdr->vsig_n * 32 + 1);
+
+ vhdr->vimg = data + 32 + vhdr->vsig_n * 32 + 1 + vhdr->vstr_len;
+ // align to 4 bytes
+ vhdr->vimg += (-(uintptr_t)vhdr->vimg) & 3;
+
+ memcpy(&vhdr->sigmask, data + vhdr->hdrlen - IMAGE_SIG_SIZE, 1);
+
+ memcpy(vhdr->sig, data + vhdr->hdrlen - IMAGE_SIG_SIZE + 1,
+ IMAGE_SIG_SIZE - 1);
+
+ return sectrue;
+}
+
+secbool check_vendor_header_model(const vendor_header *const vhdr) {
+#ifdef TREZOR_MODEL_T2T1
+ if (vhdr->hw_model == 0) {
+ // vendor headers for model T have this field set to 0
+ return sectrue;
+ }
+#endif
+ if (vhdr->hw_model == HW_MODEL) {
+ return sectrue;
+ }
+
+ return secfalse;
+}
+
+secbool check_vendor_header_sig(const vendor_header *const vhdr, uint8_t key_m,
+ uint8_t key_n, const uint8_t *const *keys) {
+ if (vhdr == NULL) {
+ return secfalse;
+ }
+
+ // check header signature
+
+ uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
+ IMAGE_HASH_CTX ctx;
+ IMAGE_HASH_INIT(&ctx);
+ IMAGE_HASH_UPDATE(&ctx, vhdr->origin, vhdr->hdrlen - IMAGE_SIG_SIZE);
+ for (int i = 0; i < IMAGE_SIG_SIZE; i++) {
+ IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)"\x00", 1);
+ }
+ IMAGE_HASH_FINAL(&ctx, hash);
+
+ ed25519_public_key pub;
+ if (sectrue != compute_pubkey(key_m, key_n, keys, vhdr->sigmask, pub))
+ return secfalse;
+
+ return sectrue *
+ (0 == ed25519_sign_open(hash, IMAGE_HASH_DIGEST_LENGTH, pub,
+ *(const ed25519_signature *)vhdr->sig));
+}
+
+secbool check_vendor_header_keys(const vendor_header *const vhdr) {
+ return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N,
+ BOOTLOADER_KEYS);
+}
+
+void vendor_header_hash(const vendor_header *const vhdr, uint8_t *hash) {
+ IMAGE_HASH_CTX ctx;
+ IMAGE_HASH_INIT(&ctx);
+ IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)vhdr->vstr, vhdr->vstr_len);
+ IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)"Trezor Vendor Header", 20);
+ IMAGE_HASH_FINAL(&ctx, hash);
+}
+
+secbool check_single_hash(const uint8_t *const hash, const uint8_t *const data,
+ int len) {
+ uint8_t s_c[IMAGE_HASH_DIGEST_LENGTH] = {0};
+
+ IMAGE_HASH_CALC(data, len, s_c);
+
+ return sectrue * (0 == memcmp(s_c, hash, IMAGE_HASH_DIGEST_LENGTH));
+}
+
+#ifdef KERNEL_MODE
+secbool check_image_contents(const image_header *const hdr, uint32_t firstskip,
+ const flash_area_t *area) {
+ if (0 == area) {
+ return secfalse;
+ }
+
+ // Check the firmware integrity, calculate and compare hashes
+
+ // check hashes of image chunks
+ // we hash the image including the padding to the end of the area
+ size_t offset = firstskip;
+ size_t end_offset = offset + hdr->codelen;
+
+ while (offset < end_offset) {
+ size_t bytes_to_check = MIN(IMAGE_CHUNK_SIZE - (offset % IMAGE_CHUNK_SIZE),
+ end_offset - offset);
+
+ const void *data = flash_area_get_address(area, offset, bytes_to_check);
+ if (!data) {
+ return secfalse;
+ }
+
+ size_t hash_offset = (offset / IMAGE_CHUNK_SIZE) * 32;
+ if (sectrue !=
+ check_single_hash(hdr->hashes + hash_offset, data, bytes_to_check)) {
+ return secfalse;
+ }
+
+ offset += bytes_to_check;
+ }
+
+ // Check the padding to the end of the area
+ end_offset = flash_area_get_size(area);
+
+ if (offset < end_offset) {
+ // Use the first byte in the checked area as the expected padding byte
+ // Firmware is always padded with 0xFF, while the bootloader might be
+ // padded with 0x00 as well
+ uint8_t expected_byte = *(
+ (const uint8_t *)flash_area_get_address(area, offset, sizeof(uint8_t)));
+
+ if (expected_byte != 0x00 && expected_byte != 0xFF) {
+ return secfalse;
+ }
+
+ uint32_t expected_word = expected_byte << 24 | expected_byte << 16 |
+ expected_byte << 8 | expected_byte;
+
+ while (offset < end_offset) {
+ size_t bytes_to_check = MIN(
+ IMAGE_CHUNK_SIZE - (offset % IMAGE_CHUNK_SIZE), end_offset - offset);
+ size_t words_to_check = bytes_to_check / sizeof(uint32_t);
+ size_t single_bytes_to_check = bytes_to_check % sizeof(uint32_t);
+
+ const uint8_t *bytes = (const uint8_t *)flash_area_get_address(
+ area, offset, single_bytes_to_check);
+ if (!bytes) {
+ return secfalse;
+ }
+
+ for (size_t i = 0; i < single_bytes_to_check; i++) {
+ if (bytes[i] != expected_byte) {
+ return secfalse;
+ }
+ }
+
+ offset += single_bytes_to_check;
+
+ const uint32_t *data = (const uint32_t *)flash_area_get_address(
+ area, offset, bytes_to_check - single_bytes_to_check);
+ if (!data) {
+ return secfalse;
+ }
+
+ for (size_t i = 0; i < words_to_check; i++) {
+ if (data[i] != expected_word) {
+ return secfalse;
+ }
+ }
+
+ offset += words_to_check * sizeof(uint32_t);
+ }
+ }
+
+ return sectrue;
+}
+#endif // KERNEL_MODE
+
+secbool check_firmware_header(const uint8_t *header, size_t header_size,
+ firmware_header_info_t *info) {
+ // parse and check vendor header
+ vendor_header vhdr;
+ if (sectrue != read_vendor_header(header, &vhdr)) {
+ return secfalse;
+ }
+ if (sectrue != check_vendor_header_keys(&vhdr)) {
+ return secfalse;
+ }
+
+ // parse and check image header
+ const image_header *ihdr;
+ if ((ihdr = read_image_header(header + vhdr.hdrlen, FIRMWARE_IMAGE_MAGIC,
+ FIRMWARE_MAXSIZE)) == NULL) {
+ return secfalse;
+ }
+ if (sectrue !=
+ check_image_header_sig(ihdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub)) {
+ return secfalse;
+ }
+
+ // copy vendor string
+ info->vstr_len = MIN(sizeof(info->vstr), vhdr.vstr_len);
+ if (info->vstr_len > 0) {
+ memcpy(info->vstr, vhdr.vstr, info->vstr_len);
+ }
+
+ // copy firmware version
+ info->ver_major = ihdr->version & 0xFF;
+ info->ver_minor = (ihdr->version >> 8) & 0xFF;
+ info->ver_patch = (ihdr->version >> 16) & 0xFF;
+ info->ver_build = (ihdr->version >> 24) & 0xFF;
+
+ // calculate and copy the image fingerprint
+ get_image_fingerprint(ihdr, info->fingerprint);
+
+ // calculate hash of both vendor and image headers
+ IMAGE_HASH_CALC(header, vhdr.hdrlen + ihdr->hdrlen, info->hash);
+
+ return sectrue;
+}
+
+secbool check_bootloader_header_sig(const image_header *const hdr) {
+ return check_image_header_sig(hdr, BOARDLOADER_KEY_M, BOARDLOADER_KEY_N,
+ BOARDLOADER_KEYS);
+}
diff --git a/core/embed/sec/image/inc/sec/boot_header.h b/core/embed/sec/image/inc/sec/boot_header.h
new file mode 100644
index 00000000..8c036542
--- /dev/null
+++ b/core/embed/sec/image/inc/sec/boot_header.h
@@ -0,0 +1,204 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <trezor_types.h>
+
+// Magic number at the start of the boot header
+#define BOOT_HEADER_MAGIC_TRZQ 0x515A5254 // TRZQ
+// Reserved space for boot header
+#define BOOT_HEADER_MAXSIZE (2 * 8192)
+/** Number of PQ/EC signatures */
+#define BOOT_HEADER_SIGNATURE_COUNT 2
+/** Length of PQ signature in bytes */
+#define BOOT_HEADER_PQ_SIGNATURE_LEN (7856)
+/** Length of EC signature in bytes */
+#define BOOT_HEADER_EC_SIGNATURE_LEN (64)
+/** Number of reserved slots for Merkle proof */
+#define BOOT_HEADER_MERKLE_PROOF_MAXLEN (256)
+
+/**
+ * 4-byte version structure used in the boot header
+ */
+typedef struct __attribute__((packed)) {
+ uint8_t major;
+ uint8_t minor;
+ uint8_t patch;
+ uint8_t build;
+} boot_header_version_t;
+
+/**
+ * Merkle proof node (SHA-256 digest)
+ */
+typedef struct {
+ uint8_t bytes[32];
+} merkle_proof_node_t;
+
+/**
+ * Authenticated part of the boot header
+ *
+ * This structure can be extended in future versions if needed.
+ * Just make sure to add new fields at the end of the structure.
+ * Never remove or reorder existing fields.
+ */
+typedef struct __attribute__((packed)) {
+ /** Magic constant 'TRZQ' */
+ uint32_t magic;
+ /** Hardware model, e.g. 'T3W1'. */
+ uint32_t hw_model;
+ /** Hardware revision, e.g. 1 */
+ uint32_t hw_revision;
+ /** Bootloader version */
+ boot_header_version_t version;
+ /** Minimum version that the device can be downgraded to without
+ * erasing storage. */
+ boot_header_version_t fix_version;
+ /** Minimum previous version that the device can be updated from when
+ * installing this header. */
+ boot_header_version_t min_prev_version;
+ /** An integer which must not decrease between updates. */
+ uint8_t monotonic_version;
+ /** Bitmask of keys used for signature verification.
+ * Each bit corresponds to a public key in the BOOTLOADER_PQ_KEY and
+ * BOOTLOADER_EC_KEY arrays. If the bit is set, the corresponding key
+ * is used for signature verification. */
+ uint8_t sigmask;
+ /* Reserved bytes (alignment) */
+ uint8_t reserved[2];
+ /** Size of the entire header in bytes, including the Merkle proof
+ * and signatures. It's calculated at link time and must be
+ * aligned to 8K boundary. */
+ uint32_t header_size;
+ /** Size of the authenticated part of the header in bytes.
+ * Final value is calculated in post-build step and includes
+ * potential padding of the structure. */
+ uint32_t auth_size;
+ /** Size of the bootloader code in bytes */
+ uint32_t code_size;
+ /** Address of storage area for storage relocation purposes */
+ uint32_t storage_address;
+ /* Firmware Merkle tree root */
+ merkle_proof_node_t firmware_root;
+ /* Padding is automatically added by the post-build step to ensure that
+ * the authenticated part of the header is maximized. */
+ uint8_t padding[0];
+
+} boot_header_auth_t;
+
+/**
+ * Merkle proof structure used in the boot header to calculate the root
+ * of the Merkle tree. It is placed just after the authenticated part
+ * of the boot header.
+ *
+ * WARNING: This struct must not change, otherwise old boardloaders will
+ * not interpret the header correctly. Any new unauthenticated fields
+ * must be added at the end of `boot_header_unauth_t`.
+ */
+typedef struct __attribute__((packed)) {
+ /** Number of nodes in the array below */
+ uint32_t node_count;
+ /** Merkle proof used for root calculation */
+ merkle_proof_node_t nodes[0];
+
+} boot_header_merkle_proof_t;
+
+/**
+ * Unauthenticated part of the boot header containing signatures and
+ * other information that need not (or must not) be authenticated.
+ * It is placed right after the Merkle proof.
+ */
+typedef struct __attribute__((packed)) {
+ /** PQ signatures */
+ uint8_t slh_signature[BOOT_HEADER_SIGNATURE_COUNT]
+ [BOOT_HEADER_PQ_SIGNATURE_LEN];
+ /** EC signatures */
+ uint8_t ec_signature[BOOT_HEADER_SIGNATURE_COUNT]
+ [BOOT_HEADER_EC_SIGNATURE_LEN];
+
+ /* Firmware type
+ * This field is modified by the bootloader during the
+ * update process). It indicates the current firmware type (custom,
+ * universal, bitcoin-only, etc.) and is used to determine whether
+ * the storage should be erased before the update.
+ */
+ uint8_t firmware_type;
+ uint8_t padding[3];
+
+} boot_header_unauth_t;
+
+/**
+ * Verifies the integrity of the boot header.
+ *
+ * Checks the magic number, header size, code size, hardware model and revision
+ *
+ * @param address Address of the boot header in flash memory
+ * @return Pointer to the boot header if valid, NULL otherwise.
+ */
+const boot_header_auth_t* boot_header_auth_get(uint32_t address);
+
+/**
+ * Gets pointer to the unauthenticated part of the boot header.
+ *
+ * @param hdr Pointer to the authenticated part of the boot header
+ * @return Pointer to the unauthenticated part of the boot header or NULL if the
+ * header is invalid.
+ */
+const boot_header_unauth_t* boot_header_unauth_get(
+ const boot_header_auth_t* hdr);
+
+/**
+ * Calculates the Merkle root for signature verification.
+ *
+ * The result includes the bootloader code, authenticated part of the
+ * boot header and the Merkle tree path.
+ *
+ * @param hdr Pointer to the boot header
+ * @param code_address Address of the bootloader code in flash memory
+ * @param root Pointer to the output Merkle root node
+ */
+void boot_header_calc_merkle_root(const boot_header_auth_t* hdr,
+ uint32_t code_address,
+ merkle_proof_node_t* root);
+
+/**
+ * Checks the signature in the boot header against the public keys.
+ *
+ * This function checks the signatures of the boot header using the
+ * bootloader public keys. It uses the Merkle root calculated from
+ * the boot header and bootloader code to perform the verification.
+ *
+ * @param hdr Pointer to the boot header
+ * @param merkle_root Pointer to the Merkle root
+ * @return secbool indicating whether the signature verification was successful.
+ */
+secbool boot_header_check_signature(const boot_header_auth_t* hdr,
+ const merkle_proof_node_t* merkle_root);
+
+/**
+ * This function compares the boot header and the bootloader code
+ * with the installed bootloader header and code. If they are different,
+ * it returns sectrue, otherwise secfalse.
+ *
+ * @param hdr Pointer to the new boot header
+ * @param code_address Address of the new bootloader code in flash memory
+ * @return secbool indicating whether the boot header and code need update
+ */
+secbool bootloader_area_needs_update(const boot_header_auth_t* hdr,
+ uint32_t code_address);
diff --git a/core/embed/sec/image/inc/sec/boot_image.h b/core/embed/sec/image/inc/sec/boot_image.h
new file mode 100644
index 00000000..7e647897
--- /dev/null
+++ b/core/embed/sec/image/inc/sec/boot_image.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <trezor_types.h>
+
+/**
+ * Structure representing a bootloader image and all its associated data.
+ */
+typedef struct {
+ const void* image_ptr;
+ size_t image_size;
+#ifndef USE_BOOT_UCB
+ uint8_t hash_00[32];
+ uint8_t hash_FF[32];
+#endif
+} boot_image_t;
+
+/**
+ * @brief Get the new bootloader available as a part of the build.
+ *
+ * This function retrieves the bootloader image that is
+ * included in the build. The image is expected to be padded
+ * with 0x00 and 0xFF bytes to match the expected size.
+ *
+ * @return Pointer to a `boot_image_t` structure containing the
+ * image data, size, and expected hashes.
+ */
+const boot_image_t* boot_image_get_embdata(void);
+
+/**
+ * @brief Verify the installed bootloader against expected hashes.
+ *
+ * Calculates the hash of the currently installed bootloader and compares
+ * it against two known-good expected hashes.
+ *
+ * @param image Pointer to the `boot_image_t` structure containing
+ * the expected hashes and other image data.
+ *
+ * @return `true` if the installed bootloader's hash does not match either
+ * of the expected hashes (indicating it should be replaced),
+ * `false` if it matches one of them.
+ */
+bool boot_image_check(const boot_image_t* image);
+
+/**
+ * @brief Replace the currently installed bootloader.
+ *
+ * Writes a new bootloader image into flash.
+ *
+ * @param image Pointer to the `boot_image_t` structure containing
+ * the new bootloader image data.
+ */
+void boot_image_replace(const boot_image_t* image);
diff --git a/core/embed/sec/image/inc/sec/boot_ucb.h b/core/embed/sec/image/inc/sec/boot_ucb.h
new file mode 100644
index 00000000..16544bbf
--- /dev/null
+++ b/core/embed/sec/image/inc/sec/boot_ucb.h
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <trezor_types.h>
+
+#include <sec/boot_header.h>
+#include <sec/image_hash_conf.h>
+
+/**
+ * Update control block structure sitting on fixed address in flash memory
+ */
+typedef struct {
+ /** Magic constant checked in boardloader */
+ uint32_t magic;
+ /** Address of the start of the header structure */
+ uint32_t header_address;
+ /** Address of the start of the bootloader code in flash memory */
+ uint32_t code_address;
+ /** Padding to align the structure to 16-bytes */
+ uint32_t padding;
+ /** Hash of the boot header
+ * This is used to verify that the boot header has not changed
+ * since the UCB was written. */
+ uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
+
+} boot_ucb_t;
+
+/**
+ * Reads the update control block from flash memory, checks its integrity and
+ * copies it to the provided structure.
+ *
+ * @param ucb Pointer to the boot update control block structure to be filled
+ * @return sectrue if the read was successful and the update control block is
+ * valid, secfalse otherwise.
+ */
+
+secbool boot_ucb_read(boot_ucb_t* ucb);
+
+/**
+ * Writes to the update control block in flash memory.
+ *
+ * This function is called by the code that updates the bootloader
+ * or the bootloader header. Before calling this function, the updater must
+ * store the bootloader header and code in flash memory.
+ *
+ * @param header_address Address of the start of the boot header in flash
+ * memory. This parameter is mandatory.
+ * @param code_address Address of the start of the bootloader code in flash
+ * memory. If the code is not present, it is expected that only the
+ * header will be updated and this parameter should be set to 0.
+ * @return sectrue if the write was successful, secfalse otherwise.
+ */
+secbool boot_ucb_write(uint32_t header_address, uint32_t code_address);
+
+/**
+ * Erases the update control block in flash memory.
+ *
+ * This function is called by the bootloader to finalize
+ * the update process and to ensure that the boardloader will not
+ * repeat the update process if it was already done.
+ *
+ * @return sectrue if the erase was successful, secfalse otherwise.
+ */
+secbool boot_ucb_erase(void);
diff --git a/core/embed/sec/image/inc/sec/image.h b/core/embed/sec/image/inc/sec/image.h
new file mode 100644
index 00000000..4b92b132
--- /dev/null
+++ b/core/embed/sec/image/inc/sec/image.h
@@ -0,0 +1,206 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <trezor_model.h>
+#include <trezor_types.h>
+
+#include <sys/flash.h>
+#include "blake2s.h"
+
+#include "image_hash_conf.h"
+
+#define VENDOR_HEADER_MAX_SIZE (64 * 1024)
+#define IMAGE_HEADER_SIZE 0x400 // size of the bootloader or firmware header
+#define SECMON_HEADER_SIZE 0x200
+#define IMAGE_SIG_SIZE 65
+#define IMAGE_INIT_CHUNK_SIZE (16 * 1024)
+
+#define BOOTLOADER_IMAGE_MAGIC 0x425A5254 // TRZB
+
+#define FIRMWARE_IMAGE_MAGIC 0x465A5254 // TRZF
+
+#define SECMON_IMAGE_MAGIC 0x43455354 // TSEC
+
+typedef struct {
+ uint32_t magic;
+ uint32_t hdrlen;
+ uint32_t expiry;
+ uint32_t codelen;
+ uint32_t version;
+ uint32_t fix_version;
+ uint32_t hw_model;
+ uint8_t hw_revision;
+ uint8_t monotonic;
+ uint8_t reserved_0[2];
+ uint8_t hashes[512];
+ uint8_t reserved_1[415];
+ uint8_t sigmask;
+ uint8_t sig[64];
+} image_header;
+
+#define MAX_VENDOR_PUBLIC_KEYS 8
+
+// The mask of the vendor screen wait time in seconds, encoded in bitwise
+// complement form.
+#define VTRUST_WAIT_MASK 0x000F
+
+// Use black background instead of red one in the vendor screen.
+#define VTRUST_NO_RED 0x0010
+
+// Do not require user click to leave the vendor screen.
+#define VTRUST_NO_CLICK 0x0020
+
+// Do not show vendor string in the vendor screen.
+#define VTRUST_NO_STRING 0x0040
+
+// Two bits for historical reasons. On T2B1, only the lower bit was used with
+// inverted logic (due to late inclusion of the secret handling during
+// development process). On T3T1, we decided to remedy the situation by
+// including the upper bit as well.
+#define VTRUST_SECRET_MASK 0x0180
+#define VTRUST_SECRET_ALLOW 0x0100
+
+#define VTRUST_NO_WARNING \
+ (VTRUST_WAIT_MASK | VTRUST_NO_RED | VTRUST_NO_CLICK | VTRUST_NO_STRING)
+
+#define VTRUST_ALLOW_PROVISIONING 0x200
+#define VTRUST_ALLOW_UNLIMITED_RUN 0x400
+
+// Globally defined values for the `vendor_header.fw_type` field.
+// !!! Do not modify existing values. Only add new ones if needed.
+//
+typedef enum {
+ // Reserved value (may appear in legacy vendor headers)
+ VENDOR_FW_TYPE_RESERVED = 0,
+ // Custom (unsafe) firmware
+ VENDOR_FW_TYPE_CUSTOM = 1,
+ // Trezor Universal firmware
+ VENDOR_FW_TYPE_UNIVERSAL = 2,
+ // Trezor Bitcoin-only firmware
+ VENDOR_FW_TYPE_BTC_ONLY = 3,
+ // Factory tester firmware
+ VENDOR_FW_TYPE_PRODTEST = 4,
+} vendor_fw_type_t;
+
+typedef struct {
+ uint32_t magic;
+ uint32_t hdrlen;
+ uint32_t expiry;
+ uint16_t version;
+ uint8_t vsig_m;
+ uint8_t vsig_n;
+ uint16_t vtrust;
+ uint32_t hw_model;
+ uint8_t fw_type;
+ // uint8_t reserved[10];
+ const uint8_t *vpub[MAX_VENDOR_PUBLIC_KEYS];
+ uint8_t vstr_len;
+ const char *vstr;
+ const uint8_t *vimg;
+ uint8_t sigmask;
+ uint8_t sig[64];
+ const uint8_t *origin; // pointer to the underlying data
+} vendor_header;
+
+typedef struct {
+ // vendor string
+ uint8_t vstr[64];
+ // vendor string length
+ size_t vstr_len;
+ // firmware version
+ uint8_t ver_major;
+ uint8_t ver_minor;
+ uint8_t ver_patch;
+ uint8_t ver_build;
+ // firmware fingerprint
+ uint8_t fingerprint[IMAGE_HASH_DIGEST_LENGTH];
+ // hash of vendor and image header
+ uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
+} firmware_header_info_t;
+
+typedef struct {
+ uint32_t magic;
+ uint32_t hdrlen;
+ uint32_t codelen;
+ uint32_t version;
+ uint32_t hw_model;
+ uint8_t hw_revision;
+ uint8_t monotonic;
+ uint8_t reserved_0[2];
+ uint8_t hash[32];
+ uint8_t reserved_1[391];
+ uint8_t sigmask;
+ uint8_t sig[64];
+} secmon_header_t;
+
+const image_header *read_image_header(const uint8_t *const data,
+ const uint32_t magic,
+ const uint32_t maxsize);
+
+secbool __wur check_image_model(const image_header *const hdr);
+
+secbool __wur check_image_header_sig(const image_header *const hdr,
+ uint8_t key_m, uint8_t key_n,
+ const uint8_t *const *keys);
+
+secbool __wur read_vendor_header(const uint8_t *const data,
+ vendor_header *const vhdr);
+
+secbool __wur check_vendor_header_model(const vendor_header *const vhdr);
+
+secbool __wur check_vendor_header_sig(const vendor_header *const vhdr,
+ uint8_t key_m, uint8_t key_n,
+ const uint8_t *const *keys);
+
+secbool check_vendor_header_keys(const vendor_header *const vhdr);
+
+void vendor_header_hash(const vendor_header *const vhdr, uint8_t *hash);
+
+secbool __wur check_single_hash(const uint8_t *const hash,
+ const uint8_t *const data, int len);
+
+#ifdef KERNEL_MODE
+secbool __wur check_image_contents(const image_header *const hdr,
+ uint32_t firstskip,
+ const flash_area_t *area);
+#endif
+
+void get_image_fingerprint(const image_header *const hdr, uint8_t *const out);
+
+secbool check_firmware_header(const uint8_t *header, size_t header_size,
+ firmware_header_info_t *info);
+
+secbool __wur check_bootloader_header_sig(const image_header *const hdr);
+
+#ifdef USE_SECMON_VERIFICATION
+const secmon_header_t *read_secmon_header(const uint8_t *const data,
+ const uint32_t maxsize);
+
+secbool __wur check_secmon_model(const secmon_header_t *const hdr);
+
+secbool __wur check_secmon_header_sig(const secmon_header_t *const hdr);
+
+#ifdef SECURE_MODE
+secbool __wur check_secmon_contents(const secmon_header_t *const hdr,
+ size_t code_offset,
+ const flash_area_t *area);
+#endif
+#endif
diff --git a/core/embed/sec/image/inc/sec/image_hash_conf.h b/core/embed/sec/image/inc/sec/image_hash_conf.h
new file mode 100644
index 00000000..5c16da33
--- /dev/null
+++ b/core/embed/sec/image/inc/sec/image_hash_conf.h
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <trezor_model.h>
+#include <trezor_types.h>
+
+#ifdef IMAGE_HASH_SHA256
+#include "sha2.h"
+#define IMAGE_HASH_DIGEST_LENGTH SHA256_DIGEST_LENGTH
+#if defined(USE_HASH_PROCESSOR) && defined(SECURE_MODE)
+#include <sec/hash_processor.h>
+#define IMAGE_HASH_CTX hash_sha256_context_t
+#define IMAGE_HASH_INIT(ctx) hash_processor_sha256_init(ctx)
+#define IMAGE_HASH_UPDATE(ctx, data, len) \
+ hash_processor_sha256_update(ctx, data, len)
+#define IMAGE_HASH_FINAL(ctx, output) hash_processor_sha256_final(ctx, output)
+#define IMAGE_HASH_CALC(data, len, output) \
+ hash_processor_sha256_calc(data, len, output)
+#else
+#define IMAGE_HASH_CTX SHA256_CTX
+#define IMAGE_HASH_INIT(ctx) sha256_Init(ctx)
+#define IMAGE_HASH_UPDATE(ctx, data, len) sha256_Update(ctx, data, len)
+#define IMAGE_HASH_FINAL(ctx, output) sha256_Final(ctx, output)
+#define IMAGE_HASH_CALC(data, len, output) sha256_Raw(data, len, output)
+#endif
+
+#elif defined IMAGE_HASH_BLAKE2S
+#include "blake2s.h"
+#define IMAGE_HASH_DIGEST_LENGTH BLAKE2S_DIGEST_LENGTH
+#define IMAGE_HASH_CTX BLAKE2S_CTX
+#define IMAGE_HASH_INIT(ctx) blake2s_Init(ctx, BLAKE2S_DIGEST_LENGTH)
+#define IMAGE_HASH_UPDATE(ctx, data, len) blake2s_Update(ctx, data, len)
+#define IMAGE_HASH_FINAL(ctx, output) \
+ blake2s_Final(ctx, output, BLAKE2S_DIGEST_LENGTH)
+#define IMAGE_HASH_CALC(data, len, output) \
+ blake2s(data, len, output, BLAKE2S_DIGEST_LENGTH)
+#else
+#error "IMAGE_HASH_SHA256 or IMAGE_HASH_BLAKE2S must be defined"
+#endif
diff --git a/core/embed/sec/storage/stm32u5/storage_salt.c b/core/embed/sec/storage/stm32u5/storage_salt.c
index 1c3b1f44..eda8bc6c 100644
--- a/core/embed/sec/storage/stm32u5/storage_salt.c
+++ b/core/embed/sec/storage/stm32u5/storage_salt.c
@@ -22,11 +22,11 @@
#include <trezor_model.h>
#include <trezor_rtl.h>
+#include <sec/image.h>
#include <sec/secret_keys.h>
#include <sys/flash_otp.h>
#include <sys/mpu.h>
#include <sys/rng.h>
-#include <util/image.h>
#include "stm32u5xx_ll_utils.h"
diff --git a/core/embed/sys/smcall/stm32/smcall_dispatch.c b/core/embed/sys/smcall/stm32/smcall_dispatch.c
index e841fa5a..5c6c11c9 100644
--- a/core/embed/sys/smcall/stm32/smcall_dispatch.c
+++ b/core/embed/sys/smcall/stm32/smcall_dispatch.c
@@ -46,7 +46,7 @@
#include <sys/suspend_io.h>
#endif
-#include <util/boot_image.h>
+#include <sec/boot_image.h>
#include "smcall_numbers.h"
#include "smcall_probe.h"
diff --git a/core/embed/sys/smcall/stm32/smcall_stubs.c b/core/embed/sys/smcall/stm32/smcall_stubs.c
index d76352e3..4285e791 100644
--- a/core/embed/sys/smcall/stm32/smcall_stubs.c
+++ b/core/embed/sys/smcall/stm32/smcall_stubs.c
@@ -39,7 +39,7 @@ void bootargs_get_args(boot_args_t *args) {
// boot_image.h
// =============================================================================
-#include <util/boot_image.h>
+#include <sec/boot_image.h>
bool boot_image_check(const boot_image_t *image) {
return (bool)smcall_invoke1((uint32_t)image, SMCALL_BOOT_IMAGE_CHECK);
diff --git a/core/embed/sys/smcall/stm32/smcall_verifiers.h b/core/embed/sys/smcall/stm32/smcall_verifiers.h
index 8f05d21f..1224aa0a 100644
--- a/core/embed/sys/smcall/stm32/smcall_verifiers.h
+++ b/core/embed/sys/smcall/stm32/smcall_verifiers.h
@@ -31,7 +31,7 @@ void bootargs_get_args__verified(boot_args_t *args);
// ---------------------------------------------------------------------
-#include <util/boot_image.h>
+#include <sec/boot_image.h>
bool boot_image_check__verified(const boot_image_t *image);
diff --git a/core/embed/sys/startup/inc/sys/bootutils.h b/core/embed/sys/startup/inc/sys/bootutils.h
index 8498e939..934d1744 100644
--- a/core/embed/sys/startup/inc/sys/bootutils.h
+++ b/core/embed/sys/startup/inc/sys/bootutils.h
@@ -23,7 +23,7 @@
#ifdef STM32F4
// Offset of the vector table in the bootloader image.
-// This should match IMAGE_HEADER_SIZE in util/image.h.
+// This should match IMAGE_HEADER_SIZE in sec/image.h.
// Duplicated here to avoid circular dependency.
#define BOOTLOADER_VECTBL_OFFSET 0x400
#endif
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index 30179e5f..ecfc5bb9 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -81,7 +81,7 @@
#endif
#if PRODUCTION || BOOTLOADER_QA
-#include <util/boot_image.h>
+#include <sec/boot_image.h>
#endif
#include "syscall_context.h"
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index 0d2358de..03197ce1 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -143,7 +143,7 @@ bool syslog_set_filter(const char *filter, size_t filter_len) {
// boot_image.h
// =============================================================================
-#include <util/boot_image.h>
+#include <sec/boot_image.h>
bool boot_image_check(const boot_image_t *image) {
return (bool)syscall_invoke1((uint32_t)image, SYSCALL_BOOT_IMAGE_CHECK);
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h
index b0b39aa6..783b0fa5 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.h
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h
@@ -78,7 +78,7 @@ void reboot_and_upgrade__verified(const uint8_t hash[32]);
// ---------------------------------------------------------------------
-#include <util/boot_image.h>
+#include <sec/boot_image.h>
bool boot_image_check__verified(const boot_image_t *image);
diff --git a/core/embed/upymod/modtrezorutils/modtrezorutils.c b/core/embed/upymod/modtrezorutils/modtrezorutils.c
index 4aa388a5..5f64a67c 100644
--- a/core/embed/upymod/modtrezorutils/modtrezorutils.c
+++ b/core/embed/upymod/modtrezorutils/modtrezorutils.c
@@ -26,7 +26,7 @@
#include "py/objstr.h"
#include "py/runtime.h"
-#include <util/image.h>
+#include <sec/image.h>
#include "version.h"
#if MICROPY_PY_TREZORUTILS
diff --git a/core/embed/util/image/boot_header.c b/core/embed/util/image/boot_header.c
deleted file mode 100644
index 0ee97c03..00000000
--- a/core/embed/util/image/boot_header.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifdef SECURE_MODE
-
-#include <trezor_model.h>
-#include <trezor_rtl.h>
-
-#include <rtl/sizedefs.h>
-#include <util/boot_header.h>
-#include <util/image_hash_conf.h>
-
-#include <../vendor/sphincsplus/ref/api.h>
-#include <ed25519-donna/ed25519.h>
-
-#include <version.h>
-
-#ifdef BOOTLOADER
-extern const uint8_t _bootloader_code_size;
-
-typedef union {
- boot_header_auth_t hdr;
- uint8_t raw[BOOT_HEADER_MAXSIZE];
-} boot_header_padded_t;
-
-__attribute__((section(".header")))
-const boot_header_padded_t g_bootloader_header = {
- .hdr = {
- .magic = BOOT_HEADER_MAGIC_TRZQ,
- .hw_model = HW_MODEL,
- .hw_revision = HW_REVISION,
- .version =
- {
- .major = VERSION_MAJOR,
- .minor = VERSION_MINOR,
- .patch = VERSION_PATCH,
- .build = VERSION_BUILD,
- },
- .fix_version =
- {
- .major = FIX_VERSION_MAJOR,
- .minor = FIX_VERSION_MINOR,
- .patch = FIX_VERSION_PATCH,
- .build = FIX_VERSION_BUILD,
- },
- .min_prev_version =
- {
- .major = 0,
- .minor = 0,
- .patch = 0,
- .build = 0,
- },
- .monotonic_version = BOOTLOADER_MONOTONIC_VERSION,
- // The sigmask field is properly initialized later by headertool_pq
- // (= 0 => no keys used for signature verification; prevents booting)
- .sigmask = 0,
- .header_size = BOOT_HEADER_MAXSIZE,
- // The authenticated part size is calculated for a zero-length Merkle
- // proof, since the Merkle proof is not known at compile time.
- // headertool_pq must update this value later when adding the Merkle
- // proof to the header.
- .auth_size = BOOT_HEADER_MAXSIZE - sizeof(boot_header_merkle_proof_t) -
- sizeof(boot_header_unauth_t),
- .code_size = (uint32_t)&_bootloader_code_size,
- .storage_address = STORAGE_1_START,
- }};
-#endif
-
-static const uint8_t * const BOARDLOADER_PQ_KEYS[] = {
-#if !PRODUCTION
- (const uint8_t*) "\xec\x01\xe6\x02\x63\x02\x4f\x7e\x71\x72\x80\x13\xb7\x31\xf7\xba\x12\x99\xf5\x18\xc2\x7b\xa3\xed\x8f\x4a\x21\x99\x74\x12\x7c\x62",
- (const uint8_t*) "\x8a\xf8\x87\x80\x85\x94\x6e\xd8\xb1\x16\xbd\x24\xc0\xf2\xaa\xc4\x8b\x7e\x8f\x11\xbf\x06\x87\x25\xcc\xfb\xb1\x52\xab\xf7\xa4\xcd",
-#else
- MODEL_BOARDLOADER_PQ_KEYS
-#endif
-};
-
-static const uint8_t * const BOARDLOADER_EC_KEYS[] = {
-#if !PRODUCTION
- (const uint8_t*) "\xdb\x99\x5f\xe2\x51\x69\xd1\x41\xca\xb9\xbb\xba\x92\xba\xa0\x1f\x9f\x2e\x1e\xce\x7d\xf4\xcb\x2a\xc0\x51\x90\xf3\x7f\xcc\x1f\x9d",
- (const uint8_t*) "\x21\x52\xf8\xd1\x9b\x79\x1d\x24\x45\x32\x42\xe1\x5f\x2e\xab\x6c\xb7\xcf\xfa\x7b\x6a\x5e\xd3\x00\x97\x96\x0e\x06\x98\x81\xdb\x12",
-#else
- MODEL_BOARDLOADER_EC_KEYS
-#endif
-};
-
-secbool boot_header_check_signature(const boot_header_auth_t* hdr,
- const merkle_proof_node_t* merkle_root) {
- // Get the signature indices based on the signature mask
- _Static_assert(ARRAY_LENGTH(BOARDLOADER_PQ_KEYS) <= 3);
- _Static_assert(ARRAY_LENGTH(BOARDLOADER_EC_KEYS) ==
- ARRAY_LENGTH(BOARDLOADER_PQ_KEYS));
-
- uint8_t sigmask = hdr->sigmask;
- uint8_t sigmask_inv = 0; // FIH
-
- const boot_header_unauth_t* sig = boot_header_unauth_get(hdr);
-
- for (int sig_idx = 0; sig_idx < ARRAY_LENGTH(sig->ec_signature); sig_idx++) {
- // Get the index of the public key in the signature mask
- int key_idx = __builtin_ctz(sigmask);
- if (key_idx >= ARRAY_LENGTH(BOARDLOADER_PQ_KEYS)) {
- return secfalse;
- }
-
- // Hash of the Merkle root and the SLH signature
- uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
- IMAGE_HASH_CTX ctx;
- IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, merkle_root->bytes, sizeof(merkle_root->bytes));
- IMAGE_HASH_UPDATE(&ctx, sig->slh_signature[sig_idx],
- sizeof(sig->slh_signature[sig_idx]));
- IMAGE_HASH_FINAL(&ctx, hash);
-
- // Verify EC signature - do it before we verify the PQC signature
- int ec_result =
- ed25519_sign_open(hash, sizeof(hash), BOARDLOADER_EC_KEYS[key_idx],
- sig->ec_signature[sig_idx]);
-
- if (ec_result != 0) {
- return secfalse;
- }
-
- // Verify the PQC signature
- int pq_result = crypto_sign_verify(
- sig->slh_signature[sig_idx], sizeof(sig->slh_signature[sig_idx]),
- merkle_root->bytes, sizeof(merkle_root->bytes),
- BOARDLOADER_PQ_KEYS[key_idx]);
-
- if (pq_result != 0) {
- return secfalse;
- }
-
- // Mark the key as used
- sigmask &= ~(1 << key_idx);
- sigmask_inv |= (1 << key_idx);
- }
-
- if (sigmask != 0 || sigmask_inv != hdr->sigmask) { // FIH
- // There were more than BOOT_HEADER_SIGNATURE_COUNT public key bits
- // set or some of the public keys in the original sigmask were not used.
- return secfalse;
- }
-
- return sectrue;
-}
-
-static size_t boot_header_merkle_proof_size(
- const boot_header_merkle_proof_t* proof) {
- return sizeof(boot_header_merkle_proof_t) +
- proof->node_count * sizeof(proof->nodes[0]);
-}
-
-static const boot_header_merkle_proof_t* boot_header_get_merkle_proof(
- const boot_header_auth_t* hdr) {
- // Check if the merkle_proof.path_len field is within the header
- if (hdr->auth_size + sizeof(boot_header_merkle_proof_t) > hdr->header_size) {
- return NULL;
- }
-
- // Merkle proof is located right after the authenticated part of the header
- boot_header_merkle_proof_t* proof =
- (boot_header_merkle_proof_t*)((uintptr_t)hdr + hdr->auth_size);
-
- // Check if the path length is in reasonable limits
- if (proof->node_count > BOOT_HEADER_MERKLE_PROOF_MAXLEN) {
- return NULL;
- }
-
- size_t proof_size = boot_header_merkle_proof_size(proof);
-
- // Check if the Merkle proof is completely within the header
- if (hdr->auth_size + proof_size > hdr->header_size) {
- return NULL;
- }
-
- return proof;
-}
-
-const boot_header_auth_t* boot_header_auth_get(uint32_t address) {
- boot_header_auth_t* hdr = (boot_header_auth_t*)address;
-
- // Check if the header starts with the magic
- if (hdr->magic != BOOT_HEADER_MAGIC_TRZQ) {
- return NULL;
- }
-
- // Check if the header size (= bootloader code offset) is aligned to 8K
- // boundary (flash page size)
- if (!IS_ALIGNED(hdr->header_size, SIZE_8K) || hdr->header_size == 0) {
- return NULL;
- }
-
- // Check if the header size is in reasonable limits
- if (hdr->header_size >= SIZE_64K) {
- return NULL;
- }
-
- // Check if the authenticated part size is within the header size
- if (hdr->auth_size >= hdr->header_size) {
- return NULL;
- }
-
- // Check if the size of the authenticated part is at least the size of the
- // authenticated boot header structure. This condition prevents updating
- // to an image whose authenticated part is smaller than the current
- // authenticated boot header structure.
- if (hdr->auth_size < sizeof(boot_header_auth_t)) {
- return NULL;
- }
-
- // Check if bootloader code size is within reasonable limits
- if (hdr->code_size < SIZE_8K) {
- return NULL;
- }
-
- // Check if the hardware model and revision match
- if (hdr->hw_model != HW_MODEL || hdr->hw_revision != HW_REVISION) {
- return secfalse;
- }
-
- // Check if the header contains a valid Merkle proof
- if (NULL == boot_header_get_merkle_proof(hdr)) {
- return NULL;
- }
-
- // Check if the header contains a valid unauthenticated part
- if (NULL == boot_header_unauth_get(hdr)) {
- return NULL;
- }
-
- return hdr;
-}
-
-const boot_header_unauth_t* boot_header_unauth_get(
- const boot_header_auth_t* hdr) {
- const boot_header_merkle_proof_t* proof = boot_header_get_merkle_proof(hdr);
-
- if (proof == NULL) {
- // If the Merkle proof is invalid, the unauthenticated part cannot
- // be valid either
- return NULL;
- }
-
- size_t proof_size = boot_header_merkle_proof_size(proof);
-
- // Unauthenticated part is located right after the Merkle proof
- boot_header_unauth_t* unauth =
- (boot_header_unauth_t*)((uintptr_t)proof + proof_size);
-
- // Check if the unauthenticated part is within the header
- if (hdr->auth_size + proof_size + sizeof(boot_header_unauth_t) >
- hdr->header_size) {
- return NULL;
- }
-
- return unauth;
-}
-
-void boot_header_calc_merkle_root(const boot_header_auth_t* hdr,
- uint32_t code_address,
- merkle_proof_node_t* root) {
- IMAGE_HASH_CTX ctx;
-
- static const uint8_t prefix0[] = {0x00};
- static const uint8_t prefix1[] = {0x01};
-
- // Hash the bootloader code
- IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, (const uint8_t*)code_address, hdr->code_size);
- IMAGE_HASH_FINAL(&ctx, root->bytes);
-
- // Hash the authenticated part of the header
- IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, prefix0, sizeof(prefix0));
- IMAGE_HASH_UPDATE(&ctx, (const uint8_t*)hdr, hdr->auth_size);
- IMAGE_HASH_UPDATE(&ctx, root->bytes, sizeof(root->bytes));
- IMAGE_HASH_FINAL(&ctx, root->bytes);
-
- const boot_header_merkle_proof_t* proof = boot_header_get_merkle_proof(hdr);
-
- // Add the Merkle proof nodes to the hash
- for (size_t i = 0; i < proof->node_count; i++) {
- const merkle_proof_node_t* node = &proof->nodes[i];
- IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, prefix1, sizeof(prefix1));
- if (memcmp(node, root->bytes, sizeof(root->bytes)) < 0) {
- IMAGE_HASH_UPDATE(&ctx, node->bytes, sizeof(node->bytes));
- IMAGE_HASH_UPDATE(&ctx, root->bytes, sizeof(root->bytes));
- } else {
- IMAGE_HASH_UPDATE(&ctx, root->bytes, sizeof(root->bytes));
- IMAGE_HASH_UPDATE(&ctx, node->bytes, sizeof(node->bytes));
- }
- IMAGE_HASH_FINAL(&ctx, root->bytes);
- }
-}
-
-secbool bootloader_area_needs_update(const boot_header_auth_t* hdr,
- uint32_t code_address) {
- boot_header_auth_t* prev_hdr = (boot_header_auth_t*)BOOTLOADER_START;
- if (hdr->header_size == prev_hdr->header_size &&
- hdr->code_size == prev_hdr->code_size &&
- (memcmp(hdr, prev_hdr, hdr->header_size) == 0) &&
- (memcmp((const uint8_t*)code_address,
- (const uint8_t*)prev_hdr + prev_hdr->header_size,
- hdr->code_size) == 0)) {
- return secfalse;
- }
- return sectrue;
-}
-
-#endif // SECURE_MODE
diff --git a/core/embed/util/image/boot_image.c b/core/embed/util/image/boot_image.c
deleted file mode 100644
index dd420cb7..00000000
--- a/core/embed/util/image/boot_image.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifdef SECURE_MODE
-
-#include <trezor_model.h>
-#include <trezor_rtl.h>
-
-#include <sec/board_capabilities.h>
-#include <sec/monoctr.h>
-#include <sys/flash.h>
-#include <sys/mpu.h>
-#include <util/boot_image.h>
-#include <util/image.h>
-
-#include "blake2s.h"
-#include "memzero.h"
-
-#ifdef USE_BOOT_UCB
-#include <util/boot_header.h>
-#include <util/boot_ucb.h>
-#else
-#include "uzlib.h"
-#endif
-
-#ifndef USE_BOOT_UCB
-
-static secbool hash_match(const uint8_t *hash, const uint8_t *hash_00,
- const uint8_t *hash_FF) {
- if (0 == memcmp(hash, hash_00, BLAKE2S_DIGEST_LENGTH)) return sectrue;
- if (0 == memcmp(hash, hash_FF, BLAKE2S_DIGEST_LENGTH)) return sectrue;
- return secfalse;
-}
-
-#define UZLIB_WINDOW_SIZE (1 << 10)
-_Static_assert(
- UZLIB_WINDOW_SIZE >= IMAGE_HEADER_SIZE,
- "UZLIB_WINDOW_SIZE must be at least as large as IMAGE_HEADER_SIZE");
-_Static_assert(
- BOOTLOADER_MAXSIZE <= IMAGE_CHUNK_SIZE,
- "BOOTLOADER_MAXSIZE must be less than or equal to IMAGE_CHUNK_SIZE");
-
-static void uzlib_prepare(struct uzlib_uncomp *decomp, uint8_t *window,
- const void *src, uint32_t srcsize, void *dest,
- uint32_t destsize) {
- memzero(decomp, sizeof(struct uzlib_uncomp));
- if (window) {
- memzero(window, UZLIB_WINDOW_SIZE);
- }
- memzero(dest, destsize);
- decomp->source = (const uint8_t *)src;
- decomp->source_limit = decomp->source + srcsize;
- decomp->dest = (uint8_t *)dest;
- decomp->dest_limit = decomp->dest + destsize;
- uzlib_uncompress_init(decomp, window, window ? UZLIB_WINDOW_SIZE : 0);
-}
-
-bool boot_image_check(const boot_image_t *image) {
- mpu_mode_t mode = mpu_reconfig(MPU_MODE_BOOTLOADER);
-
- // compute current bootloader hash
- uint8_t hash[BLAKE2S_DIGEST_LENGTH];
- const uint32_t bl_len = flash_area_get_size(&BOOTLOADER_AREA);
- const void *bl_data = flash_area_get_address(&BOOTLOADER_AREA, 0, bl_len);
- blake2s(bl_data, bl_len, hash, BLAKE2S_DIGEST_LENGTH);
-
- // don't whitelist the valid bootloaders for now
- // ensure(known_bootloader(hash, BLAKE2S_DIGEST_LENGTH), "Unknown bootloader
- // detected");
-
- // does the bootloader match?
- if (sectrue == hash_match(hash, image->hash_00, image->hash_FF)) {
- mpu_reconfig(mode);
- return false;
- }
-
- mpu_reconfig(mode);
- return true;
-}
-
-void boot_image_replace(const boot_image_t *image) {
- const uint32_t bl_len = flash_area_get_size(&BOOTLOADER_AREA);
- const void *bl_data = flash_area_get_address(&BOOTLOADER_AREA, 0, bl_len);
-
- mpu_mode_t mode = mpu_reconfig(MPU_MODE_BOOTLOADER);
-
- struct uzlib_uncomp decomp = {0};
- uint8_t decomp_window[UZLIB_WINDOW_SIZE] = {0};
- uint32_t decomp_out[IMAGE_HEADER_SIZE / sizeof(uint32_t)] = {0};
-
- uzlib_prepare(&decomp, decomp_window, image->image_ptr, image->image_size,
- decomp_out, sizeof(decomp_out));
-
- ensure((uzlib_uncompress(&decomp) == TINF_OK) ? sectrue : secfalse,
- "Bootloader header decompression failed");
-
- const image_header *new_bld_hdr = read_image_header(
- (uint8_t *)&decomp_out, BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_MAXSIZE);
-
- ensure(new_bld_hdr == (const image_header *)&decomp_out ? sectrue : secfalse,
- "Invalid embedded bootloader");
-
- ensure(check_image_model(new_bld_hdr), "Incompatible embedded bootloader");
-
- ensure(check_bootloader_header_sig(new_bld_hdr),
- "Invalid embedded bootloader signature");
-
- const image_header *current_bld_hdr =
- read_image_header(bl_data, BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_MAXSIZE);
-
- uint8_t new_bld_hash[IMAGE_HASH_DIGEST_LENGTH] = {0};
- uint8_t new_bld_hash_expected[IMAGE_HASH_DIGEST_LENGTH] = {0};
-
- IMAGE_HASH_CTX ctx;
- IMAGE_HASH_INIT(&ctx);
-
- // backup new bld header values, as it will be overwritten by uzlib
- size_t header_offset = new_bld_hdr->hdrlen;
- memcpy(new_bld_hash_expected, &new_bld_hdr->hashes[0],
- IMAGE_HASH_DIGEST_LENGTH);
- uint32_t new_bld_hw_model = new_bld_hdr->hw_model;
- uint8_t new_bld_monotonic = new_bld_hdr->monotonic;
-
- do {
- uint8_t *p = (uint8_t *)decomp_out + header_offset;
- uint32_t size = decomp.dest - (uint8_t *)decomp_out - header_offset;
- IMAGE_HASH_UPDATE(&ctx, p, size);
- decomp.dest = (uint8_t *)decomp_out;
- header_offset =
- 0; // after the first chunk, we don't need to skip the header anymore
- } while (uzlib_uncompress(&decomp) >= 0);
-
- IMAGE_HASH_FINAL(&ctx, new_bld_hash);
-
- if (0 !=
- memcmp(new_bld_hash, new_bld_hash_expected, IMAGE_HASH_DIGEST_LENGTH)) {
- // the hash in the header does not match the computed hash
- error_shutdown("Invalid bootloader contents");
- }
-
- memset(&decomp, 0, sizeof(struct uzlib_uncomp));
-
- // cannot find valid header for current bootloader, something is wrong
- ensure(current_bld_hdr == (const image_header *)bl_data ? sectrue : secfalse,
- "Invalid bootloader header");
-
- ensure(check_image_model(current_bld_hdr), "Incompatible bootloader found");
-
- if (new_bld_monotonic < current_bld_hdr->monotonic) {
- error_shutdown("Bootloader downgrade rejected");
- }
-
- uint32_t board_name = get_board_name();
- if (board_name == 0 || strncmp((const char *)&board_name, "T2T1", 4) == 0) {
- // no board capabilities, assume Model T
- if ((strncmp((const char *)&new_bld_hw_model, "T2T1", 4) != 0) &&
- (new_bld_hw_model != 0)) {
- // reject non-model T bootloader
- // 0 represents pre-model check bootloader
- error_shutdown("Incompatible embedded bootloader");
- }
- }
- // at this point, due to the previous check_image_model call, we know that the
- // new_bld_hdr is
- // meant for the same model as this firmware, so we can check the board name
- // against the firmware hw_model.
- else if (board_name != HW_MODEL) {
- // reject incompatible bootloader
- error_shutdown("Incompatible embedded bootloader");
- }
-
- ensure(flash_area_erase(&BOOTLOADER_AREA, NULL), NULL);
- ensure(flash_unlock_write(), NULL);
-
- uint32_t offset = 0;
-
- uzlib_prepare(&decomp, decomp_window, image->image_ptr, image->image_size,
- decomp_out, sizeof(decomp_out));
-
- ensure((uzlib_uncompress(&decomp) == TINF_OK) ? sectrue : secfalse,
- "Bootloader decompression failed");
-
- do {
- uint32_t *p = decomp_out;
- uint32_t size = decomp.dest - (uint8_t *)decomp_out;
- uint32_t size_padded = FLASH_ALIGN(size);
- ensure(flash_area_write_data_padded(&BOOTLOADER_AREA, offset, p, size, 0,
- size_padded),
- NULL);
- offset += size_padded;
- decomp.dest = (uint8_t *)decomp_out;
- } while (uzlib_uncompress(&decomp) >= 0);
-
- if (offset < bl_len) {
- // fill the rest of the bootloader area with 0x00
- ensure(flash_area_write_data_padded(&BOOTLOADER_AREA, offset, NULL, 0, 0,
- bl_len - offset),
- NULL);
- }
-
- ensure(flash_lock_write(), NULL);
-
- mpu_reconfig(mode);
-}
-
-#else
-
-bool boot_image_check(const boot_image_t *image) {
- if (image->image_size < sizeof(boot_header_auth_t)) {
- // Invalid image size, must be at least the size of the header
- return false;
- }
-
- mpu_mode_t mode = mpu_reconfig(MPU_MODE_BOOTLOADER);
-
- boot_header_auth_t *cur_hdr = (boot_header_auth_t *)BOOTLOADER_START;
- boot_header_auth_t *new_hdr = (boot_header_auth_t *)image->image_ptr;
-
- bool diff = (cur_hdr->header_size != new_hdr->header_size) ||
- (memcmp(cur_hdr, new_hdr, cur_hdr->header_size) != 0);
-
- mpu_restore(mode);
-
- return diff;
-}
-
-void boot_image_replace(const boot_image_t *image) {
- uint32_t header_address = (uint32_t)image->image_ptr;
-
- // Check that image is big enough to hold the header at least
- ensure(sectrue * (image->image_size >= sizeof(boot_header_auth_t)),
- "Bootloader image too small");
-
- // Read bootloader header
- const boot_header_auth_t *hdr = boot_header_auth_get(header_address);
- ensure((hdr != NULL) * sectrue, "Invalid bootloader header");
-
- // Check the image is big enough to hold both header and code
- ensure(sectrue * (hdr->header_size + hdr->code_size <= image->image_size),
- "Bootloader image too small");
-
- // Check monotonic version
-
- mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_BOOTLOADER);
-
- const boot_header_auth_t *old_hdr = boot_header_auth_get(BOOTLOADER_START);
-
- ensure((old_hdr != NULL) * sectrue, "Invalid current bootloader header");
-
- uint8_t min_monotonic_version = old_hdr->monotonic_version;
-
- mpu_restore(mpu_mode);
-
- ensure(sectrue * (hdr->monotonic_version >= min_monotonic_version),
- "Bootloader downgrade rejected");
-
- uint32_t code_address = (uint32_t)image->image_ptr + hdr->header_size;
-
- // Calculate the Merkle root from the header and the code
- merkle_proof_node_t merkle_root;
- boot_header_calc_merkle_root(hdr, code_address, &merkle_root);
-
- // Check whether the new bootloader is properly signed
- ensure(boot_header_check_signature(hdr, &merkle_root),
- "Invalid bootloader signature");
-
- // Write to update control block
- ensure(boot_ucb_write(header_address, code_address),
- "Failed to write boot UCB");
-}
-
-#endif
-
-#endif
diff --git a/core/embed/util/image/boot_image_embdata.c b/core/embed/util/image/boot_image_embdata.c
deleted file mode 100644
index 6d9cc94e..00000000
--- a/core/embed/util/image/boot_image_embdata.c
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <trezor_model.h>
-
-#include <util/boot_image.h>
-
-#define CONCAT_NAME_HELPER(prefix, name, suffix) prefix##name##suffix
-#define CONCAT_NAME(name, var) CONCAT_NAME_HELPER(BOOTLOADER_, name, var)
-
-#if BOOTLOADER_QA
-// QA bootloaders
-#define BOOTLOADER_00 CONCAT_NAME(MODEL_INTERNAL_NAME_TOKEN, _QA_00)
-#define BOOTLOADER_FF CONCAT_NAME(MODEL_INTERNAL_NAME_TOKEN, _QA_FF)
-#else
-// normal bootloaders
-#define BOOTLOADER_00 CONCAT_NAME(MODEL_INTERNAL_NAME_TOKEN, _00)
-#define BOOTLOADER_FF CONCAT_NAME(MODEL_INTERNAL_NAME_TOKEN, _FF)
-#endif
-
-// symbols from bootloader.bin => bootloader.o
-extern const void bootloader_start;
-extern const void bootloader_size;
-extern const void bootloader_size;
-
-static const boot_image_t g_bootloader_image = {
- .image_ptr = (const void *)&bootloader_start,
- .image_size = (size_t)&bootloader_size,
-#ifndef USE_BOOT_UCB
- .hash_00 = BOOTLOADER_00,
- .hash_FF = BOOTLOADER_FF,
-#endif
-};
-
-const boot_image_t *boot_image_get_embdata(void) { return &g_bootloader_image; }
diff --git a/core/embed/util/image/boot_ucb.c b/core/embed/util/image/boot_ucb.c
deleted file mode 100644
index 7c8b909c..00000000
--- a/core/embed/util/image/boot_ucb.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifdef SECURE_MODE
-
-#include <trezor_bsp.h>
-#include <trezor_model.h>
-#include <trezor_rtl.h>
-
-#include <rtl/sizedefs.h>
-#include <sys/flash.h>
-#include <sys/mpu.h>
-#include <util/boot_header.h>
-#include <util/boot_ucb.h>
-#include <util/image_hash_conf.h>
-
-#define BOOT_UCB_MAGIC 0x5A8C7BF3
-
-#if defined(BOOTLOADER) || defined(BOARDLOADER)
-void adjust_to_secure_flash(uint32_t* address) {
- if (*address < FLASH_BASE_S) {
- // Address is in the non-secure flash region, adjust it to point to the
- // secure flash region.
- *address += FLASH_BASE_S - FLASH_BASE_NS;
- }
-}
-#endif
-
-secbool boot_ucb_read(boot_ucb_t* ucb) {
- mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_BOOTUCB);
- *ucb = *((const boot_ucb_t*)BOOTUCB_START);
- mpu_restore(mpu_mode);
-
- if (ucb->magic != BOOT_UCB_MAGIC) {
- return secfalse;
- }
-
-#if defined(BOOTLOADER) || defined(BOARDLOADER)
- // Addresses in the UCB block may reside in both non-secure and secure
- // flash regions. We need to adjust them to point to the secure flash
- // region in order to proceed.
- adjust_to_secure_flash(&ucb->header_address);
- adjust_to_secure_flash(&ucb->code_address);
-#endif
-
- // Before reading the boot header fields, we need to ensure that it's
- // located in the valid address range.
- uint32_t min_address = NONBOARDLOADER_START;
- uint32_t max_address = NONBOARDLOADER_START + NONBOARDLOADER_MAXSIZE;
-
- if (ucb->header_address < min_address ||
- ucb->header_address > max_address - sizeof(boot_header_auth_t)) {
- return secfalse;
- }
-
- const boot_header_auth_t* hdr = (boot_header_auth_t*)ucb->header_address;
-
- // Get address range where the header and code can be located.
- // Both header and code must be inside flash area reserved for the
- // firmware and must not overlap with the address range where
- // the new bootloader will be written.
- min_address = NONBOARDLOADER_START + hdr->header_size + hdr->code_size;
- max_address = NONBOARDLOADER_START + NONBOARDLOADER_MAXSIZE;
-
- // Check if the entire boot header is within the valid address range
- if (ucb->header_address < min_address || hdr->header_size > max_address ||
- ucb->header_address > max_address - hdr->header_size) {
- return secfalse;
- }
-
- // Check if code (if present) is within the valid address range
- if (ucb->code_address != 0) {
- if (ucb->code_address < min_address || hdr->code_size > max_address ||
- ucb->code_address > max_address - hdr->code_size) {
- return secfalse;
- }
- }
-
- uint8_t hash[IMAGE_HASH_DIGEST_LENGTH]; // Hash of the header
-
- IMAGE_HASH_CTX ctx;
- IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, (const uint8_t*)hdr, hdr->header_size);
- IMAGE_HASH_FINAL(&ctx, hash);
-
- if (memcmp(hash, ucb->hash, sizeof(hash)) != 0) {
- // Header hash does not match the one stored in UCB
- // This can happen if the header was modified after the UCB was written.
- return secfalse;
- }
-
- return sectrue;
-}
-
-secbool boot_ucb_write(uint32_t header_address, uint32_t code_address) {
- boot_ucb_t ucb = {
- .magic = BOOT_UCB_MAGIC,
- .header_address = header_address,
- .code_address = code_address,
- };
-
- // Calculate the hash of the header
- boot_header_auth_t* hdr = (boot_header_auth_t*)header_address;
-
- IMAGE_HASH_CTX ctx;
- IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, (const uint8_t*)hdr, hdr->header_size);
- IMAGE_HASH_FINAL(&ctx, ucb.hash);
-
- secbool result = secfalse;
-
- mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_BOOTUCB);
-
- // Erase the UCB area
- if (sectrue != flash_area_erase(&BOOTUCB_AREA, NULL)) {
- goto cleanup;
- }
-
- if (sectrue != flash_unlock_write()) {
- goto cleanup;
- }
-
- // Write the UCB
- if (sectrue !=
- flash_area_write_data(&BOOTUCB_AREA, 0, (const void*)&ucb, sizeof(ucb))) {
- goto cleanup;
- }
-
- ensure(flash_lock_write(), NULL);
-
- result = sectrue;
-
-cleanup:
-
- mpu_restore(mpu_mode);
- return result;
-}
-
-secbool boot_ucb_erase(void) {
- mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_BOOTUCB);
- secbool result = flash_area_is_erased(&BOOTUCB_AREA);
- if (sectrue != result) {
- result = flash_area_erase(&BOOTUCB_AREA, NULL);
- }
- mpu_restore(mpu_mode);
- return result;
-}
-
-#endif // SECURE_MODE
diff --git a/core/embed/util/image/image.c b/core/embed/util/image/image.c
deleted file mode 100644
index a2c52d7f..00000000
--- a/core/embed/util/image/image.c
+++ /dev/null
@@ -1,511 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <trezor_model.h>
-#include <trezor_rtl.h>
-
-#include "ed25519-donna/ed25519.h"
-
-#include <sys/bootutils.h>
-#include <sys/flash.h>
-#include <util/image.h>
-
-#ifdef STM32F4
-_Static_assert(BOOTLOADER_VECTBL_OFFSET == IMAGE_HEADER_SIZE,
- "BOOTLOADER_VECTBL_OFFSET must match IMAGE_HEADER_SIZE");
-#endif
-
-_Static_assert(VENDOR_HEADER_MAX_SIZE + IMAGE_HEADER_SIZE <= IMAGE_CHUNK_SIZE,
- "The size of the firmware headers must be less than or equal to "
- "IMAGE_CHUNK_SIZE");
-
-const uint8_t BOARDLOADER_KEY_M = 2;
-const uint8_t BOARDLOADER_KEY_N = 3;
-static const uint8_t * const BOARDLOADER_KEYS[] = {
-#if !PRODUCTION
- (const uint8_t *)"\xdb\x99\x5f\xe2\x51\x69\xd1\x41\xca\xb9\xbb\xba\x92\xba\xa0\x1f\x9f\x2e\x1e\xce\x7d\xf4\xcb\x2a\xc0\x51\x90\xf3\x7f\xcc\x1f\x9d",
- (const uint8_t *)"\x21\x52\xf8\xd1\x9b\x79\x1d\x24\x45\x32\x42\xe1\x5f\x2e\xab\x6c\xb7\xcf\xfa\x7b\x6a\x5e\xd3\x00\x97\x96\x0e\x06\x98\x81\xdb\x12",
- (const uint8_t *)"\x22\xfc\x29\x77\x92\xf0\xb6\xff\xc0\xbf\xcf\xdb\x7e\xdb\x0c\x0a\xa1\x4e\x02\x5a\x36\x5e\xc0\xe3\x42\xe8\x6e\x38\x29\xcb\x74\xb6",
-#else
- MODEL_BOARDLOADER_KEYS
-#endif
-};
-
-const uint8_t BOOTLOADER_KEY_M = 2;
-const uint8_t BOOTLOADER_KEY_N = 3;
-static const uint8_t * const BOOTLOADER_KEYS[] = {
-#if !PRODUCTION
- /*** DEVEL/QA KEYS ***/
- (const uint8_t *)"\xd7\x59\x79\x3b\xbc\x13\xa2\x81\x9a\x82\x7c\x76\xad\xb6\xfb\xa8\xa4\x9a\xee\x00\x7f\x49\xf2\xd0\x99\x2d\x99\xb8\x25\xad\x2c\x48",
- (const uint8_t *)"\x63\x55\x69\x1c\x17\x8a\x8f\xf9\x10\x07\xa7\x47\x8a\xfb\x95\x5e\xf7\x35\x2c\x63\xe7\xb2\x57\x03\x98\x4c\xf7\x8b\x26\xe2\x1a\x56",
- (const uint8_t *)"\xee\x93\xa4\xf6\x6f\x8d\x16\xb8\x19\xbb\x9b\xeb\x9f\xfc\xcd\xfc\xdc\x14\x12\xe8\x7f\xee\x6a\x32\x4c\x2a\x99\xa1\xe0\xe6\x71\x48",
-#else
- MODEL_BOOTLOADER_KEYS
-#endif
-};
-
-#ifdef USE_SECMON_VERIFICATION
-const uint8_t SECMON_KEY_M = 2;
-const uint8_t SECMON_KEY_N = 3;
-static const uint8_t * const SECMON_KEYS[] = {
-#if !PRODUCTION
- /*** DEVEL/QA KEYS ***/
- (const uint8_t *)"\xdb\x99\x5f\xe2\x51\x69\xd1\x41\xca\xb9\xbb\xba\x92\xba\xa0\x1f\x9f\x2e\x1e\xce\x7d\xf4\xcb\x2a\xc0\x51\x90\xf3\x7f\xcc\x1f\x9d",
- (const uint8_t *)"\x21\x52\xf8\xd1\x9b\x79\x1d\x24\x45\x32\x42\xe1\x5f\x2e\xab\x6c\xb7\xcf\xfa\x7b\x6a\x5e\xd3\x00\x97\x96\x0e\x06\x98\x81\xdb\x12",
- (const uint8_t *)"\x22\xfc\x29\x77\x92\xf0\xb6\xff\xc0\xbf\xcf\xdb\x7e\xdb\x0c\x0a\xa1\x4e\x02\x5a\x36\x5e\xc0\xe3\x42\xe8\x6e\x38\x29\xcb\x74\xb6",
-#else
- MODEL_SECMON_KEYS
-#endif
-};
-#endif
-
-static secbool compute_pubkey(uint8_t sig_m, uint8_t sig_n,
- const uint8_t *const *pub, uint8_t sigmask,
- ed25519_public_key res) {
- if (0 == sig_m || 0 == sig_n) return secfalse;
- if (sig_m > sig_n) return secfalse;
-
- // discard bits higher than sig_n
- sigmask &= ((1 << sig_n) - 1);
-
- // remove if number of set bits in sigmask is not equal to sig_m
- if (__builtin_popcount(sigmask) != sig_m) return secfalse;
-
- ed25519_public_key keys[sig_m];
- int j = 0;
- for (int i = 0; i < sig_n; i++) {
- if ((1 << i) & sigmask) {
- memcpy(keys[j], pub[i], 32);
- j++;
- }
- }
-
- return sectrue * (0 == ed25519_cosi_combine_publickeys(res, keys, sig_m));
-}
-
-const image_header *read_image_header(const uint8_t *const data,
- const uint32_t magic,
- const uint32_t maxsize) {
- const image_header *hdr = (const image_header *)data;
-
- if (hdr->magic != magic) {
- return NULL;
- }
- if (hdr->hdrlen != IMAGE_HEADER_SIZE) {
- return NULL;
- }
-
- // TODO: expiry mechanism needs to be ironed out before production or those
- // devices won't accept expiring bootloaders (due to boardloader write
- // protection).
- // lowest bit is used for breaking compatibility between old TT bootloaders
- // and non TT images
- // which is evaluated in check_image_model function
- if ((hdr->expiry & 0xFFFFFFFE) != 0) return NULL;
-
- if (hdr->codelen > (maxsize - hdr->hdrlen)) return NULL;
- if ((hdr->hdrlen + hdr->codelen) < 4 * 1024) return NULL;
- if ((hdr->hdrlen + hdr->codelen) % 512 != 0) return NULL;
-
- return hdr;
-}
-
-secbool check_image_model(const image_header *const hdr) {
- // abusing expiry field to break compatibility of non-TT images with existing
- // bootloaders/boardloaders
-#ifdef TREZOR_MODEL_T2T1
- if (hdr->expiry == 0 && hdr->hw_model == 0 && hdr->hw_revision == 0) {
- // images for model TT older than this check
- return sectrue;
- }
-#else
- if ((hdr->expiry & 0x01) == 0) {
- // for models other than TT, expiry == 0 is unacceptable, as the image will
- // run on bootloaders older that this check
- return secfalse;
- }
-#endif
-
-#ifndef TREZOR_EMULATOR
- if (hdr->hw_model != HW_MODEL) {
- return secfalse;
- }
- if (hdr->hw_revision != HW_REVISION) {
- return secfalse;
- }
-#endif
-
- return sectrue;
-}
-
-void get_image_fingerprint(const image_header *const hdr, uint8_t *const out) {
- IMAGE_HASH_CTX ctx;
- IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, (uint8_t *)hdr, IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE);
- for (int i = 0; i < IMAGE_SIG_SIZE; i++) {
- IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)"\x00", 1);
- }
- IMAGE_HASH_FINAL(&ctx, out);
-}
-
-secbool check_image_header_sig(const image_header *const hdr, uint8_t key_m,
- uint8_t key_n, const uint8_t *const *keys) {
- // check header signature
-
- uint8_t fingerprint[32];
- get_image_fingerprint(hdr, fingerprint);
-
- ed25519_public_key pub;
- if (sectrue != compute_pubkey(key_m, key_n, keys, hdr->sigmask, pub))
- return secfalse;
-
- return sectrue *
- (0 == ed25519_sign_open(fingerprint, IMAGE_HASH_DIGEST_LENGTH, pub,
- *(const ed25519_signature *)hdr->sig));
-}
-
-#ifdef USE_SECMON_VERIFICATION
-const secmon_header_t *read_secmon_header(const uint8_t *const data,
- const uint32_t maxsize) {
- const secmon_header_t *hdr = (const secmon_header_t *)data;
-
- if (hdr->magic != SECMON_IMAGE_MAGIC) {
- return NULL;
- }
- if (hdr->hdrlen != SECMON_HEADER_SIZE) {
- return NULL;
- }
-
- if (hdr->codelen > (maxsize - hdr->hdrlen)) return secfalse;
- if ((hdr->hdrlen + hdr->codelen) < 4 * 1024) return secfalse;
- if ((hdr->hdrlen + hdr->codelen) % 512 != 0) return secfalse;
-
- return hdr;
-}
-
-secbool check_secmon_model(const secmon_header_t *const hdr) {
-#ifndef TREZOR_EMULATOR
- if (hdr->hw_model != HW_MODEL) {
- return secfalse;
- }
- if (hdr->hw_revision != HW_REVISION) {
- return secfalse;
- }
-#endif
-
- return sectrue;
-}
-
-void get_secmon_fingerprint(const secmon_header_t *const hdr,
- uint8_t *const out) {
- IMAGE_HASH_CTX ctx;
- IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, (uint8_t *)hdr, SECMON_HEADER_SIZE - IMAGE_SIG_SIZE);
- for (int i = 0; i < IMAGE_SIG_SIZE; i++) {
- IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)"\x00", 1);
- }
- IMAGE_HASH_FINAL(&ctx, out);
-}
-
-secbool check_secmon_header_sig(const secmon_header_t *const hdr) {
- // check header signature
-
- uint8_t fingerprint[32];
- get_secmon_fingerprint(hdr, fingerprint);
-
- ed25519_public_key pub;
- if (sectrue != compute_pubkey(SECMON_KEY_M, SECMON_KEY_N, SECMON_KEYS,
- hdr->sigmask, pub))
- return secfalse;
-
- return sectrue *
- (0 == ed25519_sign_open(fingerprint, IMAGE_HASH_DIGEST_LENGTH, pub,
- *(const ed25519_signature *)hdr->sig));
-}
-
-#ifdef SECURE_MODE
-secbool check_secmon_contents(const secmon_header_t *const hdr,
- size_t code_offset, const flash_area_t *area) {
- if (0 == area) {
- return secfalse;
- }
-
- // Check the secmon integrity, calculate and compare hash
- const void *data = flash_area_get_address(
- area, code_offset + SECMON_HEADER_SIZE, hdr->codelen);
- if (!data) {
- return secfalse;
- }
-
- if (sectrue != check_single_hash(hdr->hash, data, hdr->codelen)) {
- return secfalse;
- }
-
- return sectrue;
-}
-#endif // SECURE_MODE
-
-#endif // USE_SECMON_VERIFICATION
-
-secbool __wur read_vendor_header(const uint8_t *const data,
- vendor_header *const vhdr) {
- memcpy(&vhdr->magic, data, 4);
- if (vhdr->magic != 0x565A5254) return secfalse; // TRZV
-
- memcpy(&vhdr->hdrlen, data + 4, 4);
- if (vhdr->hdrlen > VENDOR_HEADER_MAX_SIZE) return secfalse;
-
- memcpy(&vhdr->expiry, data + 8, 4);
- if (vhdr->expiry != 0) return secfalse;
-
- vhdr->origin = data;
-
- memcpy(&vhdr->version, data + 12, 2);
-
- memcpy(&vhdr->vsig_m, data + 14, 1);
- memcpy(&vhdr->vsig_n, data + 15, 1);
- memcpy(&vhdr->vtrust, data + 16, 2);
- memcpy(&vhdr->hw_model, data + 18, 4);
- memcpy(&vhdr->fw_type, data + 22, 1);
-
- if (vhdr->vsig_n > MAX_VENDOR_PUBLIC_KEYS) {
- return secfalse;
- }
-
- for (int i = 0; i < vhdr->vsig_n; i++) {
- vhdr->vpub[i] = data + 32 + i * 32;
- }
- for (int i = vhdr->vsig_n; i < MAX_VENDOR_PUBLIC_KEYS; i++) {
- vhdr->vpub[i] = 0;
- }
-
- memcpy(&vhdr->vstr_len, data + 32 + vhdr->vsig_n * 32, 1);
-
- vhdr->vstr = (const char *)(data + 32 + vhdr->vsig_n * 32 + 1);
-
- vhdr->vimg = data + 32 + vhdr->vsig_n * 32 + 1 + vhdr->vstr_len;
- // align to 4 bytes
- vhdr->vimg += (-(uintptr_t)vhdr->vimg) & 3;
-
- memcpy(&vhdr->sigmask, data + vhdr->hdrlen - IMAGE_SIG_SIZE, 1);
-
- memcpy(vhdr->sig, data + vhdr->hdrlen - IMAGE_SIG_SIZE + 1,
- IMAGE_SIG_SIZE - 1);
-
- return sectrue;
-}
-
-secbool check_vendor_header_model(const vendor_header *const vhdr) {
-#ifdef TREZOR_MODEL_T2T1
- if (vhdr->hw_model == 0) {
- // vendor headers for model T have this field set to 0
- return sectrue;
- }
-#endif
- if (vhdr->hw_model == HW_MODEL) {
- return sectrue;
- }
-
- return secfalse;
-}
-
-secbool check_vendor_header_sig(const vendor_header *const vhdr, uint8_t key_m,
- uint8_t key_n, const uint8_t *const *keys) {
- if (vhdr == NULL) {
- return secfalse;
- }
-
- // check header signature
-
- uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
- IMAGE_HASH_CTX ctx;
- IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, vhdr->origin, vhdr->hdrlen - IMAGE_SIG_SIZE);
- for (int i = 0; i < IMAGE_SIG_SIZE; i++) {
- IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)"\x00", 1);
- }
- IMAGE_HASH_FINAL(&ctx, hash);
-
- ed25519_public_key pub;
- if (sectrue != compute_pubkey(key_m, key_n, keys, vhdr->sigmask, pub))
- return secfalse;
-
- return sectrue *
- (0 == ed25519_sign_open(hash, IMAGE_HASH_DIGEST_LENGTH, pub,
- *(const ed25519_signature *)vhdr->sig));
-}
-
-secbool check_vendor_header_keys(const vendor_header *const vhdr) {
- return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N,
- BOOTLOADER_KEYS);
-}
-
-void vendor_header_hash(const vendor_header *const vhdr, uint8_t *hash) {
- IMAGE_HASH_CTX ctx;
- IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)vhdr->vstr, vhdr->vstr_len);
- IMAGE_HASH_UPDATE(&ctx, (const uint8_t *)"Trezor Vendor Header", 20);
- IMAGE_HASH_FINAL(&ctx, hash);
-}
-
-secbool check_single_hash(const uint8_t *const hash, const uint8_t *const data,
- int len) {
- uint8_t s_c[IMAGE_HASH_DIGEST_LENGTH] = {0};
-
- IMAGE_HASH_CALC(data, len, s_c);
-
- return sectrue * (0 == memcmp(s_c, hash, IMAGE_HASH_DIGEST_LENGTH));
-}
-
-#ifdef KERNEL_MODE
-secbool check_image_contents(const image_header *const hdr, uint32_t firstskip,
- const flash_area_t *area) {
- if (0 == area) {
- return secfalse;
- }
-
- // Check the firmware integrity, calculate and compare hashes
-
- // check hashes of image chunks
- // we hash the image including the padding to the end of the area
- size_t offset = firstskip;
- size_t end_offset = offset + hdr->codelen;
-
- while (offset < end_offset) {
- size_t bytes_to_check = MIN(IMAGE_CHUNK_SIZE - (offset % IMAGE_CHUNK_SIZE),
- end_offset - offset);
-
- const void *data = flash_area_get_address(area, offset, bytes_to_check);
- if (!data) {
- return secfalse;
- }
-
- size_t hash_offset = (offset / IMAGE_CHUNK_SIZE) * 32;
- if (sectrue !=
- check_single_hash(hdr->hashes + hash_offset, data, bytes_to_check)) {
- return secfalse;
- }
-
- offset += bytes_to_check;
- }
-
- // Check the padding to the end of the area
- end_offset = flash_area_get_size(area);
-
- if (offset < end_offset) {
- // Use the first byte in the checked area as the expected padding byte
- // Firmware is always padded with 0xFF, while the bootloader might be
- // padded with 0x00 as well
- uint8_t expected_byte = *(
- (const uint8_t *)flash_area_get_address(area, offset, sizeof(uint8_t)));
-
- if (expected_byte != 0x00 && expected_byte != 0xFF) {
- return secfalse;
- }
-
- uint32_t expected_word = expected_byte << 24 | expected_byte << 16 |
- expected_byte << 8 | expected_byte;
-
- while (offset < end_offset) {
- size_t bytes_to_check = MIN(
- IMAGE_CHUNK_SIZE - (offset % IMAGE_CHUNK_SIZE), end_offset - offset);
- size_t words_to_check = bytes_to_check / sizeof(uint32_t);
- size_t single_bytes_to_check = bytes_to_check % sizeof(uint32_t);
-
- const uint8_t *bytes = (const uint8_t *)flash_area_get_address(
- area, offset, single_bytes_to_check);
- if (!bytes) {
- return secfalse;
- }
-
- for (size_t i = 0; i < single_bytes_to_check; i++) {
- if (bytes[i] != expected_byte) {
- return secfalse;
- }
- }
-
- offset += single_bytes_to_check;
-
- const uint32_t *data = (const uint32_t *)flash_area_get_address(
- area, offset, bytes_to_check - single_bytes_to_check);
- if (!data) {
- return secfalse;
- }
-
- for (size_t i = 0; i < words_to_check; i++) {
- if (data[i] != expected_word) {
- return secfalse;
- }
- }
-
- offset += words_to_check * sizeof(uint32_t);
- }
- }
-
- return sectrue;
-}
-#endif // KERNEL_MODE
-
-secbool check_firmware_header(const uint8_t *header, size_t header_size,
- firmware_header_info_t *info) {
- // parse and check vendor header
- vendor_header vhdr;
- if (sectrue != read_vendor_header(header, &vhdr)) {
- return secfalse;
- }
- if (sectrue != check_vendor_header_keys(&vhdr)) {
- return secfalse;
- }
-
- // parse and check image header
- const image_header *ihdr;
- if ((ihdr = read_image_header(header + vhdr.hdrlen, FIRMWARE_IMAGE_MAGIC,
- FIRMWARE_MAXSIZE)) == NULL) {
- return secfalse;
- }
- if (sectrue !=
- check_image_header_sig(ihdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub)) {
- return secfalse;
- }
-
- // copy vendor string
- info->vstr_len = MIN(sizeof(info->vstr), vhdr.vstr_len);
- if (info->vstr_len > 0) {
- memcpy(info->vstr, vhdr.vstr, info->vstr_len);
- }
-
- // copy firmware version
- info->ver_major = ihdr->version & 0xFF;
- info->ver_minor = (ihdr->version >> 8) & 0xFF;
- info->ver_patch = (ihdr->version >> 16) & 0xFF;
- info->ver_build = (ihdr->version >> 24) & 0xFF;
-
- // calculate and copy the image fingerprint
- get_image_fingerprint(ihdr, info->fingerprint);
-
- // calculate hash of both vendor and image headers
- IMAGE_HASH_CALC(header, vhdr.hdrlen + ihdr->hdrlen, info->hash);
-
- return sectrue;
-}
-
-secbool check_bootloader_header_sig(const image_header *const hdr) {
- return check_image_header_sig(hdr, BOARDLOADER_KEY_M, BOARDLOADER_KEY_N,
- BOARDLOADER_KEYS);
-}
diff --git a/core/embed/util/image/inc/util/boot_header.h b/core/embed/util/image/inc/util/boot_header.h
deleted file mode 100644
index 8c036542..00000000
--- a/core/embed/util/image/inc/util/boot_header.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <trezor_types.h>
-
-// Magic number at the start of the boot header
-#define BOOT_HEADER_MAGIC_TRZQ 0x515A5254 // TRZQ
-// Reserved space for boot header
-#define BOOT_HEADER_MAXSIZE (2 * 8192)
-/** Number of PQ/EC signatures */
-#define BOOT_HEADER_SIGNATURE_COUNT 2
-/** Length of PQ signature in bytes */
-#define BOOT_HEADER_PQ_SIGNATURE_LEN (7856)
-/** Length of EC signature in bytes */
-#define BOOT_HEADER_EC_SIGNATURE_LEN (64)
-/** Number of reserved slots for Merkle proof */
-#define BOOT_HEADER_MERKLE_PROOF_MAXLEN (256)
-
-/**
- * 4-byte version structure used in the boot header
- */
-typedef struct __attribute__((packed)) {
- uint8_t major;
- uint8_t minor;
- uint8_t patch;
- uint8_t build;
-} boot_header_version_t;
-
-/**
- * Merkle proof node (SHA-256 digest)
- */
-typedef struct {
- uint8_t bytes[32];
-} merkle_proof_node_t;
-
-/**
- * Authenticated part of the boot header
- *
- * This structure can be extended in future versions if needed.
- * Just make sure to add new fields at the end of the structure.
- * Never remove or reorder existing fields.
- */
-typedef struct __attribute__((packed)) {
- /** Magic constant 'TRZQ' */
- uint32_t magic;
- /** Hardware model, e.g. 'T3W1'. */
- uint32_t hw_model;
- /** Hardware revision, e.g. 1 */
- uint32_t hw_revision;
- /** Bootloader version */
- boot_header_version_t version;
- /** Minimum version that the device can be downgraded to without
- * erasing storage. */
- boot_header_version_t fix_version;
- /** Minimum previous version that the device can be updated from when
- * installing this header. */
- boot_header_version_t min_prev_version;
- /** An integer which must not decrease between updates. */
- uint8_t monotonic_version;
- /** Bitmask of keys used for signature verification.
- * Each bit corresponds to a public key in the BOOTLOADER_PQ_KEY and
- * BOOTLOADER_EC_KEY arrays. If the bit is set, the corresponding key
- * is used for signature verification. */
- uint8_t sigmask;
- /* Reserved bytes (alignment) */
- uint8_t reserved[2];
- /** Size of the entire header in bytes, including the Merkle proof
- * and signatures. It's calculated at link time and must be
- * aligned to 8K boundary. */
- uint32_t header_size;
- /** Size of the authenticated part of the header in bytes.
- * Final value is calculated in post-build step and includes
- * potential padding of the structure. */
- uint32_t auth_size;
- /** Size of the bootloader code in bytes */
- uint32_t code_size;
- /** Address of storage area for storage relocation purposes */
- uint32_t storage_address;
- /* Firmware Merkle tree root */
- merkle_proof_node_t firmware_root;
- /* Padding is automatically added by the post-build step to ensure that
- * the authenticated part of the header is maximized. */
- uint8_t padding[0];
-
-} boot_header_auth_t;
-
-/**
- * Merkle proof structure used in the boot header to calculate the root
- * of the Merkle tree. It is placed just after the authenticated part
- * of the boot header.
- *
- * WARNING: This struct must not change, otherwise old boardloaders will
- * not interpret the header correctly. Any new unauthenticated fields
- * must be added at the end of `boot_header_unauth_t`.
- */
-typedef struct __attribute__((packed)) {
- /** Number of nodes in the array below */
- uint32_t node_count;
- /** Merkle proof used for root calculation */
- merkle_proof_node_t nodes[0];
-
-} boot_header_merkle_proof_t;
-
-/**
- * Unauthenticated part of the boot header containing signatures and
- * other information that need not (or must not) be authenticated.
- * It is placed right after the Merkle proof.
- */
-typedef struct __attribute__((packed)) {
- /** PQ signatures */
- uint8_t slh_signature[BOOT_HEADER_SIGNATURE_COUNT]
- [BOOT_HEADER_PQ_SIGNATURE_LEN];
- /** EC signatures */
- uint8_t ec_signature[BOOT_HEADER_SIGNATURE_COUNT]
- [BOOT_HEADER_EC_SIGNATURE_LEN];
-
- /* Firmware type
- * This field is modified by the bootloader during the
- * update process). It indicates the current firmware type (custom,
- * universal, bitcoin-only, etc.) and is used to determine whether
- * the storage should be erased before the update.
- */
- uint8_t firmware_type;
- uint8_t padding[3];
-
-} boot_header_unauth_t;
-
-/**
- * Verifies the integrity of the boot header.
- *
- * Checks the magic number, header size, code size, hardware model and revision
- *
- * @param address Address of the boot header in flash memory
- * @return Pointer to the boot header if valid, NULL otherwise.
- */
-const boot_header_auth_t* boot_header_auth_get(uint32_t address);
-
-/**
- * Gets pointer to the unauthenticated part of the boot header.
- *
- * @param hdr Pointer to the authenticated part of the boot header
- * @return Pointer to the unauthenticated part of the boot header or NULL if the
- * header is invalid.
- */
-const boot_header_unauth_t* boot_header_unauth_get(
- const boot_header_auth_t* hdr);
-
-/**
- * Calculates the Merkle root for signature verification.
- *
- * The result includes the bootloader code, authenticated part of the
- * boot header and the Merkle tree path.
- *
- * @param hdr Pointer to the boot header
- * @param code_address Address of the bootloader code in flash memory
- * @param root Pointer to the output Merkle root node
- */
-void boot_header_calc_merkle_root(const boot_header_auth_t* hdr,
- uint32_t code_address,
- merkle_proof_node_t* root);
-
-/**
- * Checks the signature in the boot header against the public keys.
- *
- * This function checks the signatures of the boot header using the
- * bootloader public keys. It uses the Merkle root calculated from
- * the boot header and bootloader code to perform the verification.
- *
- * @param hdr Pointer to the boot header
- * @param merkle_root Pointer to the Merkle root
- * @return secbool indicating whether the signature verification was successful.
- */
-secbool boot_header_check_signature(const boot_header_auth_t* hdr,
- const merkle_proof_node_t* merkle_root);
-
-/**
- * This function compares the boot header and the bootloader code
- * with the installed bootloader header and code. If they are different,
- * it returns sectrue, otherwise secfalse.
- *
- * @param hdr Pointer to the new boot header
- * @param code_address Address of the new bootloader code in flash memory
- * @return secbool indicating whether the boot header and code need update
- */
-secbool bootloader_area_needs_update(const boot_header_auth_t* hdr,
- uint32_t code_address);
diff --git a/core/embed/util/image/inc/util/boot_image.h b/core/embed/util/image/inc/util/boot_image.h
deleted file mode 100644
index 7e647897..00000000
--- a/core/embed/util/image/inc/util/boot_image.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <trezor_types.h>
-
-/**
- * Structure representing a bootloader image and all its associated data.
- */
-typedef struct {
- const void* image_ptr;
- size_t image_size;
-#ifndef USE_BOOT_UCB
- uint8_t hash_00[32];
- uint8_t hash_FF[32];
-#endif
-} boot_image_t;
-
-/**
- * @brief Get the new bootloader available as a part of the build.
- *
- * This function retrieves the bootloader image that is
- * included in the build. The image is expected to be padded
- * with 0x00 and 0xFF bytes to match the expected size.
- *
- * @return Pointer to a `boot_image_t` structure containing the
- * image data, size, and expected hashes.
- */
-const boot_image_t* boot_image_get_embdata(void);
-
-/**
- * @brief Verify the installed bootloader against expected hashes.
- *
- * Calculates the hash of the currently installed bootloader and compares
- * it against two known-good expected hashes.
- *
- * @param image Pointer to the `boot_image_t` structure containing
- * the expected hashes and other image data.
- *
- * @return `true` if the installed bootloader's hash does not match either
- * of the expected hashes (indicating it should be replaced),
- * `false` if it matches one of them.
- */
-bool boot_image_check(const boot_image_t* image);
-
-/**
- * @brief Replace the currently installed bootloader.
- *
- * Writes a new bootloader image into flash.
- *
- * @param image Pointer to the `boot_image_t` structure containing
- * the new bootloader image data.
- */
-void boot_image_replace(const boot_image_t* image);
diff --git a/core/embed/util/image/inc/util/boot_ucb.h b/core/embed/util/image/inc/util/boot_ucb.h
deleted file mode 100644
index add0d15c..00000000
--- a/core/embed/util/image/inc/util/boot_ucb.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <trezor_types.h>
-
-#include <util/boot_header.h>
-#include <util/image_hash_conf.h>
-
-/**
- * Update control block structure sitting on fixed address in flash memory
- */
-typedef struct {
- /** Magic constant checked in boardloader */
- uint32_t magic;
- /** Address of the start of the header structure */
- uint32_t header_address;
- /** Address of the start of the bootloader code in flash memory */
- uint32_t code_address;
- /** Padding to align the structure to 16-bytes */
- uint32_t padding;
- /** Hash of the boot header
- * This is used to verify that the boot header has not changed
- * since the UCB was written. */
- uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
-
-} boot_ucb_t;
-
-/**
- * Reads the update control block from flash memory, checks its integrity and
- * copies it to the provided structure.
- *
- * @param ucb Pointer to the boot update control block structure to be filled
- * @return sectrue if the read was successful and the update control block is
- * valid, secfalse otherwise.
- */
-
-secbool boot_ucb_read(boot_ucb_t* ucb);
-
-/**
- * Writes to the update control block in flash memory.
- *
- * This function is called by the code that updates the bootloader
- * or the bootloader header. Before calling this function, the updater must
- * store the bootloader header and code in flash memory.
- *
- * @param header_address Address of the start of the boot header in flash
- * memory. This parameter is mandatory.
- * @param code_address Address of the start of the bootloader code in flash
- * memory. If the code is not present, it is expected that only the
- * header will be updated and this parameter should be set to 0.
- * @return sectrue if the write was successful, secfalse otherwise.
- */
-secbool boot_ucb_write(uint32_t header_address, uint32_t code_address);
-
-/**
- * Erases the update control block in flash memory.
- *
- * This function is called by the bootloader to finalize
- * the update process and to ensure that the boardloader will not
- * repeat the update process if it was already done.
- *
- * @return sectrue if the erase was successful, secfalse otherwise.
- */
-secbool boot_ucb_erase(void);
diff --git a/core/embed/util/image/inc/util/image.h b/core/embed/util/image/inc/util/image.h
deleted file mode 100644
index 4b92b132..00000000
--- a/core/embed/util/image/inc/util/image.h
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <trezor_model.h>
-#include <trezor_types.h>
-
-#include <sys/flash.h>
-#include "blake2s.h"
-
-#include "image_hash_conf.h"
-
-#define VENDOR_HEADER_MAX_SIZE (64 * 1024)
-#define IMAGE_HEADER_SIZE 0x400 // size of the bootloader or firmware header
-#define SECMON_HEADER_SIZE 0x200
-#define IMAGE_SIG_SIZE 65
-#define IMAGE_INIT_CHUNK_SIZE (16 * 1024)
-
-#define BOOTLOADER_IMAGE_MAGIC 0x425A5254 // TRZB
-
-#define FIRMWARE_IMAGE_MAGIC 0x465A5254 // TRZF
-
-#define SECMON_IMAGE_MAGIC 0x43455354 // TSEC
-
-typedef struct {
- uint32_t magic;
- uint32_t hdrlen;
- uint32_t expiry;
- uint32_t codelen;
- uint32_t version;
- uint32_t fix_version;
- uint32_t hw_model;
- uint8_t hw_revision;
- uint8_t monotonic;
- uint8_t reserved_0[2];
- uint8_t hashes[512];
- uint8_t reserved_1[415];
- uint8_t sigmask;
- uint8_t sig[64];
-} image_header;
-
-#define MAX_VENDOR_PUBLIC_KEYS 8
-
-// The mask of the vendor screen wait time in seconds, encoded in bitwise
-// complement form.
-#define VTRUST_WAIT_MASK 0x000F
-
-// Use black background instead of red one in the vendor screen.
-#define VTRUST_NO_RED 0x0010
-
-// Do not require user click to leave the vendor screen.
-#define VTRUST_NO_CLICK 0x0020
-
-// Do not show vendor string in the vendor screen.
-#define VTRUST_NO_STRING 0x0040
-
-// Two bits for historical reasons. On T2B1, only the lower bit was used with
-// inverted logic (due to late inclusion of the secret handling during
-// development process). On T3T1, we decided to remedy the situation by
-// including the upper bit as well.
-#define VTRUST_SECRET_MASK 0x0180
-#define VTRUST_SECRET_ALLOW 0x0100
-
-#define VTRUST_NO_WARNING \
- (VTRUST_WAIT_MASK | VTRUST_NO_RED | VTRUST_NO_CLICK | VTRUST_NO_STRING)
-
-#define VTRUST_ALLOW_PROVISIONING 0x200
-#define VTRUST_ALLOW_UNLIMITED_RUN 0x400
-
-// Globally defined values for the `vendor_header.fw_type` field.
-// !!! Do not modify existing values. Only add new ones if needed.
-//
-typedef enum {
- // Reserved value (may appear in legacy vendor headers)
- VENDOR_FW_TYPE_RESERVED = 0,
- // Custom (unsafe) firmware
- VENDOR_FW_TYPE_CUSTOM = 1,
- // Trezor Universal firmware
- VENDOR_FW_TYPE_UNIVERSAL = 2,
- // Trezor Bitcoin-only firmware
- VENDOR_FW_TYPE_BTC_ONLY = 3,
- // Factory tester firmware
- VENDOR_FW_TYPE_PRODTEST = 4,
-} vendor_fw_type_t;
-
-typedef struct {
- uint32_t magic;
- uint32_t hdrlen;
- uint32_t expiry;
- uint16_t version;
- uint8_t vsig_m;
- uint8_t vsig_n;
- uint16_t vtrust;
- uint32_t hw_model;
- uint8_t fw_type;
- // uint8_t reserved[10];
- const uint8_t *vpub[MAX_VENDOR_PUBLIC_KEYS];
- uint8_t vstr_len;
- const char *vstr;
- const uint8_t *vimg;
- uint8_t sigmask;
- uint8_t sig[64];
- const uint8_t *origin; // pointer to the underlying data
-} vendor_header;
-
-typedef struct {
- // vendor string
- uint8_t vstr[64];
- // vendor string length
- size_t vstr_len;
- // firmware version
- uint8_t ver_major;
- uint8_t ver_minor;
- uint8_t ver_patch;
- uint8_t ver_build;
- // firmware fingerprint
- uint8_t fingerprint[IMAGE_HASH_DIGEST_LENGTH];
- // hash of vendor and image header
- uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
-} firmware_header_info_t;
-
-typedef struct {
- uint32_t magic;
- uint32_t hdrlen;
- uint32_t codelen;
- uint32_t version;
- uint32_t hw_model;
- uint8_t hw_revision;
- uint8_t monotonic;
- uint8_t reserved_0[2];
- uint8_t hash[32];
- uint8_t reserved_1[391];
- uint8_t sigmask;
- uint8_t sig[64];
-} secmon_header_t;
-
-const image_header *read_image_header(const uint8_t *const data,
- const uint32_t magic,
- const uint32_t maxsize);
-
-secbool __wur check_image_model(const image_header *const hdr);
-
-secbool __wur check_image_header_sig(const image_header *const hdr,
- uint8_t key_m, uint8_t key_n,
- const uint8_t *const *keys);
-
-secbool __wur read_vendor_header(const uint8_t *const data,
- vendor_header *const vhdr);
-
-secbool __wur check_vendor_header_model(const vendor_header *const vhdr);
-
-secbool __wur check_vendor_header_sig(const vendor_header *const vhdr,
- uint8_t key_m, uint8_t key_n,
- const uint8_t *const *keys);
-
-secbool check_vendor_header_keys(const vendor_header *const vhdr);
-
-void vendor_header_hash(const vendor_header *const vhdr, uint8_t *hash);
-
-secbool __wur check_single_hash(const uint8_t *const hash,
- const uint8_t *const data, int len);
-
-#ifdef KERNEL_MODE
-secbool __wur check_image_contents(const image_header *const hdr,
- uint32_t firstskip,
- const flash_area_t *area);
-#endif
-
-void get_image_fingerprint(const image_header *const hdr, uint8_t *const out);
-
-secbool check_firmware_header(const uint8_t *header, size_t header_size,
- firmware_header_info_t *info);
-
-secbool __wur check_bootloader_header_sig(const image_header *const hdr);
-
-#ifdef USE_SECMON_VERIFICATION
-const secmon_header_t *read_secmon_header(const uint8_t *const data,
- const uint32_t maxsize);
-
-secbool __wur check_secmon_model(const secmon_header_t *const hdr);
-
-secbool __wur check_secmon_header_sig(const secmon_header_t *const hdr);
-
-#ifdef SECURE_MODE
-secbool __wur check_secmon_contents(const secmon_header_t *const hdr,
- size_t code_offset,
- const flash_area_t *area);
-#endif
-#endif
diff --git a/core/embed/util/image/inc/util/image_hash_conf.h b/core/embed/util/image/inc/util/image_hash_conf.h
deleted file mode 100644
index 5c16da33..00000000
--- a/core/embed/util/image/inc/util/image_hash_conf.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * This file is part of the Trezor project, https://trezor.io/
- *
- * Copyright (c) SatoshiLabs
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <trezor_model.h>
-#include <trezor_types.h>
-
-#ifdef IMAGE_HASH_SHA256
-#include "sha2.h"
-#define IMAGE_HASH_DIGEST_LENGTH SHA256_DIGEST_LENGTH
-#if defined(USE_HASH_PROCESSOR) && defined(SECURE_MODE)
-#include <sec/hash_processor.h>
-#define IMAGE_HASH_CTX hash_sha256_context_t
-#define IMAGE_HASH_INIT(ctx) hash_processor_sha256_init(ctx)
-#define IMAGE_HASH_UPDATE(ctx, data, len) \
- hash_processor_sha256_update(ctx, data, len)
-#define IMAGE_HASH_FINAL(ctx, output) hash_processor_sha256_final(ctx, output)
-#define IMAGE_HASH_CALC(data, len, output) \
- hash_processor_sha256_calc(data, len, output)
-#else
-#define IMAGE_HASH_CTX SHA256_CTX
-#define IMAGE_HASH_INIT(ctx) sha256_Init(ctx)
-#define IMAGE_HASH_UPDATE(ctx, data, len) sha256_Update(ctx, data, len)
-#define IMAGE_HASH_FINAL(ctx, output) sha256_Final(ctx, output)
-#define IMAGE_HASH_CALC(data, len, output) sha256_Raw(data, len, output)
-#endif
-
-#elif defined IMAGE_HASH_BLAKE2S
-#include "blake2s.h"
-#define IMAGE_HASH_DIGEST_LENGTH BLAKE2S_DIGEST_LENGTH
-#define IMAGE_HASH_CTX BLAKE2S_CTX
-#define IMAGE_HASH_INIT(ctx) blake2s_Init(ctx, BLAKE2S_DIGEST_LENGTH)
-#define IMAGE_HASH_UPDATE(ctx, data, len) blake2s_Update(ctx, data, len)
-#define IMAGE_HASH_FINAL(ctx, output) \
- blake2s_Final(ctx, output, BLAKE2S_DIGEST_LENGTH)
-#define IMAGE_HASH_CALC(data, len, output) \
- blake2s(data, len, output, BLAKE2S_DIGEST_LENGTH)
-#else
-#error "IMAGE_HASH_SHA256 or IMAGE_HASH_BLAKE2S must be defined"
-#endif
diff --git a/core/site_scons/models/D002/discovery2.py b/core/site_scons/models/D002/discovery2.py
index a303dff0..0e17e8e1 100644
--- a/core/site_scons/models/D002/discovery2.py
+++ b/core/site_scons/models/D002/discovery2.py
@@ -54,8 +54,8 @@ def configure(
]
if "boot_ucb" in features_wanted:
- sources += ["embed/util/image/boot_header.c"]
- sources += ["embed/util/image/boot_ucb.c"]
+ sources += ["embed/sec/image/boot_header.c"]
+ sources += ["embed/sec/image/boot_ucb.c"]
defines += [("USE_BOOT_UCB", "1")]
features_available.append("boot_ucb")
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
index 9416c492..0a47c70a 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
@@ -62,8 +62,8 @@ def configure(
]
if "boot_ucb" in features_wanted:
- sources += ["embed/util/image/boot_header.c"]
- sources += ["embed/util/image/boot_ucb.c"]
+ sources += ["embed/sec/image/boot_header.c"]
+ sources += ["embed/sec/image/boot_ucb.c"]
defines += [("USE_BOOT_UCB", "1")]
features_available.append("boot_ucb")
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revB.py b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
index 38b44a21..6fc8b2a2 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revB.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
@@ -62,8 +62,8 @@ def configure(
]
if "boot_ucb" in features_wanted:
- sources += ["embed/util/image/boot_header.c"]
- sources += ["embed/util/image/boot_ucb.c"]
+ sources += ["embed/sec/image/boot_header.c"]
+ sources += ["embed/sec/image/boot_ucb.c"]
defines += [("USE_BOOT_UCB", "1")]
features_available.append("boot_ucb")
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revC.py b/core/site_scons/models/T3W1/trezor_t3w1_revC.py
index d16a9bc6..a8724b8e 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revC.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revC.py
@@ -62,8 +62,8 @@ def configure(
]
if "boot_ucb" in features_wanted:
- sources += ["embed/util/image/boot_header.c"]
- sources += ["embed/util/image/boot_ucb.c"]
+ sources += ["embed/sec/image/boot_header.c"]
+ sources += ["embed/sec/image/boot_ucb.c"]
defines += [("USE_BOOT_UCB", "1")]
features_available.append("boot_ucb")
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.