from pathlib import Path import construct import pytest import requests from trezorlib import firmware from trezorlib.firmware import ( VendorFirmware, LegacyFirmware, LegacyV2Firmware, VendorHeader, ) CORE_FW_VERSION = "2.4.2" CORE_FW_FINGERPRINT = "54ccf155510b5292bd17ed748409d0d135112e24e62eb74184639460beecb213" LEGACY_FW_VERSION = "1.10.3" LEGACY_FW_FINGERPRINT = ( "bf0cc936a9afbf0a4ae7b727a2817fb69fba432d7230a0ff7b79b4a73b845197" ) CORE_FW = f"https://data.trezor.io/firmware/2/trezor-{CORE_FW_VERSION}.bin" LEGACY_FW = f"https://data.trezor.io/firmware/1/trezor-{LEGACY_FW_VERSION}.bin" HERE = Path(__file__).parent VENDOR_HEADER = ( HERE.parent.parent / "core" / "embed" / "vendorheader" / "vendorheader_satoshilabs_signed_prod.bin" ) def _fetch(url: str, version: str) -> bytes: path = HERE / f"trezor-{version}.bin" if not path.exists(): r = requests.get(url) r.raise_for_status() path.write_bytes(r.content) return path.read_bytes() @pytest.fixture() def legacy_fw() -> bytes: return _fetch(LEGACY_FW, LEGACY_FW_VERSION) @pytest.fixture() def core_fw() -> bytes: return _fetch(CORE_FW, CORE_FW_VERSION) def test_core_basic(core_fw: bytes) -> None: fw = VendorFirmware.parse(core_fw) fw.verify() assert fw.digest().hex() == CORE_FW_FINGERPRINT version_str = ".".join(str(x) for x in fw.firmware.header.version) assert version_str.startswith(CORE_FW_VERSION) assert fw.vendor_header.text == "SatoshiLabs" assert fw.build() == core_fw def test_vendor_header(core_fw: bytes) -> None: fw = VendorFirmware.parse(core_fw) vh_data = fw.vendor_header.build() assert vh_data in core_fw assert vh_data == VENDOR_HEADER.read_bytes() vh = VendorHeader.parse(vh_data) assert vh == fw.vendor_header vh.verify() with pytest.raises(construct.ConstructError): VendorFirmware.parse(vh_data) def test_core_code_hashes(core_fw: bytes) -> None: fw = VendorFirmware.parse(core_fw) fw.firmware.header.hashes = [] assert fw.digest().hex() == CORE_FW_FINGERPRINT def test_legacy_basic(legacy_fw: bytes) -> None: fw = LegacyFirmware.parse(legacy_fw) fw.verify() assert fw.digest().hex() == LEGACY_FW_FINGERPRINT assert fw.build() == legacy_fw def test_unsigned(legacy_fw: bytes) -> None: legacy = LegacyFirmware.parse(legacy_fw) legacy.verify() legacy.key_indexes = [0, 0, 0] legacy.signatures = [b"", b"", b""] with pytest.raises(firmware.Unsigned): legacy.verify() assert legacy.embedded_v2 is not None legacy.embedded_v2.verify() legacy.embedded_v2.header.v1_key_indexes = [0, 0, 0] legacy.embedded_v2.header.v1_signatures = [b"", b"", b""] with pytest.raises(firmware.Unsigned): legacy.embedded_v2.verify() def test_disallow_unsigned(core_fw: bytes) -> None: core = VendorFirmware.parse(core_fw) core.firmware.header.sigmask = 0 core.firmware.header.signature = b"" with pytest.raises(firmware.InvalidSignatureError): core.verify() def test_embedded_v2(legacy_fw: bytes) -> None: legacy = LegacyFirmware.parse(legacy_fw) assert legacy.embedded_v2 is not None legacy.embedded_v2.verify() embedded_data = legacy.embedded_v2.build() cutoff_data = legacy_fw[256:] assert cutoff_data == embedded_data embedded = LegacyV2Firmware.parse(cutoff_data) assert embedded == legacy.embedded_v2 def test_integrity_legacy(legacy_fw: bytes) -> None: legacy = LegacyFirmware.parse(legacy_fw) legacy.verify() modified_data = bytearray(legacy_fw) modified_data[-1] ^= 0x01 modified = LegacyFirmware.parse(modified_data) assert modified.digest() != legacy.digest() with pytest.raises(firmware.InvalidSignatureError): modified.verify() def test_integrity_core(core_fw: bytes) -> None: core = VendorFirmware.parse(core_fw) core.verify() modified_data = bytearray(core_fw) modified_data[-1] ^= 0x01 modified = VendorFirmware.parse(modified_data) assert modified.digest() != core.digest() with pytest.raises(firmware.FirmwareIntegrityError): modified.verify()