chore(tools): verify secmon-wrapped prodtest images in `verify_signed_firmware`
What changed, and why it matters
This is a developer tooling change for Trezor hardware wallets. It improves an internal Python script that checks whether signed firmware images are correctly built. The script now also validates a special 'secmon-wrapped' production-test image used on at least one Trezor model. There is no change to device firmware, no runtime security fix, and no evidence of a vulnerability being patched.
No immediate action required. Treat as a routine tooling improvement. If this tool is used in CI/release signing workflows, ensure the updated script is deployed so secmon-wrapped prodtest images are validated consistently with other firmware images.
Security signals we found
Adds verification of inner secmon signatures in a developer tool
Refactors signature verification into a reusable helper
Uses strict=True parsing in parse_any
No device-side firmware or bootloader changes
Evidence from the diff
The commit modifies core/tools/trezor_core_tools/verify_signed_firmware.py, a helper used to verify signed/unsigned firmware pairs. It adds handling for VendorFirmware images whose code section contains an embedded SecmonImage (e.g., T3W1 prodtest). For these images the tool now zeroes the inner secmon COSI signature fields before recomputing hashes, and verifies both the outer firmware signature and the inner secmon signature against production keys. The signature-verification logic is refactored into a helper _check_signatures. The change is defensive/verification-only and does not alter firmware parsing on the device or any cryptographic checks performed by the bootloader.
Changed components
core/tools/trezor_core_tools/verify_signed_firmware.pyInspect captured patch +46 / −18
### core/tools/trezor_core_tools/verify_signed_firmware.py
@@ -43,6 +43,8 @@
from trezorlib.firmware import FirmwareHeader, SecmonHeader
from trezorlib.firmware.core import FirmwareImage
+Image = fh.SignableImageProto | fh.BootloaderV2Image
+
# ---- pretty output -------------------------------------------------------
def ok(msg: str) -> None:
@@ -58,11 +60,19 @@ def info(msg: str) -> None:
# ---- trezorlib glue ------------------------------------------------------
-def parse_any(data: bytes) -> fh.SignableImageProto | fh.BootloaderV2Image:
+def parse_any(data: bytes) -> Image:
"""Parse any supported image. parse_image() doesn't dispatch TRZQ, so do it here."""
if data[:4] == b"TRZQ":
- return fh.BootloaderV2Image.parse(data)
- return fh.parse_image(data)
+ return fh.BootloaderV2Image.parse(data, strict=True)
+ return fh.parse_image(data, strict=True)
+
+
+def _inner_secmon(img: fh.VendorFirmware) -> fh.SecmonImage | None:
+ """Parse the code section as an embedded secmon image, if it is one."""
+ try:
+ return fh.SecmonImage.parse(img.firmware.code)
+ except Exception: # noqa: BLE001
+ return None
def _zero_cosi_header(h: FirmwareHeader | SecmonHeader) -> None:
@@ -84,6 +94,13 @@ def normalized(data: bytes) -> bytes:
img = parse_any(data)
if isinstance(img, fh.VendorFirmware): # TRZV
_zero_cosi_header(img.firmware.header)
+
+ inner = _inner_secmon(img)
+ if inner is not None: # Secmon-wrapped image (e.g. T3W1 prodtest)
+ _zero_cosi_header(inner.header)
+ img.firmware.code = inner.build()
+ img.firmware.header.hashes = img.firmware.code_hashes()
+
elif isinstance(img, fh.BootloaderV2Image): # TRZQ
img.header.sigmask = 0
img.unauth.slh_signatures = [
@@ -109,6 +126,25 @@ def _runs(offsets: list[int]) -> int:
return runs
+# ---- helper - verify signature of an image -------------------------------
+def _check_signatures(img: Image, name_prefix: str = "") -> bool:
+ img_name = f"{name_prefix}{getattr(img, 'NAME', 'image')}"
+ try:
+ if not img.signature_present():
+ bad(f"no signature present in the signed file ({img_name})")
+ else:
+ img.verify() # production keys; raises on bad signature or bad hashes
+ ok(
+ f"signature of ({img_name}) is GENUINE -- verified against production keys"
+ )
+ return True
+ except Exception as e: # noqa: BLE001
+ bad(
+ f"signature verification of ({img_name}) FAILED against production keys: {type(e).__name__}: {e}"
+ )
+ return False
+
+
# ---- core verification of one (unsigned, signed) pair --------------------
def verify_pair(unsigned: Path, signed: Path) -> bool:
click.echo()
@@ -176,21 +212,13 @@ def verify_pair(unsigned: Path, signed: Path) -> bool:
confined = " [all within signature fields]" if nu == ns else ""
info(f"{len(diffs)} bytes differ across {_runs(diffs)} run(s){confined}")
- # ---- CHECK 3: signature authenticity -----------------------------------
- try:
- if not img.signature_present():
- bad("no signature present in the signed file")
- okall = False
- else:
- img.verify() # production keys; raises on bad signature or bad hashes
- ok(
- f"signature is GENUINE -- verified against production keys ({getattr(img, 'NAME', 'image')})"
- )
- except Exception as e: # noqa: BLE001
- bad(
- f"signature verification FAILED against production keys: {type(e).__name__}: {e}"
- )
- okall = False
+ # ---- CHECK 3 signature authenticity -----------------------------------
+ okall &= _check_signatures(img)
+
+ if isinstance(img, fh.VendorFirmware):
+ inner = _inner_secmon(img)
+ if inner is not None:
+ okall &= _check_signatures(inner, name_prefix="inner ")
return okall
Why this scored 17/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.