File: test_firmware.py

package info (click to toggle)
python-trezor 0.13.10-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,596 kB
  • sloc: python: 14,947; xml: 33; sh: 16; makefile: 3
file content (155 lines) | stat: -rw-r--r-- 4,259 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
from pathlib import Path

import construct
import pytest
import requests

from trezorlib import firmware
from trezorlib.firmware import (
    LegacyFirmware,
    LegacyV2Firmware,
    VendorFirmware,
    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"
    / "models"
    / "T2T1"
    / "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()