mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-27 23:02:02 +00:00
refactor(python/firmware): introduce legacy v3 signing detection
related, change verify() method to accept dev_keys bool and select the appropriate keys, as opposed to caller needing to figure out the keys
This commit is contained in:
parent
66eedd164c
commit
b37afe0199
1
python/.changelog.d/2701.added
Normal file
1
python/.changelog.d/2701.added
Normal file
@ -0,0 +1 @@
|
|||||||
|
Add support for v3-style Trezor One signatures.
|
1
python/.changelog.d/2701.incompatible
Normal file
1
python/.changelog.d/2701.incompatible
Normal file
@ -0,0 +1 @@
|
|||||||
|
Instead of accepting a list of public keys, `FirmwareType.verify()` accepts a parameter configuring whether to use production or development keys.
|
@ -47,7 +47,7 @@ if t.TYPE_CHECKING:
|
|||||||
def parse(cls: t.Type[T], data: bytes) -> T:
|
def parse(cls: t.Type[T], data: bytes) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
def verify(self, public_keys: t.Sequence[bytes] = ()) -> None:
|
def verify(self, dev_keys: bool = False) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
def digest(self) -> bytes:
|
def digest(self) -> bytes:
|
||||||
|
@ -185,9 +185,11 @@ class VendorFirmware(Struct):
|
|||||||
def digest(self) -> bytes:
|
def digest(self) -> bytes:
|
||||||
return self.firmware.digest()
|
return self.firmware.digest()
|
||||||
|
|
||||||
def verify(self, _public_keys: t.Sequence[bytes] = ()) -> None:
|
def verify(self, dev_keys: bool = False) -> None:
|
||||||
if _public_keys:
|
if dev_keys:
|
||||||
raise ValueError("Cannot supply custom keys for vendor firmware.")
|
raise ValueError(
|
||||||
|
"Cannot select dev keys for a vendor firmware; use development vendor header instead."
|
||||||
|
)
|
||||||
|
|
||||||
self.firmware.validate_code_hashes()
|
self.firmware.validate_code_hashes()
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import construct as c
|
|||||||
import ecdsa
|
import ecdsa
|
||||||
from construct_classes import Struct, subcon
|
from construct_classes import Struct, subcon
|
||||||
|
|
||||||
from . import consts, util
|
from . import consts, models, util
|
||||||
from .core import FirmwareImage
|
from .core import FirmwareImage
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -32,23 +32,32 @@ __all__ = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
ZERO_SIG = b"\x00" * 64
|
||||||
|
|
||||||
|
|
||||||
def check_sig_v1(
|
def check_sig_v1(
|
||||||
digest: bytes,
|
digest: bytes,
|
||||||
key_indexes: t.Sequence[int],
|
key_indexes: t.Sequence[int],
|
||||||
signatures: t.Sequence[bytes],
|
signatures: t.Sequence[bytes],
|
||||||
|
sigs_required: int,
|
||||||
public_keys: t.Sequence[bytes],
|
public_keys: t.Sequence[bytes],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Validate signatures of `digest` using the Trezor One V1 method."""
|
"""Validate signatures of `digest` using the Trezor One V1 method."""
|
||||||
distinct_indexes = set(i for i in key_indexes if i != 0)
|
distinct_indexes = set(i for i in key_indexes[:sigs_required] if i != 0)
|
||||||
if not distinct_indexes:
|
if not distinct_indexes:
|
||||||
raise util.Unsigned
|
raise util.Unsigned
|
||||||
|
|
||||||
if len(distinct_indexes) < len(key_indexes):
|
if len(distinct_indexes) != sigs_required:
|
||||||
raise util.InvalidSignatureError(
|
raise util.InvalidSignatureError(
|
||||||
f"Not enough distinct signatures (found {len(distinct_indexes)}, need {len(key_indexes)})"
|
f"Not enough distinct signatures (found {len(distinct_indexes)}, need {sigs_required})"
|
||||||
)
|
)
|
||||||
|
|
||||||
for i in range(len(key_indexes)):
|
if any(k != 0 for k in key_indexes[sigs_required:]) or any(
|
||||||
|
sig != ZERO_SIG for sig in signatures[sigs_required:]
|
||||||
|
):
|
||||||
|
raise util.InvalidSignatureError("Too many signatures")
|
||||||
|
|
||||||
|
for i in range(sigs_required):
|
||||||
key_idx = key_indexes[i] - 1
|
key_idx = key_indexes[i] - 1
|
||||||
signature = signatures[i]
|
signature = signatures[i]
|
||||||
|
|
||||||
@ -56,14 +65,36 @@ def check_sig_v1(
|
|||||||
# unknown pubkey
|
# unknown pubkey
|
||||||
raise util.InvalidSignatureError(f"Unknown key in slot {i}")
|
raise util.InvalidSignatureError(f"Unknown key in slot {i}")
|
||||||
|
|
||||||
pubkey = public_keys[key_idx][1:]
|
verify = ecdsa.VerifyingKey.from_string(
|
||||||
verify = ecdsa.VerifyingKey.from_string(pubkey, curve=ecdsa.curves.SECP256k1)
|
public_keys[key_idx],
|
||||||
|
curve=ecdsa.curves.SECP256k1,
|
||||||
|
hashfunc=hashlib.sha256,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
verify.verify_digest(signature, digest)
|
verify.verify_digest(signature, digest)
|
||||||
except ecdsa.BadSignatureError as e:
|
except ecdsa.BadSignatureError as e:
|
||||||
raise util.InvalidSignatureError(f"Invalid signature in slot {i}") from e
|
raise util.InvalidSignatureError(f"Invalid signature in slot {i}") from e
|
||||||
|
|
||||||
|
|
||||||
|
def check_sig_signmessage(
|
||||||
|
digest: bytes,
|
||||||
|
key_indexes: t.Sequence[int],
|
||||||
|
signatures: t.Sequence[bytes],
|
||||||
|
sigs_required: int,
|
||||||
|
public_keys: t.Sequence[bytes],
|
||||||
|
) -> None:
|
||||||
|
"""Validate signatures of `digest` using the Trezor One SignMessage method."""
|
||||||
|
btc_digest = hashlib.sha256(b"\x18Bitcoin Signed Message:\n\x20" + digest).digest()
|
||||||
|
final_digest = hashlib.sha256(btc_digest).digest()
|
||||||
|
check_sig_v1(
|
||||||
|
final_digest,
|
||||||
|
key_indexes,
|
||||||
|
signatures,
|
||||||
|
sigs_required,
|
||||||
|
public_keys,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LegacyV2Firmware(FirmwareImage):
|
class LegacyV2Firmware(FirmwareImage):
|
||||||
"""Firmware image in the format used by Trezor One 1.8.0 and newer."""
|
"""Firmware image in the format used by Trezor One 1.8.0 and newer."""
|
||||||
|
|
||||||
@ -73,17 +104,49 @@ class LegacyV2Firmware(FirmwareImage):
|
|||||||
padding_byte=b"\xff",
|
padding_byte=b"\xff",
|
||||||
)
|
)
|
||||||
|
|
||||||
def verify(
|
V3_FIRST_VERSION = (1, 12, 0)
|
||||||
self, public_keys: t.Sequence[bytes] = consts.V1_BOOTLOADER_KEYS
|
|
||||||
) -> None:
|
def verify_v2(self, dev_keys: bool) -> None:
|
||||||
|
if not dev_keys:
|
||||||
|
public_keys = models.TREZOR_ONE_V1V2.firmware_keys
|
||||||
|
else:
|
||||||
|
public_keys = models.TREZOR_ONE_V1V2_DEV.firmware_keys
|
||||||
|
|
||||||
self.validate_code_hashes()
|
self.validate_code_hashes()
|
||||||
check_sig_v1(
|
check_sig_v1(
|
||||||
self.digest(),
|
self.digest(),
|
||||||
self.header.v1_key_indexes,
|
self.header.v1_key_indexes,
|
||||||
self.header.v1_signatures,
|
self.header.v1_signatures,
|
||||||
|
models.TREZOR_ONE_V1V2.firmware_sigs_needed,
|
||||||
public_keys,
|
public_keys,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def verify_v3(self, dev_keys: bool) -> None:
|
||||||
|
if not dev_keys:
|
||||||
|
model_keys = models.TREZOR_ONE_V3
|
||||||
|
else:
|
||||||
|
model_keys = models.TREZOR_ONE_V3_DEV
|
||||||
|
|
||||||
|
self.validate_code_hashes()
|
||||||
|
check_sig_signmessage(
|
||||||
|
self.digest(),
|
||||||
|
self.header.v1_key_indexes,
|
||||||
|
self.header.v1_signatures,
|
||||||
|
model_keys.firmware_sigs_needed,
|
||||||
|
model_keys.firmware_keys,
|
||||||
|
)
|
||||||
|
|
||||||
|
def verify(self, dev_keys: bool = False) -> None:
|
||||||
|
if self.header.version >= self.V3_FIRST_VERSION:
|
||||||
|
try:
|
||||||
|
self.verify_v3(dev_keys)
|
||||||
|
except util.InvalidSignatureError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.verify_v2(dev_keys)
|
||||||
|
|
||||||
def verify_unsigned(self) -> None:
|
def verify_unsigned(self) -> None:
|
||||||
self.validate_code_hashes()
|
self.validate_code_hashes()
|
||||||
if any(i != 0 for i in self.header.v1_key_indexes):
|
if any(i != 0 for i in self.header.v1_key_indexes):
|
||||||
@ -126,15 +189,18 @@ class LegacyFirmware(Struct):
|
|||||||
def digest(self) -> bytes:
|
def digest(self) -> bytes:
|
||||||
return hashlib.sha256(self.code).digest()
|
return hashlib.sha256(self.code).digest()
|
||||||
|
|
||||||
def verify(
|
def verify(self, dev_keys: bool = False) -> None:
|
||||||
self, public_keys: t.Sequence[bytes] = consts.V1_BOOTLOADER_KEYS
|
if not dev_keys:
|
||||||
) -> None:
|
model_keys = models.TREZOR_ONE_V1V2
|
||||||
|
else:
|
||||||
|
model_keys = models.TREZOR_ONE_V1V2_DEV
|
||||||
check_sig_v1(
|
check_sig_v1(
|
||||||
self.digest(),
|
self.digest(),
|
||||||
self.key_indexes,
|
self.key_indexes,
|
||||||
self.signatures,
|
self.signatures,
|
||||||
public_keys,
|
model_keys.firmware_sigs_needed,
|
||||||
|
model_keys.firmware_keys,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.embedded_v2:
|
if self.embedded_v2:
|
||||||
self.embedded_v2.verify(consts.V1_BOOTLOADER_KEYS)
|
self.embedded_v2.verify()
|
||||||
|
@ -24,7 +24,8 @@ from construct_classes import Struct, subcon
|
|||||||
from .. import cosi
|
from .. import cosi
|
||||||
from ..toif import ToifStruct
|
from ..toif import ToifStruct
|
||||||
from ..tools import TupleAdapter
|
from ..tools import TupleAdapter
|
||||||
from . import consts, util
|
from . import util
|
||||||
|
from .models import TREZOR_T, TREZOR_T_DEV
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"VendorTrust",
|
"VendorTrust",
|
||||||
@ -125,14 +126,19 @@ class VendorHeader(Struct):
|
|||||||
h.update(b"\x00" * 32)
|
h.update(b"\x00" * 32)
|
||||||
return h.digest()
|
return h.digest()
|
||||||
|
|
||||||
def verify(self, pubkeys: t.Sequence[bytes] = consts.V2_BOOTLOADER_KEYS) -> None:
|
def verify(self, dev_keys: bool = False) -> None:
|
||||||
digest = self.digest()
|
digest = self.digest()
|
||||||
|
if not dev_keys:
|
||||||
|
public_keys = TREZOR_T.bootloader_keys
|
||||||
|
else:
|
||||||
|
public_keys = TREZOR_T_DEV.bootloader_keys
|
||||||
|
# TODO: add model awareness
|
||||||
try:
|
try:
|
||||||
cosi.verify(
|
cosi.verify(
|
||||||
self.signature,
|
self.signature,
|
||||||
digest,
|
digest,
|
||||||
consts.V2_SIGS_REQUIRED,
|
TREZOR_T.bootloader_sigs_needed,
|
||||||
pubkeys,
|
public_keys,
|
||||||
self.sigmask,
|
self.sigmask,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
Loading…
Reference in New Issue
Block a user