mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-07 08:38:46 +00:00
feat(core): update Ethereum definitions to verify using CoSi
This commit is contained in:
parent
dc5e4c1c8f
commit
b92c5c21f9
@ -1,17 +1,8 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from trezor import protobuf, utils
|
|
||||||
from trezor.crypto.curve import ed25519
|
|
||||||
from trezor.crypto.hashlib import sha256
|
|
||||||
from trezor.enums import EthereumDefinitionType
|
|
||||||
from trezor.messages import EthereumNetworkInfo, EthereumTokenInfo
|
from trezor.messages import EthereumNetworkInfo, EthereumTokenInfo
|
||||||
from trezor.wire import DataError
|
from trezor.wire import DataError
|
||||||
|
|
||||||
from apps.common import readers
|
|
||||||
|
|
||||||
from . import definitions_constants as consts, networks, tokens
|
|
||||||
from .networks import UNKNOWN_NETWORK
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
@ -20,8 +11,17 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
def decode_definition(definition: bytes, expected_type: type[DefType]) -> DefType:
|
def decode_definition(definition: bytes, expected_type: type[DefType]) -> DefType:
|
||||||
|
from trezor.crypto.cosi import verify as cosi_verify
|
||||||
|
from trezor.crypto.hashlib import sha256
|
||||||
|
from trezor.enums import EthereumDefinitionType
|
||||||
|
from trezor.protobuf import decode as protobuf_decode
|
||||||
|
from trezor.utils import BufferReader
|
||||||
|
|
||||||
|
from apps.common import readers
|
||||||
|
from . import definitions_constants as consts
|
||||||
|
|
||||||
# check network definition
|
# check network definition
|
||||||
r = utils.BufferReader(definition)
|
r = BufferReader(definition)
|
||||||
expected_type_number = EthereumDefinitionType.NETWORK
|
expected_type_number = EthereumDefinitionType.NETWORK
|
||||||
# TODO: can't check equality of MsgDefObjs now, so we check the name
|
# TODO: can't check equality of MsgDefObjs now, so we check the name
|
||||||
if expected_type.MESSAGE_NAME == EthereumTokenInfo.MESSAGE_NAME:
|
if expected_type.MESSAGE_NAME == EthereumTokenInfo.MESSAGE_NAME:
|
||||||
@ -59,7 +59,8 @@ def decode_definition(definition: bytes, expected_type: type[DefType]) -> DefTyp
|
|||||||
hasher.update(hash_b)
|
hasher.update(hash_b)
|
||||||
hash = hasher.digest()
|
hash = hasher.digest()
|
||||||
|
|
||||||
signed_tree_root = r.read_memoryview(64)
|
sigmask = r.get()
|
||||||
|
signature = r.read_memoryview(64)
|
||||||
|
|
||||||
if r.remaining_count():
|
if r.remaining_count():
|
||||||
raise DataError("Invalid Ethereum definition")
|
raise DataError("Invalid Ethereum definition")
|
||||||
@ -68,22 +69,18 @@ def decode_definition(definition: bytes, expected_type: type[DefType]) -> DefTyp
|
|||||||
raise DataError("Invalid Ethereum definition")
|
raise DataError("Invalid Ethereum definition")
|
||||||
|
|
||||||
# verify signature
|
# verify signature
|
||||||
if not ed25519.verify(consts.DEFINITIONS_PUBLIC_KEY, signed_tree_root, hash):
|
result = cosi_verify(signature, hash, consts.THRESHOLD, consts.PUBLIC_KEYS, sigmask)
|
||||||
error_msg = DataError("Invalid definition signature")
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
# check against dev key
|
debug_result = cosi_verify(
|
||||||
if not ed25519.verify(
|
signature, hash, consts.THRESHOLD, consts.DEV_PUBLIC_KEYS, sigmask
|
||||||
consts.DEFINITIONS_DEV_PUBLIC_KEY,
|
)
|
||||||
signed_tree_root,
|
result = result or debug_result
|
||||||
hash,
|
if not result:
|
||||||
):
|
raise DataError("Invalid definition signature")
|
||||||
raise error_msg
|
|
||||||
else:
|
|
||||||
raise error_msg
|
|
||||||
|
|
||||||
# decode it if it's OK
|
# decode it if it's OK
|
||||||
try:
|
try:
|
||||||
return protobuf.decode(payload, expected_type, True)
|
return protobuf_decode(payload, expected_type, True)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise DataError("Invalid Ethereum definition")
|
raise DataError("Invalid Ethereum definition")
|
||||||
|
|
||||||
@ -107,14 +104,16 @@ class Definitions:
|
|||||||
chain_id: int | None = None,
|
chain_id: int | None = None,
|
||||||
slip44: int | None = None,
|
slip44: int | None = None,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
from .networks import UNKNOWN_NETWORK, by_chain_id, by_slip44
|
||||||
|
|
||||||
network = UNKNOWN_NETWORK
|
network = UNKNOWN_NETWORK
|
||||||
tokens: dict[bytes, EthereumTokenInfo] = {}
|
tokens: dict[bytes, EthereumTokenInfo] = {}
|
||||||
|
|
||||||
# if we have a built-in definition, use it
|
# if we have a built-in definition, use it
|
||||||
if chain_id is not None:
|
if chain_id is not None:
|
||||||
network = networks.by_chain_id(chain_id)
|
network = by_chain_id(chain_id)
|
||||||
elif slip44 is not None:
|
elif slip44 is not None:
|
||||||
network = networks.by_slip44(slip44)
|
network = by_slip44(slip44)
|
||||||
else:
|
else:
|
||||||
# ignore encoded definitions if we can't match them to request details
|
# ignore encoded definitions if we can't match them to request details
|
||||||
return cls(UNKNOWN_NETWORK, {})
|
return cls(UNKNOWN_NETWORK, {})
|
||||||
@ -143,12 +142,14 @@ class Definitions:
|
|||||||
return cls(network, tokens)
|
return cls(network, tokens)
|
||||||
|
|
||||||
def get_token(self, address: bytes) -> EthereumTokenInfo:
|
def get_token(self, address: bytes) -> EthereumTokenInfo:
|
||||||
|
from .tokens import token_by_chain_address, UNKNOWN_TOKEN
|
||||||
|
|
||||||
# if we have a built-in definition, use it
|
# if we have a built-in definition, use it
|
||||||
token = tokens.token_by_chain_address(self.network.chain_id, address)
|
token = token_by_chain_address(self.network.chain_id, address)
|
||||||
if token is not None:
|
if token is not None:
|
||||||
return token
|
return token
|
||||||
|
|
||||||
if address in self._tokens:
|
if address in self._tokens:
|
||||||
return self._tokens[address]
|
return self._tokens[address]
|
||||||
|
|
||||||
return tokens.UNKNOWN_TOKEN
|
return UNKNOWN_TOKEN
|
||||||
|
@ -2,13 +2,19 @@
|
|||||||
# (by running `make templates` in `core`)
|
# (by running `make templates` in `core`)
|
||||||
# do not edit manually!
|
# do not edit manually!
|
||||||
|
|
||||||
from ubinascii import unhexlify
|
THRESHOLD = 2
|
||||||
|
PUBLIC_KEYS = (
|
||||||
|
b"\x43\x34\x99\x63\x43\x62\x3e\x46\x2f\x0f\xc9\x33\x11\xfe\xf1\x48\x4c\xa2\x3d\x2f\xf1\xee\xc6\xdf\x1f\xa8\xeb\x7e\x35\x73\xb3\xdb",
|
||||||
|
b"\xa9\xa2\x2c\xc2\x65\xa0\xcb\x1d\x6c\xb3\x29\xbc\x0e\x60\xbc\x45\xdf\x76\xb9\xab\x28\xfb\x87\xb6\x11\x36\xfe\xaf\x8d\x8f\xdc\x96",
|
||||||
|
b"\xb8\xd2\xb2\x1d\xe2\x71\x24\xf0\x51\x1f\x90\x3a\xe7\xe6\x0e\x07\x96\x18\x10\xa0\xb8\xf2\x8e\xa7\x55\xfa\x50\x36\x7a\x8a\x2b\x8b",
|
||||||
|
)
|
||||||
|
|
||||||
DEFINITIONS_PUBLIC_KEY = b""
|
|
||||||
MIN_DATA_VERSION = 1669892465
|
MIN_DATA_VERSION = 1669892465
|
||||||
FORMAT_VERSION = b"trzd1"
|
FORMAT_VERSION = b"trzd1"
|
||||||
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
DEFINITIONS_DEV_PUBLIC_KEY = unhexlify(
|
DEV_PUBLIC_KEYS = (
|
||||||
"db995fe25169d141cab9bbba92baa01f9f2e1ece7df4cb2ac05190f37fcc1f9d"
|
b"\x68\x46\x0e\xbe\xf3\xb1\x38\x16\x4e\xc7\xfd\x86\x10\xe9\x58\x00\xdf\x75\x98\xf7\x0f\x2f\x2e\xa7\xdb\x51\x72\xac\x74\xeb\xc1\x44",
|
||||||
|
b"\x8d\x4a\xbe\x07\x4f\xef\x92\x29\xd3\xb4\x41\xdf\xea\x4f\x98\xf8\x05\xb1\xa2\xb3\xa0\x6a\xe6\x45\x81\x0e\xfe\xce\x77\xfd\x50\x44",
|
||||||
|
b"\x97\xf7\x13\x5a\x9a\x26\x90\xe7\x3b\xeb\x26\x55\x6f\x1c\xb1\x63\xbe\xa2\x53\x2a\xff\xa1\xe7\x78\x24\x30\xbe\x98\xc0\xe5\x68\x12",
|
||||||
)
|
)
|
||||||
|
@ -2,13 +2,19 @@
|
|||||||
# (by running `make templates` in `core`)
|
# (by running `make templates` in `core`)
|
||||||
# do not edit manually!
|
# do not edit manually!
|
||||||
|
|
||||||
from ubinascii import unhexlify
|
THRESHOLD = 2
|
||||||
|
PUBLIC_KEYS = (
|
||||||
|
b"\x43\x34\x99\x63\x43\x62\x3e\x46\x2f\x0f\xc9\x33\x11\xfe\xf1\x48\x4c\xa2\x3d\x2f\xf1\xee\xc6\xdf\x1f\xa8\xeb\x7e\x35\x73\xb3\xdb",
|
||||||
|
b"\xa9\xa2\x2c\xc2\x65\xa0\xcb\x1d\x6c\xb3\x29\xbc\x0e\x60\xbc\x45\xdf\x76\xb9\xab\x28\xfb\x87\xb6\x11\x36\xfe\xaf\x8d\x8f\xdc\x96",
|
||||||
|
b"\xb8\xd2\xb2\x1d\xe2\x71\x24\xf0\x51\x1f\x90\x3a\xe7\xe6\x0e\x07\x96\x18\x10\xa0\xb8\xf2\x8e\xa7\x55\xfa\x50\x36\x7a\x8a\x2b\x8b",
|
||||||
|
)
|
||||||
|
|
||||||
DEFINITIONS_PUBLIC_KEY = b""
|
|
||||||
MIN_DATA_VERSION = ${ethereum_defs_timestamp}
|
MIN_DATA_VERSION = ${ethereum_defs_timestamp}
|
||||||
FORMAT_VERSION = b"trzd1"
|
FORMAT_VERSION = b"trzd1"
|
||||||
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
DEFINITIONS_DEV_PUBLIC_KEY = unhexlify(
|
DEV_PUBLIC_KEYS = (
|
||||||
"db995fe25169d141cab9bbba92baa01f9f2e1ece7df4cb2ac05190f37fcc1f9d"
|
b"\x68\x46\x0e\xbe\xf3\xb1\x38\x16\x4e\xc7\xfd\x86\x10\xe9\x58\x00\xdf\x75\x98\xf7\x0f\x2f\x2e\xa7\xdb\x51\x72\xac\x74\xeb\xc1\x44",
|
||||||
|
b"\x8d\x4a\xbe\x07\x4f\xef\x92\x29\xd3\xb4\x41\xdf\xea\x4f\x98\xf8\x05\xb1\xa2\xb3\xa0\x6a\xe6\x45\x81\x0e\xfe\xce\x77\xfd\x50\x44",
|
||||||
|
b"\x97\xf7\x13\x5a\x9a\x26\x90\xe7\x3b\xeb\x26\x55\x6f\x1c\xb1\x63\xbe\xa2\x53\x2a\xff\xa1\xe7\x78\x24\x30\xbe\x98\xc0\xe5\x68\x12",
|
||||||
)
|
)
|
||||||
|
@ -2,12 +2,11 @@ from ubinascii import unhexlify # noqa: F401
|
|||||||
|
|
||||||
from trezor import messages, protobuf
|
from trezor import messages, protobuf
|
||||||
from trezor.enums import EthereumDefinitionType
|
from trezor.enums import EthereumDefinitionType
|
||||||
|
from trezor.crypto import cosi
|
||||||
from trezor.crypto.curve import ed25519
|
from trezor.crypto.curve import ed25519
|
||||||
from trezor.crypto.hashlib import sha256
|
from trezor.crypto.hashlib import sha256
|
||||||
|
|
||||||
DEFINITIONS_DEV_PRIVATE_KEY = unhexlify(
|
PRIVATE_KEYS_DEV = [byte * 32 for byte in (b"\xdd", b"\xde", b"\xdf")]
|
||||||
"4141414141414141414141414141414141414141414141414141414141414141"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def make_network(
|
def make_network(
|
||||||
@ -60,7 +59,11 @@ def make_payload(
|
|||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
def sign_payload(payload: bytes, merkle_neighbors: list[bytes]) -> tuple[bytes, bytes]:
|
def sign_payload(
|
||||||
|
payload: bytes,
|
||||||
|
merkle_neighbors: list[bytes],
|
||||||
|
threshold: int = 3,
|
||||||
|
) -> tuple[bytes, bytes]:
|
||||||
digest = sha256(b"\x00" + payload).digest()
|
digest = sha256(b"\x00" + payload).digest()
|
||||||
merkle_proof = []
|
merkle_proof = []
|
||||||
for item in merkle_neighbors:
|
for item in merkle_neighbors:
|
||||||
@ -69,8 +72,29 @@ def sign_payload(payload: bytes, merkle_neighbors: list[bytes]) -> tuple[bytes,
|
|||||||
merkle_proof.append(digest)
|
merkle_proof.append(digest)
|
||||||
|
|
||||||
merkle_proof = len(merkle_proof).to_bytes(1, "little") + b"".join(merkle_proof)
|
merkle_proof = len(merkle_proof).to_bytes(1, "little") + b"".join(merkle_proof)
|
||||||
signature = ed25519.sign(DEFINITIONS_DEV_PRIVATE_KEY, digest)
|
|
||||||
return merkle_proof, signature
|
nonces, commits, pubkeys = [], [], []
|
||||||
|
for i, private_key in enumerate(PRIVATE_KEYS_DEV[:threshold]):
|
||||||
|
nonce, commit = cosi.commit()
|
||||||
|
pubkey = ed25519.publickey(private_key)
|
||||||
|
nonces.append(nonce)
|
||||||
|
commits.append(commit)
|
||||||
|
pubkeys.append(pubkey)
|
||||||
|
|
||||||
|
global_commit = cosi.combine_publickeys(commits)
|
||||||
|
global_pubkey = cosi.combine_publickeys(pubkeys)
|
||||||
|
sigmask = 0
|
||||||
|
signatures = []
|
||||||
|
for i, nonce in enumerate(nonces):
|
||||||
|
sigmask |= 1 << i
|
||||||
|
sig = cosi.sign(
|
||||||
|
PRIVATE_KEYS_DEV[i], digest, nonce, global_commit, global_pubkey
|
||||||
|
)
|
||||||
|
signatures.append(sig)
|
||||||
|
|
||||||
|
signature = cosi.combine_signatures(global_commit, signatures)
|
||||||
|
sigmask_byte = sigmask.to_bytes(1, "little")
|
||||||
|
return merkle_proof, sigmask_byte + signature
|
||||||
|
|
||||||
|
|
||||||
def encode_network(
|
def encode_network(
|
||||||
|
@ -56,6 +56,11 @@ class TestDecodeDefinition(unittest.TestCase):
|
|||||||
bad_signature = signature[:-1] + b"\xff"
|
bad_signature = signature[:-1] + b"\xff"
|
||||||
self.assertFailed(payload + proof + bad_signature)
|
self.assertFailed(payload + proof + bad_signature)
|
||||||
|
|
||||||
|
def test_not_enough_signatures(self):
|
||||||
|
payload = make_payload()
|
||||||
|
proof, signature = sign_payload(payload, [], threshold=1)
|
||||||
|
self.assertFailed(payload + proof + signature)
|
||||||
|
|
||||||
def test_missing_signature(self):
|
def test_missing_signature(self):
|
||||||
payload = make_payload()
|
payload = make_payload()
|
||||||
proof, signature = sign_payload(payload, [])
|
proof, signature = sign_payload(payload, [])
|
||||||
|
Loading…
Reference in New Issue
Block a user