feat(core): update Ethereum definitions to verify using CoSi

pull/2914/head
matejcik 1 year ago committed by matejcik
parent dc5e4c1c8f
commit b92c5c21f9

@ -1,17 +1,8 @@
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.wire import DataError
from apps.common import readers
from . import definitions_constants as consts, networks, tokens
from .networks import UNKNOWN_NETWORK
if TYPE_CHECKING:
from typing import TypeVar
from typing_extensions import Self
@ -20,8 +11,17 @@ if TYPE_CHECKING:
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
r = utils.BufferReader(definition)
r = BufferReader(definition)
expected_type_number = EthereumDefinitionType.NETWORK
# TODO: can't check equality of MsgDefObjs now, so we check the 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)
hash = hasher.digest()
signed_tree_root = r.read_memoryview(64)
sigmask = r.get()
signature = r.read_memoryview(64)
if r.remaining_count():
raise DataError("Invalid Ethereum definition")
@ -68,22 +69,18 @@ def decode_definition(definition: bytes, expected_type: type[DefType]) -> DefTyp
raise DataError("Invalid Ethereum definition")
# verify signature
if not ed25519.verify(consts.DEFINITIONS_PUBLIC_KEY, signed_tree_root, hash):
error_msg = DataError("Invalid definition signature")
if __debug__:
# check against dev key
if not ed25519.verify(
consts.DEFINITIONS_DEV_PUBLIC_KEY,
signed_tree_root,
hash,
):
raise error_msg
else:
raise error_msg
result = cosi_verify(signature, hash, consts.THRESHOLD, consts.PUBLIC_KEYS, sigmask)
if __debug__:
debug_result = cosi_verify(
signature, hash, consts.THRESHOLD, consts.DEV_PUBLIC_KEYS, sigmask
)
result = result or debug_result
if not result:
raise DataError("Invalid definition signature")
# decode it if it's OK
try:
return protobuf.decode(payload, expected_type, True)
return protobuf_decode(payload, expected_type, True)
except ValueError:
raise DataError("Invalid Ethereum definition")
@ -107,14 +104,16 @@ class Definitions:
chain_id: int | None = None,
slip44: int | None = None,
) -> Self:
from .networks import UNKNOWN_NETWORK, by_chain_id, by_slip44
network = UNKNOWN_NETWORK
tokens: dict[bytes, EthereumTokenInfo] = {}
# if we have a built-in definition, use it
if chain_id is not None:
network = networks.by_chain_id(chain_id)
network = by_chain_id(chain_id)
elif slip44 is not None:
network = networks.by_slip44(slip44)
network = by_slip44(slip44)
else:
# ignore encoded definitions if we can't match them to request details
return cls(UNKNOWN_NETWORK, {})
@ -143,12 +142,14 @@ class Definitions:
return cls(network, tokens)
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
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:
return token
if address in self._tokens:
return self._tokens[address]
return tokens.UNKNOWN_TOKEN
return UNKNOWN_TOKEN

@ -2,13 +2,19 @@
# (by running `make templates` in `core`)
# 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
FORMAT_VERSION = b"trzd1"
if __debug__:
DEFINITIONS_DEV_PUBLIC_KEY = unhexlify(
"db995fe25169d141cab9bbba92baa01f9f2e1ece7df4cb2ac05190f37fcc1f9d"
DEV_PUBLIC_KEYS = (
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`)
# 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}
FORMAT_VERSION = b"trzd1"
if __debug__:
DEFINITIONS_DEV_PUBLIC_KEY = unhexlify(
"db995fe25169d141cab9bbba92baa01f9f2e1ece7df4cb2ac05190f37fcc1f9d"
DEV_PUBLIC_KEYS = (
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.enums import EthereumDefinitionType
from trezor.crypto import cosi
from trezor.crypto.curve import ed25519
from trezor.crypto.hashlib import sha256
DEFINITIONS_DEV_PRIVATE_KEY = unhexlify(
"4141414141414141414141414141414141414141414141414141414141414141"
)
PRIVATE_KEYS_DEV = [byte * 32 for byte in (b"\xdd", b"\xde", b"\xdf")]
def make_network(
@ -60,7 +59,11 @@ def make_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()
merkle_proof = []
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 = 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(

@ -56,6 +56,11 @@ class TestDecodeDefinition(unittest.TestCase):
bad_signature = signature[:-1] + b"\xff"
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):
payload = make_payload()
proof, signature = sign_payload(payload, [])

Loading…
Cancel
Save