diff --git a/.gitignore b/.gitignore index 8052b8621..1d8824a32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.cache/ *.pyc .sconsign.dblite .vscode/ diff --git a/SConscript.firmware b/SConscript.firmware index 6caba9d2e..49e79befd 100644 --- a/SConscript.firmware +++ b/SConscript.firmware @@ -24,7 +24,8 @@ CPPPATH_MOD += [ CPPDEFINES_MOD += [ 'AES_128', 'AES_192', - 'USE_KECCAK', + ('USE_KECCAK', '1'), + ('USE_ETHEREUM', '1'), ] SOURCE_MOD += [ 'embed/extmod/modtrezorcrypto/modtrezorcrypto.c', diff --git a/SConscript.unix b/SConscript.unix index 00726f968..e312e3712 100644 --- a/SConscript.unix +++ b/SConscript.unix @@ -25,7 +25,8 @@ CPPPATH_MOD += [ CPPDEFINES_MOD += [ 'AES_128', 'AES_192', - 'USE_KECCAK', + ('USE_KECCAK', '1'), + ('USE_ETHEREUM', '1'), ] SOURCE_MOD += [ 'embed/extmod/modtrezorcrypto/modtrezorcrypto.c', diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h index 83b19e16c..104f33fd6 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h @@ -298,6 +298,21 @@ STATIC mp_obj_t mod_trezorcrypto_HDNode_address(mp_obj_t self, mp_obj_t version) } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_HDNode_address_obj, mod_trezorcrypto_HDNode_address); +/// def ethereum_pubkeyhash(self) -> bytes: +/// ''' +/// Compute an Ethereum pubkeyhash (aka address) from the HD node. +/// ''' +STATIC mp_obj_t mod_trezorcrypto_HDNode_ethereum_pubkeyhash(mp_obj_t self) { + mp_obj_HDNode_t *o = MP_OBJ_TO_PTR(self); + + vstr_t vstr; + vstr_init_len(&vstr, 20); + + hdnode_get_ethereum_pubkeyhash(&o->hdnode, (uint8_t *)vstr.buf); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_HDNode_ethereum_pubkeyhash_obj, mod_trezorcrypto_HDNode_ethereum_pubkeyhash); + STATIC const mp_rom_map_elem_t mod_trezorcrypto_HDNode_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_derive), MP_ROM_PTR(&mod_trezorcrypto_HDNode_derive_obj) }, { MP_ROM_QSTR(MP_QSTR_derive_path), MP_ROM_PTR(&mod_trezorcrypto_HDNode_derive_path_obj) }, @@ -312,6 +327,7 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_HDNode_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_private_key), MP_ROM_PTR(&mod_trezorcrypto_HDNode_private_key_obj) }, { MP_ROM_QSTR(MP_QSTR_public_key), MP_ROM_PTR(&mod_trezorcrypto_HDNode_public_key_obj) }, { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&mod_trezorcrypto_HDNode_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_ethereum_pubkeyhash), MP_ROM_PTR(&mod_trezorcrypto_HDNode_ethereum_pubkeyhash_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_HDNode_locals_dict, mod_trezorcrypto_HDNode_locals_dict_table); diff --git a/src/apps/ethereum/__init__.py b/src/apps/ethereum/__init__.py index cfe9e92da..52c9f7736 100644 --- a/src/apps/ethereum/__init__.py +++ b/src/apps/ethereum/__init__.py @@ -1,7 +1,7 @@ from trezor.wire import register, protobuf_workflow from trezor.utils import unimport from trezor.messages.wire_types import \ - EthereumGetAddress, EthereumSignTx + EthereumGetAddress, EthereumSignTx, EthereumSignMessage, EthereumVerifyMessage @unimport @@ -16,6 +16,20 @@ def dispatch_EthereumSignTx(*args, **kwargs): return ethereum_sign_tx(*args, **kwargs) +@unimport +def dispatch_EthereumSignMessage(*args, **kwargs): + from .sign_message import ethereum_sign_message + return ethereum_sign_message(*args, **kwargs) + + +@unimport +def dispatch_EthereumVerifyMessage(*args, **kwargs): + from .verify_message import ethereum_verify_message + return ethereum_verify_message(*args, **kwargs) + + def boot(): register(EthereumGetAddress, protobuf_workflow, dispatch_EthereumGetAddress) register(EthereumSignTx, protobuf_workflow, dispatch_EthereumSignTx) + register(EthereumSignMessage, protobuf_workflow, dispatch_EthereumSignMessage) + register(EthereumVerifyMessage, protobuf_workflow, dispatch_EthereumVerifyMessage) diff --git a/src/apps/ethereum/sign_message.py b/src/apps/ethereum/sign_message.py new file mode 100644 index 000000000..32bf1aac7 --- /dev/null +++ b/src/apps/ethereum/sign_message.py @@ -0,0 +1,34 @@ +from trezor.utils import unimport + + +def message_digest(message): + from apps.wallet.sign_tx.signing import write_varint + from trezor.crypto.hashlib import sha3_256 + from apps.common.hash_writer import HashWriter + + h = HashWriter(sha3_256) + signed_message_header = 'Ethereum Signed Message:\n' + write_varint(h, len(signed_message_header)) + h.extend(signed_message_header) + write_varint(h, len(message)) + h.extend(message) + + return h.get_digest(True) + + +@unimport +async def ethereum_sign_message(ctx, msg): + from trezor.messages.EthereumMessageSignature import EthereumMessageSignature + from trezor.crypto.curve import secp256k1 + from ..common import seed + + address_n = msg.address_n or () + node = await seed.get_root(ctx) + node.derive_path(address_n) + + signature = secp256k1.sign(node.private_key(), message_digest(msg.message), False) + + sig = EthereumMessageSignature() + sig.address = node.ethereum_pubkeyhash() + sig.signature = signature[1:] + bytearray([signature[0]]) + return sig diff --git a/src/apps/ethereum/verify_message.py b/src/apps/ethereum/verify_message.py new file mode 100644 index 000000000..282ecc36a --- /dev/null +++ b/src/apps/ethereum/verify_message.py @@ -0,0 +1,30 @@ +from trezor.utils import unimport + + +@unimport +async def ethereum_verify_message(ctx, msg): + from .sign_message import message_digest + from trezor.crypto.curve import secp256k1 + from trezor.crypto.hashlib import sha3_256 + from trezor import ui + from trezor.messages.Success import Success + + digest = message_digest(msg.message) + sig = bytearray([msg.signature[64]]) + msg.signature[:64] + pubkey = secp256k1.verify_recover(sig, digest) + + if not pubkey: + raise ValueError('Invalid signature') + + pkh = sha3_256(pubkey[1:]).digest(True)[-20:] + + if msg.address != pkh: + raise ValueError('Invalid signature') + + ui.display.clear() + ui.display.text(10, 30, 'Verifying message', + ui.BOLD, ui.LIGHT_GREEN, ui.BG) + ui.display.text(10, 60, msg.message, ui.MONO, ui.FG, ui.BG) + ui.display.text(10, 80, msg.address, ui.MONO, ui.FG, ui.BG) + + return Success(message='Message verified') diff --git a/tests/.gitignore b/tests/.gitignore deleted file mode 100644 index ceddaa37f..000000000 --- a/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.cache/