From 2bd3b6ff06159a15447c9e6ea73692149682a49f Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Wed, 23 Jul 2025 17:20:50 +0200 Subject: [PATCH] fix(core): fix bootloader monotonic check during upgrade [no changelog] --- core/embed/models/D002/memory.h | 8 ++--- core/embed/models/D002/memory.ld | 8 ++--- core/embed/models/D002/memory_secmon.h | 8 ++--- core/embed/models/D002/memory_secmon.ld | 8 ++--- core/embed/projects/boardloader/main.c | 12 ++++---- .../prodtest/cmd/prodtest_bootloader.c | 2 +- core/embed/util/flash/stm32u5/flash_layout.c | 2 ++ core/embed/util/image/boot_header.c | 29 +++++++++--------- core/embed/util/image/boot_image.c | 28 +++++++++-------- core/embed/util/image/boot_image_embdata.c | 2 ++ core/embed/util/image/boot_ucb.c | 6 ++-- core/embed/util/image/inc/util/boot_header.h | 18 ++++++----- core/embed/util/image/inc/util/boot_image.h | 3 +- core/tools/trezor_core_tools/headertool_pq.py | 20 ++++++------- .../trezorlib/_internal/firmware_headers.py | 7 +++-- python/src/trezorlib/firmware/core.py | 30 ++++++++++++++----- 16 files changed, 109 insertions(+), 82 deletions(-) diff --git a/core/embed/models/D002/memory.h b/core/embed/models/D002/memory.h index 416271a686..8854d28359 100644 --- a/core/embed/models/D002/memory.h +++ b/core/embed/models/D002/memory.h @@ -50,10 +50,10 @@ #define BOOTUCB_SECTOR_END 0xE // Non-boardloader area (includes bootloader, firmware, assets and storage) -#define NONBLDR_START (0x0C01E000) -#define NONBLDR_MAXSIZE (497 * 8 * 1024) // 3976 kB -#define NONBLDR_SECTOR_START 0xF -#define NONBLDR_SECTOR_END 0x1FF +#define NONBOARDLOADER_START (0x0C01E000) +#define NONBOARDLOADER_MAXSIZE (497 * 8 * 1024) // 3976 kB +#define NONBOARDLOADER_SECTOR_START 0xF +#define NONBOARDLOADER_SECTOR_END 0x1FF #define BOOTLOADER_START (0x0C01E000) #define BOOTLOADER_MAXSIZE (32 * 8 * 1024) // 256 kB diff --git a/core/embed/models/D002/memory.ld b/core/embed/models/D002/memory.ld index ec16327475..f148e844a8 100644 --- a/core/embed/models/D002/memory.ld +++ b/core/embed/models/D002/memory.ld @@ -19,10 +19,10 @@ BOOTUCB_START = 0xc01c000; BOOTUCB_MAXSIZE = 0x2000; BOOTUCB_SECTOR_START = 0xe; BOOTUCB_SECTOR_END = 0xe; -NONBLDR_START = 0xc01e000; -NONBLDR_MAXSIZE = 0x3e2000; -NONBLDR_SECTOR_START = 0xf; -NONBLDR_SECTOR_END = 0x1ff; +NONBOARDLOADER_START = 0xc01e000; +NONBOARDLOADER_MAXSIZE = 0x3e2000; +NONBOARDLOADER_SECTOR_START = 0xf; +NONBOARDLOADER_SECTOR_END = 0x1ff; BOOTLOADER_START = 0xc01e000; BOOTLOADER_MAXSIZE = 0x40000; BOOTLOADER_SECTOR_START = 0xf; diff --git a/core/embed/models/D002/memory_secmon.h b/core/embed/models/D002/memory_secmon.h index ff99ac5d85..2d022f16f9 100644 --- a/core/embed/models/D002/memory_secmon.h +++ b/core/embed/models/D002/memory_secmon.h @@ -50,10 +50,10 @@ #define BOOTUCB_SECTOR_END 0xE // Non-boardloader area (includes bootloader, firmware, assets and storage) -#define NONBLDR_START (0x0C01E000) -#define NONBLDR_MAXSIZE (497 * 8 * 1024) // 3976 kB -#define NONBLDR_SECTOR_START 0xF -#define NONBLDR_SECTOR_END 0x1FF +#define NONBOARDLOADER_START (0x0C01E000) +#define NONBOARDLOADER_MAXSIZE (497 * 8 * 1024) // 3976 kB +#define NONBOARDLOADER_SECTOR_START 0xF +#define NONBOARDLOADER_SECTOR_END 0x1FF #define BOOTLOADER_START (0x0C01E000) #define BOOTLOADER_MAXSIZE (32 * 8 * 1024) // 256 kB diff --git a/core/embed/models/D002/memory_secmon.ld b/core/embed/models/D002/memory_secmon.ld index 1a9ea53831..4af773c00f 100644 --- a/core/embed/models/D002/memory_secmon.ld +++ b/core/embed/models/D002/memory_secmon.ld @@ -19,10 +19,10 @@ BOOTUCB_START = 0xc01c000; BOOTUCB_MAXSIZE = 0x2000; BOOTUCB_SECTOR_START = 0xe; BOOTUCB_SECTOR_END = 0xe; -NONBLDR_START = 0xc01e000; -NONBLDR_MAXSIZE = 0x3e2000; -NONBLDR_SECTOR_START = 0xf; -NONBLDR_SECTOR_END = 0x1ff; +NONBOARDLOADER_START = 0xc01e000; +NONBOARDLOADER_MAXSIZE = 0x3e2000; +NONBOARDLOADER_SECTOR_START = 0xf; +NONBOARDLOADER_SECTOR_END = 0x1ff; BOOTLOADER_START = 0xc01e000; BOOTLOADER_MAXSIZE = 0x40000; BOOTLOADER_SECTOR_START = 0xf; diff --git a/core/embed/projects/boardloader/main.c b/core/embed/projects/boardloader/main.c index f2d58df010..7292558e7c 100644 --- a/core/embed/projects/boardloader/main.c +++ b/core/embed/projects/boardloader/main.c @@ -131,7 +131,7 @@ board_capabilities_t capabilities #ifdef USE_BOOT_UCB -static void try_to_upgrade(void) { +static void try_bootloader_update(void) { boot_ucb_t ucb; // Start with some non-deterministic delay @@ -143,7 +143,7 @@ static void try_to_upgrade(void) { } // Check if the new boot header is present and valid - const boot_header_t* hdr = boot_header_check_integrity(ucb.header_address); + const boot_header_auth_t* hdr = boot_header_auth_get(ucb.header_address); if (hdr == NULL) { return; } @@ -167,7 +167,7 @@ static void try_to_upgrade(void) { // Check if the new bootloader is the same as the old one // (just prevents unnecessary flash erase/write) - if (bootloader_is_unchanged(hdr, code_address)) { + if (sectrue != bootloader_area_needs_update(hdr, code_address)) { return; } @@ -240,8 +240,8 @@ static inline void ensure_signed_bootloader( fih_delay(0); // Check if the boot header is present and valid - const boot_header_t* hdr = boot_header_check_integrity(BOOTLOADER_START); - fih_ensure(sectrue * (hdr != NULL), "invalid bootloader header"); + const boot_header_auth_t* hdr = boot_header_auth_get(BOOTLOADER_START); + fih_ensure(sectrue * (hdr != NULL), "invalid boot header"); // Get address of the bootloader code uint32_t code_address = BOOTLOADER_START + hdr->header_size; @@ -326,7 +326,7 @@ int main(void) { #ifdef USE_BOOT_UCB // Try to update the bootloader from the UCB (update control block) if it // is present, valid and points to a new valid/signed bootloader image. - try_to_upgrade(); + try_bootloader_update(); #endif // Address of the next stage to jump to. It's set at the end of diff --git a/core/embed/projects/prodtest/cmd/prodtest_bootloader.c b/core/embed/projects/prodtest/cmd/prodtest_bootloader.c index 8ee141995f..48e699d23c 100644 --- a/core/embed/projects/prodtest/cmd/prodtest_bootloader.c +++ b/core/embed/projects/prodtest/cmd/prodtest_bootloader.c @@ -40,7 +40,7 @@ static void prodtest_bootloader_version(cli_t *cli) { mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_BOOTUPDATE); - const boot_header_t *hdr = boot_header_check_integrity(BOOTLOADER_START); + const boot_header_auth_t *hdr = boot_header_auth_get(BOOTLOADER_START); if (hdr == NULL) { mpu_restore(mpu_mode); diff --git a/core/embed/util/flash/stm32u5/flash_layout.c b/core/embed/util/flash/stm32u5/flash_layout.c index ea1cc95047..0201aed257 100644 --- a/core/embed/util/flash/stm32u5/flash_layout.c +++ b/core/embed/util/flash/stm32u5/flash_layout.c @@ -60,6 +60,8 @@ DEFINE_SINGLE_AREA(ASSETS_AREA, ASSETS, ACCESS_APP); #endif #ifdef USE_BOOT_UCB +// Area dedicated to the UCB (Update Control Block) used during +// boot-loader or boot-header updates. DEFINE_SINGLE_AREA(BOOTUCB_AREA, BOOTUCB, ACCESS_DEFAULT); #ifdef BOARDLOADER // Area used by the boardloader during bootloader update process. diff --git a/core/embed/util/image/boot_header.c b/core/embed/util/image/boot_header.c index 0858e35b3e..0ee97c0307 100644 --- a/core/embed/util/image/boot_header.c +++ b/core/embed/util/image/boot_header.c @@ -35,7 +35,7 @@ extern const uint8_t _bootloader_code_size; typedef union { - boot_header_t hdr; + boot_header_auth_t hdr; uint8_t raw[BOOT_HEADER_MAXSIZE]; } boot_header_padded_t; @@ -100,17 +100,17 @@ static const uint8_t * const BOARDLOADER_EC_KEYS[] = { #endif }; -secbool boot_header_check_signature(const boot_header_t* hdr, +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)); - uint32_t sigmask = hdr->sigmask; - uint32_t sigmask_inv = 0; // FIH + uint8_t sigmask = hdr->sigmask; + uint8_t sigmask_inv = 0; // FIH - const boot_header_unauth_t* sig = boot_header_get_unauth(hdr); + 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 @@ -193,8 +193,8 @@ static const boot_header_merkle_proof_t* boot_header_get_merkle_proof( return proof; } -const boot_header_t* boot_header_check_integrity(uint32_t address) { - boot_header_t* hdr = (boot_header_t*)address; +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) { @@ -248,7 +248,8 @@ const boot_header_t* boot_header_check_integrity(uint32_t address) { return hdr; } -const boot_header_unauth_t* boot_header_get_unauth(const boot_header_t* 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) { @@ -272,7 +273,7 @@ const boot_header_unauth_t* boot_header_get_unauth(const boot_header_t* hdr) { return unauth; } -void boot_header_calc_merkle_root(const boot_header_t* hdr, +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; @@ -310,18 +311,18 @@ void boot_header_calc_merkle_root(const boot_header_t* hdr, } } -secbool bootloader_is_unchanged(const boot_header_t* hdr, - uint32_t code_address) { - boot_header_t* prev_hdr = (boot_header_t*)BOOTLOADER_START; +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 sectrue; + return secfalse; } - 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 index 6ef6a31f39..a6192b8200 100644 --- a/core/embed/util/image/boot_image.c +++ b/core/embed/util/image/boot_image.c @@ -221,15 +221,15 @@ void boot_image_replace(const boot_image_t *image) { #else bool boot_image_check(const boot_image_t *image) { - if (image->image_size < sizeof(boot_header_t)) { + 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_BOOTUPDATE); - boot_header_t *cur_hdr = (boot_header_t *)BOOTLOADER_START; - boot_header_t *new_hdr = (boot_header_t *)image->image_ptr; + 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); @@ -243,25 +243,29 @@ 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_t)), + ensure(sectrue * (image->image_size >= sizeof(boot_header_auth_t)), "Bootloader image too small"); // Read bootloader header - const boot_header_t *hdr = boot_header_check_integrity(header_address); + 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 <= image->image_size), - "Bootloader header too big"); - ensure(sectrue * (hdr->code_size <= image->image_size), - "Bootloader code too big"); ensure(sectrue * (hdr->header_size + hdr->code_size <= image->image_size), "Bootloader image too small"); // Check monotonic version - uint8_t min_monotonic_version = 0; - ensure(monoctr_read(MONOCTR_BOOTLOADER_VERSION, &min_monotonic_version), - "monoctr read"); + + mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_BOOTUPDATE); + + 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"); diff --git a/core/embed/util/image/boot_image_embdata.c b/core/embed/util/image/boot_image_embdata.c index 46fc2c9aa8..6d9cc94e93 100644 --- a/core/embed/util/image/boot_image_embdata.c +++ b/core/embed/util/image/boot_image_embdata.c @@ -42,8 +42,10 @@ 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 index 9ba4adf81f..0c4fb6436e 100644 --- a/core/embed/util/image/boot_ucb.c +++ b/core/embed/util/image/boot_ucb.c @@ -65,11 +65,11 @@ secbool boot_ucb_read(boot_ucb_t* ucb) { uint32_t max_address = NONBOARDLOADER_START + NONBOARDLOADER_MAXSIZE; if (ucb->header_address < min_address || - ucb->header_address > max_address - sizeof(boot_header_t)) { + ucb->header_address > max_address - sizeof(boot_header_auth_t)) { return secfalse; } - const boot_header_t* hdr = (boot_header_t*)ucb->header_address; + 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 @@ -116,7 +116,7 @@ secbool boot_ucb_write(uint32_t header_address, uint32_t code_address) { }; // Calculate the hash of the header - boot_header_t* hdr = (boot_header_t*)header_address; + boot_header_auth_t* hdr = (boot_header_auth_t*)header_address; IMAGE_HASH_CTX ctx; IMAGE_HASH_INIT(&ctx); diff --git a/core/embed/util/image/inc/util/boot_header.h b/core/embed/util/image/inc/util/boot_header.h index bcc6d14b7f..8c036542dd 100644 --- a/core/embed/util/image/inc/util/boot_header.h +++ b/core/embed/util/image/inc/util/boot_header.h @@ -53,6 +53,10 @@ typedef struct { /** * 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' */ @@ -96,7 +100,7 @@ typedef struct __attribute__((packed)) { * the authenticated part of the header is maximized. */ uint8_t padding[0]; -} boot_header_t; +} boot_header_auth_t; /** * Merkle proof structure used in the boot header to calculate the root @@ -147,7 +151,7 @@ typedef struct __attribute__((packed)) { * @param address Address of the boot header in flash memory * @return Pointer to the boot header if valid, NULL otherwise. */ -const boot_header_t* boot_header_check_integrity(uint32_t address); +const boot_header_auth_t* boot_header_auth_get(uint32_t address); /** * Gets pointer to the unauthenticated part of the boot header. @@ -169,7 +173,7 @@ const boot_header_unauth_t* boot_header_unauth_get( * @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_t* hdr, +void boot_header_calc_merkle_root(const boot_header_auth_t* hdr, uint32_t code_address, merkle_proof_node_t* root); @@ -184,7 +188,7 @@ void boot_header_calc_merkle_root(const boot_header_t* hdr, * @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_t* hdr, +secbool boot_header_check_signature(const boot_header_auth_t* hdr, const merkle_proof_node_t* merkle_root); /** @@ -194,7 +198,7 @@ secbool boot_header_check_signature(const boot_header_t* hdr, * * @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 are unchanged + * @return secbool indicating whether the boot header and code need update */ -secbool bootloader_is_unchanged(const boot_header_t* hdr, - uint32_t code_address); +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 index f363cb753e..7e647897f9 100644 --- a/core/embed/util/image/inc/util/boot_image.h +++ b/core/embed/util/image/inc/util/boot_image.h @@ -27,9 +27,10 @@ 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; /** diff --git a/core/tools/trezor_core_tools/headertool_pq.py b/core/tools/trezor_core_tools/headertool_pq.py index c11b7e0b09..85a9f01aea 100755 --- a/core/tools/trezor_core_tools/headertool_pq.py +++ b/core/tools/trezor_core_tools/headertool_pq.py @@ -16,11 +16,11 @@ def no_echo(*args, **kwargs): "-D", "--sign-dev-keys", is_flag=True, help="Sign with development header keys." ) @click.option( - "-d", - "--digest", - "print_digest", + "-m", + "--merkle-root", + "print_merkle_root", is_flag=True, - help="Only output header digest for signing and exit.", + help="Only output Merkle root for signing and exit.", ) @click.option( "-M", @@ -37,7 +37,7 @@ def cli( dry_run, sign_dev_keys, merkle_proof, - print_digest, + print_merkle_root, quiet, ): """Manage firmware headers. @@ -64,9 +64,10 @@ def cli( f"Could not parse file (magic bytes: {magic})" ) from e - digest = fw.digest() - if print_digest: - click.echo(digest.hex()) + fw.set_merkle_proof(list(map(bytes.fromhex, merkle_proof))) + + if print_merkle_root: + click.echo(fw.merkle_root().hex()) return if quiet: @@ -74,9 +75,6 @@ def cli( else: echo = click.echo - # Add 16 items - fw.set_merkle_proof(list(map(bytes.fromhex, merkle_proof))) - echo("Signing with dev keys...", err=True) fw.sign_with_devkeys() diff --git a/python/src/trezorlib/_internal/firmware_headers.py b/python/src/trezorlib/_internal/firmware_headers.py index cd07f076f8..870ddf7828 100644 --- a/python/src/trezorlib/_internal/firmware_headers.py +++ b/python/src/trezorlib/_internal/firmware_headers.py @@ -477,7 +477,7 @@ class BootloaderV2Image(firmware.BootableImage): # digest is calculated from self.header.sigmask = (1 << 0) | (1 << 1) - digest = self.digest() + digest = self.merkle_root() # SLH signature signs the image digest for idx, key in enumerate(self.DEV_PRIVATE_PQ_KEYS): @@ -504,13 +504,14 @@ class BootloaderV2Image(firmware.BootableImage): output = [ "Firmware Header " + _format_container(header_out), - f"Fingerprint: {click.style(self.digest().hex(), bold=True)}", + f"Leaf hash: {click.style(self.leaf_hash().hex(), bold=True)}", + f"Merkle root: {click.style(self.merkle_root().hex(), bold=True)}", ] return "\n".join(output) def verify(self, dev_keys: bool = False) -> None: - digest = self.digest() + digest = self.merkle_root() for idx, key in enumerate(self.public_ec_keys(dev_keys)): if not _ed25519.checkvalid(self.unauth.ec_signatures[idx], digest, key): diff --git a/python/src/trezorlib/firmware/core.py b/python/src/trezorlib/firmware/core.py index 99f6bb2e03..b481950574 100644 --- a/python/src/trezorlib/firmware/core.py +++ b/python/src/trezorlib/firmware/core.py @@ -245,7 +245,9 @@ class BootHeader(Struct): "version" / TupleAdapter(c.Int8ul, c.Int8ul, c.Int8ul, c.Int8ul), "fix_version" / TupleAdapter(c.Int8ul, c.Int8ul, c.Int8ul, c.Int8ul), "min_prev_version" / TupleAdapter(c.Int8ul, c.Int8ul, c.Int8ul, c.Int8ul), - "monotonic" / c.Int32ul, + "monotonic" / c.Int8ul, + "sigmask" / c.Int8ul, + "_reserved" / c.Padding(2), "header_len" / c.Int32ul, "auth_len" / c.Int32ul, "code_length" / c.Rebuild( @@ -255,7 +257,6 @@ class BootHeader(Struct): else (this.code_length or 0) ), "storage_address" / c.Int32ul, - "sigmask" / c.Int32ul, "firmware_root" / c.Bytes(32), # Variable-length padding that's part of the authenticated header @@ -319,15 +320,28 @@ class BootableImage(Struct): self.unauth.merkle_proof = proof self.header.auth_len = self.header.header_len - len(self.unauth.build()) - def digest(self) -> bytes: - hash_params = self.get_hash_params() - hash_fn = hash_params.hash_function + def _leaf_value(self) -> bytes: + hash_fn = self.get_hash_params().hash_function assert hash_fn is hashlib.sha256 # currently hardcoded in trezorlib.merkle_tree - auth_header = self.header.build() code_hash = hash_fn(self.code).digest() - leaf = auth_header + code_hash - return merkle_tree.evaluate_proof(leaf, self.unauth.merkle_proof) + return auth_header + code_hash + + def leaf_hash(self) -> bytes: + """Calculate the Merkle leaf hash. + + This is a fingerprint of _this particular_ boot header, which is not affected + by other members of the Merkle tree. + """ + return merkle_tree.leaf_hash(self._leaf_value()) + + def merkle_root(self) -> bytes: + """Calculate the Merkle root hash. + + It identifies the entire Merkle tree that contains this boot header. Signatures + are evaluated over this value. + """ + return merkle_tree.evaluate_proof(self._leaf_value(), self.unauth.merkle_proof) def model(self) -> Model | None: if isinstance(self.header.hw_model, Model):