From 8795cbcf5c0e7b46059fca7c2090c2905b2ab1fc Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Sun, 15 Jun 2025 19:33:59 +0200 Subject: [PATCH] feat(core): verify secmon signature in bootloader [no changelog] --- core/embed/projects/bootloader/main.c | 87 +++++++++++++-- .../bootloader/workflow/wf_firmware_update.c | 105 ++++++++++++++++++ core/embed/util/image/image.c | 91 +++++++++++++++ core/embed/util/image/inc/util/image.h | 28 +++++ core/site_scons/models/D002/discovery2.py | 1 + .../models/T3W1/trezor_t3w1_revA.py | 1 + .../models/T3W1/trezor_t3w1_revB.py | 1 + .../models/T3W1/trezor_t3w1_revC.py | 1 + .../trezorlib/_internal/firmware_headers.py | 74 ++++++++++++ 9 files changed, 380 insertions(+), 9 deletions(-) diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c index a491dcd782..4e129045d2 100644 --- a/core/embed/projects/bootloader/main.c +++ b/core/embed/projects/bootloader/main.c @@ -338,9 +338,37 @@ void real_jump_to_firmware(void) { &FIRMWARE_AREA), "Firmware is corrupted"); - secret_prepare_fw( - ((vhdr.vtrust & VTRUST_SECRET_MASK) == VTRUST_SECRET_ALLOW) * sectrue, - ((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) * sectrue); + size_t secmon_code_offset = 0; + +#ifdef USE_SECMON_VERIFICATION + size_t secmon_start = (size_t)IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen + + IMAGE_HEADER_SIZE); + const secmon_header_t *secmon_hdr = + read_secmon_header((const uint8_t *)secmon_start, FIRMWARE_MAXSIZE); + + if (secmon_hdr != NULL) { + secmon_code_offset = IMAGE_CODE_ALIGN(SECMON_HEADER_SIZE); + } + + ensure((secmon_hdr != NULL) * sectrue, "Secmon header not found"); + + ensure(check_secmon_model(secmon_hdr), "Wrong secmon model"); + + ensure(check_secmon_header_sig(secmon_hdr), "Invalid secmon signature"); + + ensure(check_secmon_contents(secmon_hdr, secmon_start - FIRMWARE_START, + &FIRMWARE_AREA), + "Secmon is corrupted"); +#endif + + secbool provisioning_access = + ((vhdr.vtrust & VTRUST_ALLOW_PROVISIONING) == VTRUST_ALLOW_PROVISIONING) * + sectrue; + + secbool secret_run_access = + ((vhdr.vtrust & VTRUST_SECRET_MASK) == VTRUST_SECRET_ALLOW) * sectrue; + + secret_prepare_fw(secret_run_access, provisioning_access); // if all warnings are disabled in VTRUST flags then skip the procedure if ((vhdr.vtrust & VTRUST_NO_WARNING) != VTRUST_NO_WARNING) { @@ -381,7 +409,8 @@ void real_jump_to_firmware(void) { system_deinit(); jump_to_next_stage( - IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen + IMAGE_HEADER_SIZE)); + IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen + IMAGE_HEADER_SIZE) + + secmon_code_offset); } __attribute__((noreturn)) void reboot_with_fade(void) { @@ -398,7 +427,11 @@ int bootloader_main(void) { system_init(&rsod_panic_handler); - secbool manufacturing_mode = is_manufacturing_mode(); + vendor_header vhdr; + volatile secbool vhdr_present = secfalse; + vhdr_present = read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr); + + secbool manufacturing_mode = is_manufacturing_mode(&vhdr); secbool stay_in_bootloader = boot_sequence(manufacturing_mode); @@ -426,10 +459,8 @@ int bootloader_main(void) { #endif const image_header *hdr = NULL; - vendor_header vhdr; // detect whether the device contains a valid firmware - volatile secbool vhdr_present = secfalse; volatile secbool vhdr_keys_ok = secfalse; volatile secbool vhdr_lock_ok = secfalse; volatile secbool img_hdr_ok = secfalse; @@ -440,8 +471,7 @@ int bootloader_main(void) { volatile secbool firmware_present = secfalse; volatile secbool firmware_present_backup = secfalse; volatile secbool auto_upgrade = secfalse; - - vhdr_present = read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr); + volatile secbool secmon_valid = secfalse; if (sectrue == vhdr_present) { vhdr_keys_ok = check_vendor_header_keys(&vhdr); @@ -476,7 +506,46 @@ int bootloader_main(void) { header_present = version_ok; } +#ifdef USE_SECMON_VERIFICATION + size_t secmon_start = (size_t)IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen + + IMAGE_HEADER_SIZE); + + const secmon_header_t *secmon_hdr = + read_secmon_header((const uint8_t *)secmon_start, FIRMWARE_MAXSIZE); + + volatile secbool secmon_header_present = secfalse; + volatile secbool secmon_model_valid = secfalse; + volatile secbool secmon_header_sig_valid = secfalse; + volatile secbool secmon_contents_valid = secfalse; + if (sectrue == header_present) { + secmon_header_present = + secbool_and(header_present, (secmon_hdr != NULL) * sectrue); + } + + if (sectrue == secmon_header_present) { + secmon_model_valid = + secbool_and(secmon_header_present, check_secmon_model(secmon_hdr)); + } + + if (sectrue == secmon_model_valid) { + secmon_header_sig_valid = + secbool_and(secmon_model_valid, check_secmon_header_sig(secmon_hdr)); + } + + if (sectrue == secmon_header_sig_valid) { + secmon_contents_valid = secbool_and( + secmon_header_sig_valid, + check_secmon_contents(secmon_hdr, secmon_start - FIRMWARE_START, + &FIRMWARE_AREA)); + secmon_valid = secmon_contents_valid; + } + +#else + secmon_valid = header_present; +#endif + + if (sectrue == secmon_valid) { ensure_firmware_min_version(hdr->monotonic); firmware_present = check_image_contents( hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, &FIRMWARE_AREA); diff --git a/core/embed/projects/bootloader/workflow/wf_firmware_update.c b/core/embed/projects/bootloader/workflow/wf_firmware_update.c index 13c84a40d9..586703083c 100644 --- a/core/embed/projects/bootloader/workflow/wf_firmware_update.c +++ b/core/embed/projects/bootloader/workflow/wf_firmware_update.c @@ -62,6 +62,11 @@ typedef enum { UPLOAD_ERR_NOT_FULLTRUST_IMAGE = -13, UPLOAD_ERR_INVALID_CHUNK_PADDING = -14, UPLOAD_ERR_COMMUNICATION = -17, + UPLOAD_ERR_INVALID_SECMON_HEADER = -18, + UPLOAD_ERR_INVALID_SECMON_HEADER_SIG = -19, + UPLOAD_ERR_INVALID_SECMON_MODEL = -20, + UPLOAD_ERR_INVALID_SECMON_HASH = -21, + UPLOAD_ERR_SECMON_TOO_BIG = -22, } upload_status_t; #define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 @@ -80,6 +85,19 @@ typedef struct { size_t headers_offset; // offset of headers in the first block size_t read_offset; // offset of the next read data in the chunk buffer uint32_t chunk_size; // size of already received chunk data +#ifdef USE_SECMON_VERIFICATION + size_t secmon_code_offset; // offset of the secmon code in the first block + size_t secmon_code_size; // size of the secmon code + size_t secmon_code_processed; // size of the processed secmon code + uint8_t expected_secmon_hash[IMAGE_HASH_DIGEST_LENGTH]; // expected hash of + // the secmon code + // todo should be IMAGE_HASH_CTX, but due to limitations of the hash_processor + // driver implementation, we can't run two hash calculations in parallel so + // temporarily we use SW hashing for secmon code during update. + // As secmon is only used on U5 MCUs where also SHA256is used for image + // hashes, this works, but should be fixed by improving the hash_processor + SHA256_CTX secmon_hash_ctx; +#endif } firmware_update_ctx_t; static int version_compare(uint32_t vera, uint32_t verb) { @@ -233,6 +251,43 @@ static upload_status_t process_msg_FirmwareUpload(protob_io_t *iface, return UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION; } +#ifdef USE_SECMON_VERIFICATION + size_t secmon_start_offset = + (size_t)IMAGE_CODE_ALIGN(vhdr.hdrlen + IMAGE_HEADER_SIZE); + size_t secmon_start = (size_t)chunk_buffer + secmon_start_offset; + const secmon_header_t *secmon_hdr = + read_secmon_header((const uint8_t *)secmon_start, FIRMWARE_MAXSIZE); + + if (secmon_hdr != NULL) { + ctx->secmon_code_offset = + IMAGE_CODE_ALIGN(vhdr.hdrlen + IMAGE_HEADER_SIZE) + + SECMON_HEADER_SIZE; + } + + if (secmon_hdr != (const secmon_header_t *)secmon_start) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid secmon header"); + return UPLOAD_ERR_INVALID_SECMON_HEADER; + } + + if (sectrue != check_secmon_model(secmon_hdr)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Wrong secmon model"); + return UPLOAD_ERR_INVALID_SECMON_MODEL; + } + + if (sectrue != check_secmon_header_sig(secmon_hdr)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid secmon signature"); + return UPLOAD_ERR_INVALID_SECMON_HEADER_SIG; + } + + ctx->secmon_code_size = secmon_hdr->codelen; + + memcpy(ctx->expected_secmon_hash, secmon_hdr->hash, + IMAGE_HASH_DIGEST_LENGTH); +#endif + memcpy(&hdr, received_hdr, sizeof(hdr)); vendor_header current_vhdr; @@ -313,6 +368,16 @@ static upload_status_t process_msg_FirmwareUpload(protob_io_t *iface, } #endif +#ifdef USE_SECMON_VERIFICATION + if (ctx->secmon_code_size > + ((hdr.codelen + IMAGE_HEADER_SIZE + vhdr.hdrlen) - + ctx->secmon_code_offset)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Secmon code too big"); + return UPLOAD_ERR_SECMON_TOO_BIG; + } +#endif + confirm_result_t response = CANCEL; if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) && (sectrue == is_new || sectrue == is_ilu)) { @@ -408,6 +473,46 @@ static upload_status_t process_msg_FirmwareUpload(protob_io_t *iface, return UPLOAD_ERR_INVALID_CHUNK_HASH; } +#ifdef USE_SECMON_VERIFICATION + // validate secmon code hash + if (ctx->secmon_code_size > 0) { + if (ctx->secmon_code_processed == 0) { + // todo SW SHA256, see comment in firmware_update_ctx_t + sha256_Init(&ctx->secmon_hash_ctx); + } + + size_t secmon_code_remaining = + ctx->secmon_code_size - ctx->secmon_code_processed; + + size_t secmon_code_to_process = IMAGE_CHUNK_SIZE - ctx->secmon_code_offset; + + secmon_code_to_process = MIN(secmon_code_to_process, secmon_code_remaining); + + sha256_Update(&ctx->secmon_hash_ctx, + (uint8_t *)chunk_buffer + ctx->secmon_code_offset, + secmon_code_to_process); + + ctx->secmon_code_processed += secmon_code_to_process; + ctx->secmon_code_offset = 0; + + if (ctx->secmon_code_processed >= ctx->secmon_code_size) { + // secmon code is fully processed + uint8_t secmon_hash[IMAGE_HASH_DIGEST_LENGTH]; + sha256_Final(&ctx->secmon_hash_ctx, secmon_hash); + + if (memcmp(secmon_hash, ctx->expected_secmon_hash, + IMAGE_HASH_DIGEST_LENGTH) != 0) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid secmon hash"); + return UPLOAD_ERR_INVALID_SECMON_HASH; + } + ctx->secmon_code_size = 0; // reset secmon code size to prevent + // reprocessing in the next chunk + } + } + +#endif + // buffer with the received data const uint32_t *src = (const uint32_t *)chunk_buffer; // number of received bytes diff --git a/core/embed/util/image/image.c b/core/embed/util/image/image.c index a0a85a6cba..1f0fef1e85 100644 --- a/core/embed/util/image/image.c +++ b/core/embed/util/image/image.c @@ -54,6 +54,19 @@ static const uint8_t * const BOOTLOADER_KEYS[] = { #endif }; +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_SCMON_KEYS +#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) { @@ -159,6 +172,84 @@ secbool check_image_header_sig(const image_header *const hdr, uint8_t key_m, *(const ed25519_signature *)hdr->sig)); } +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)); +} + +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; +} + secbool __wur read_vendor_header(const uint8_t *const data, vendor_header *const vhdr) { memcpy(&vhdr->magic, data, 4); diff --git a/core/embed/util/image/inc/util/image.h b/core/embed/util/image/inc/util/image.h index ef715c3300..e8e962e72f 100644 --- a/core/embed/util/image/inc/util/image.h +++ b/core/embed/util/image/inc/util/image.h @@ -29,6 +29,7 @@ #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) @@ -36,6 +37,8 @@ #define FIRMWARE_IMAGE_MAGIC 0x465A5254 // TRZF +#define SECMON_IMAGE_MAGIC 0x43455354 // TSEC + #define IMAGE_CODE_ALIGN(addr) \ ((((uint32_t)(uintptr_t)addr) + (CODE_ALIGNMENT - 1)) & ~(CODE_ALIGNMENT - 1)) @@ -120,6 +123,20 @@ typedef struct { 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 reserved_0[3]; + 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); @@ -156,3 +173,14 @@ 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); + +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); + +secbool __wur check_secmon_contents(const secmon_header_t *const hdr, + size_t code_offset, + const flash_area_t *area); diff --git a/core/site_scons/models/D002/discovery2.py b/core/site_scons/models/D002/discovery2.py index 39961efc06..47b93ba045 100644 --- a/core/site_scons/models/D002/discovery2.py +++ b/core/site_scons/models/D002/discovery2.py @@ -47,6 +47,7 @@ def configure( ("USE_HSE", "1"), ("USE_BOOTARGS_RSOD", "1"), ("LOCKABLE_BOOTLOADER", "1"), + ("USE_SECMON_VERIFICATION", "1"), ] if "display" in features_wanted: diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py index 110f4bbdca..91b3785b5f 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py @@ -49,6 +49,7 @@ def configure( ("FIXED_HW_DEINIT", "1"), ("LOCKABLE_BOOTLOADER", "1"), ("LAZY_DISPLAY_INIT", "1"), + ("USE_SECMON_VERIFICATION", "1"), ("USE_BOOTARGS_RSOD", "1"), ("TERMINAL_FONT_SCALE", "2"), ("TERMINAL_X_PADDING", "4"), diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revB.py b/core/site_scons/models/T3W1/trezor_t3w1_revB.py index f638933f1c..c31e0cdfcc 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revB.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revB.py @@ -49,6 +49,7 @@ def configure( ("FIXED_HW_DEINIT", "1"), ("LOCKABLE_BOOTLOADER", "1"), ("LAZY_DISPLAY_INIT", "1"), + ("USE_SECMON_VERIFICATION", "1"), ("USE_BOOTARGS_RSOD", "1"), ("TERMINAL_FONT_SCALE", "2"), ("TERMINAL_X_PADDING", "4"), diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revC.py b/core/site_scons/models/T3W1/trezor_t3w1_revC.py index 76acae87f8..dea010ba87 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revC.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revC.py @@ -49,6 +49,7 @@ def configure( ("FIXED_HW_DEINIT", "1"), ("LOCKABLE_BOOTLOADER", "1"), ("LAZY_DISPLAY_INIT", "1"), + ("USE_SECMON_VERIFICATION", "1"), ("USE_BOOTARGS_RSOD", "1"), ("TERMINAL_FONT_SCALE", "2"), ("TERMINAL_X_PADDING", "4"), diff --git a/python/src/trezorlib/_internal/firmware_headers.py b/python/src/trezorlib/_internal/firmware_headers.py index 3a1ee3422b..b84192542d 100644 --- a/python/src/trezorlib/_internal/firmware_headers.py +++ b/python/src/trezorlib/_internal/firmware_headers.py @@ -183,6 +183,42 @@ def format_header( return "\n".join(output) +def format_secmon_header( + header: firmware.secmon.SecmonHeader, + code_hash: bytes, + digest: bytes, + sig_status: Status, +) -> str: + header_dict = asdict(header) + header_out = header_dict.copy() + + for key, val in header_out.items(): + if "version" in key: + header_out[key] = LiteralStr(_format_version(val)) + + # status = SYM_OK if header.hash == code_hash else SYM_FAIL + + if all_zero(header.hash): + hash_status = Status.MISSING + elif header.hash != code_hash: + hash_status = Status.INVALID + else: + hash_status = Status.VALID + # + # header_out["hashes"] = hashes_out + + all_ok = SYM_OK if hash_status.is_ok() and sig_status.is_ok() else SYM_FAIL + + output = [ + "SECMON Header " + _format_container(header_out), + "Code hash: " + click.style(code_hash.hex(), bold=True), + f"Fingerprint: {click.style(digest.hex(), bold=True)}", + f"{all_ok} Signature is {sig_status.value}, hash is {hash_status.value}", + ] + + return "\n".join(output) + + # =========================== functionality implementations =============== @@ -391,6 +427,39 @@ class BootloaderImage(firmware.FirmwareImage, CosiSignedMixin): return self.get_model_keys(dev_keys).boardloader_keys +class SecmonImage(firmware.SecmonImage, CosiSignedMixin): + NAME: t.ClassVar[str] = "secmon" + DEV_KEYS = _make_dev_keys(b"\x41", b"\x42") + + def get_header(self) -> CosiSignatureHeaderProto: + return self.header + + def format(self, verbose: bool = False) -> str: + return format_secmon_header( + self.header, + self.code_hash(), + self.digest(), + _check_signature_any(self), + ) + + def verify(self, dev_keys: bool = False) -> None: + self.validate_code_hash() + public_keys = self.public_keys(dev_keys) + try: + cosi.verify( + self.header.signature, + self.digest(), + self.get_model_keys(dev_keys).boardloader_sigs_needed, + public_keys, + self.header.sigmask, + ) + except Exception: + raise firmware.InvalidSignatureError("Invalid bootloader signature") + + def public_keys(self, dev_keys: bool = False) -> t.Sequence[bytes]: + return self.get_model_keys(dev_keys).boardloader_keys + + class LegacyFirmware(firmware.LegacyFirmware): NAME: t.ClassVar[str] = "legacy_firmware_v1" @@ -488,6 +557,11 @@ def parse_image(image: bytes) -> SignableImageProto: except c.ConstructError: pass + try: + return SecmonImage.parse(image) + except c.ConstructError: + pass + try: firmware_img = firmware.core.FirmwareImage.parse(image) if firmware_img.header.magic == firmware.core.HeaderType.BOOTLOADER: