refactor(core): relocate bootloader header, fix bootloader_ci build
What changed, and why it matters
This is a code cleanup and build-fix change for the Trezor firmware bootloader. It moves the bootloader's binary header definition from a shared security file into a bootloader-specific file, and fixes the build configuration for a special continuous-integration (CI) bootloader variant. There is no direct evidence in the commit that this fixes an exploitable security vulnerability; it appears to be a refactoring and build repair.
Treat as a routine refactor/build-fix. Reviewers may optionally verify that the relocated header definition is byte-identical to the removed one and that the CI bootloader produces a valid signed image, but no urgent security action is indicated by this commit alone.
Security signals we found
Bootloader header structure moved from shared security code to project-specific file
CI bootloader build configuration corrected to use correct header source and header tool
Signature mask and authenticated-size fields are still explicitly zeroed/placeholdered, with comments noting headertool_pq must fill them later
Evidence from the diff
The commit relocates the g_bootloader_header definition and its boot_header_padded_t union out of core/embed/sec/image/boot_header.c and into a new file core/embed/projects/bootloader/header_pq.c. It updates both core/SConscript.bootloader and core/SConscript.bootloader_ci to include header_pq.c when the boot_ucb feature is available, and to use the headertool_pq header tool in that case. For the CI bootloader without boot_ucb, it keeps the old assembly header and headertool. It also adds -lm to the CI bootloader link command and removes a duplicate HEADERTOOL setting. The change is described by the author as a refactor to fix the bootloader_ci build.
Changed components
Trezor Core bootloader build systemTrezor Core CI bootloader build systemcore/embed/projects/bootloader/header_pq.ccore/embed/sec/image/boot_header.ccore/SConscript.bootloadercore/SConscript.bootloader_ciInspect captured patch +89 / −94
diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader
index 603148ea..ec8f8d73 100644
--- a/core/SConscript.bootloader
+++ b/core/SConscript.bootloader
@@ -178,6 +178,9 @@ if DEBUGLINK:
]
if "boot_ucb" in FEATURES_AVAILABLE:
+ SOURCE_BOOTLOADER += [
+ 'embed/projects/bootloader/header_pq.c',
+ ]
env.Replace(
HEADERTOOL='headertool_pq',
)
diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci
index 09a3192c..c5742556 100644
--- a/core/SConscript.bootloader_ci
+++ b/core/SConscript.bootloader_ci
@@ -128,10 +128,20 @@ SOURCE_BOOTLOADER = [
'embed/projects/bootloader_ci/version_check.c',
]
-if not "boot_ucb" in FEATURES_AVAILABLE:
+if "boot_ucb" in FEATURES_AVAILABLE:
+ SOURCE_BOOTLOADER += [
+ 'embed/projects/bootloader/header_pq.c',
+ ]
+ env.Replace(
+ HEADERTOOL='headertool_pq',
+ )
+else:
SOURCE_BOOTLOADER += [
'embed/projects/bootloader/header.S',
]
+ env.Replace(
+ HEADERTOOL='headertool',
+ )
env.Replace(
CAT='cat',
@@ -186,10 +196,6 @@ env.Replace(
ASPPFLAGS='$CFLAGS $CCFLAGS',
ALLSOURCES=SOURCE_MOD + SOURCE_MOD_CRYPTO + SOURCE_BOOTLOADER + SOURCE_HAL+ SOURCE_NANOPB, )
-env.Replace(
- HEADERTOOL='headertool',
-)
-
env.Replace(
ALLSOURCES=SOURCE_MOD + SOURCE_MOD_CRYPTO + SOURCE_BOOTLOADER + SOURCE_NANOPB + SOURCE_HAL,
ALLDEFS=tools.get_defs_for_cmake(env['CPPDEFINES'] + env['CPPDEFINES_IMPLICIT']))
@@ -227,7 +233,7 @@ program_elf = env.Command(
target='bootloader.elf',
source=obj_program,
action=
- '$LINK -o $TARGET $CCFLAGS $CFLAGS $LINKFLAGS $SOURCES -lc_nano -lgcc',
+ '$LINK -o $TARGET $CCFLAGS $CFLAGS $LINKFLAGS $SOURCES -lc_nano -lgcc -lm',
)
env.Depends(program_elf, linkerscript_gen)
diff --git a/core/embed/projects/bootloader/header_pq.c b/core/embed/projects/bootloader/header_pq.c
new file mode 100644
index 00000000..70cd3650
--- /dev/null
+++ b/core/embed/projects/bootloader/header_pq.c
@@ -0,0 +1,74 @@
+/*
+ * 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 <sec/boot_header.h>
+
+#include <version.h>
+
+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,
+ }};
diff --git a/core/embed/projects/bootloader_ci/header.S b/core/embed/projects/bootloader_ci/header.S
deleted file mode 100644
index 29f9a9fd..00000000
--- a/core/embed/projects/bootloader_ci/header.S
+++ /dev/null
@@ -1,35 +0,0 @@
- .syntax unified
-
-#include "version.h"
-
- .section .header, "a"
-
- .type g_header, %object
- .size g_header, .-g_header
-
-g_header:
- .byte 'T','R','Z','B' // magic
- .word g_header_end - g_header // hdrlen
-#ifdef TREZOR_MODEL_T2T1
- .word 0 // expiry
-#else
- .word 1 // expiry
-#endif
- .word _codelen // codelen
- .byte VERSION_MAJOR // vmajor
- .byte VERSION_MINOR // vminor
- .byte VERSION_PATCH // vpatch
- .byte VERSION_BUILD // vbuild
- .byte FIX_VERSION_MAJOR // fix_vmajor
- .byte FIX_VERSION_MINOR // fix_vminor
- .byte FIX_VERSION_PATCH // fix_vpatch
- .byte FIX_VERSION_BUILD // fix_vbuild
- .word HW_MODEL // type of the designated hardware
- .byte HW_REVISION // revision of the designated hardware
- .byte BOOTLOADER_MONOTONIC_VERSION // monotonic version of the binary
- . = . + 2 // reserved
- . = . + 512 // hash1 ... hash16
- . = . + 415 // reserved
- .byte 0 // sigmask
- . = . + 64 // sig
-g_header_end:
diff --git a/core/embed/sec/image/boot_header.c b/core/embed/sec/image/boot_header.c
index 72770a32..842b9ed7 100644
--- a/core/embed/sec/image/boot_header.c
+++ b/core/embed/sec/image/boot_header.c
@@ -29,59 +29,6 @@
#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",
Why this scored 18/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.