Skip to content

Commit

Permalink
add eth sign message cl and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
markrypt0 committed Sep 13, 2022
1 parent b17d773 commit 85a7e37
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 3 deletions.
15 changes: 15 additions & 0 deletions keepkeyctl
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,15 @@ class Commands(object):
address = self.client.ethereum_get_address(address_n, args.show_display)
return "0x%s" % (binascii.hexlify(address),)

def ethereum_sign_msg(self, args):
n = self.client.expand_path(args.n)
retval = self.client.ethereum_sign_message(
n = n,
message=bytes(args.message, 'utf8')
)
ret = "address: " + retval.address.hex() + "\n" + "signature: " + retval.signature.hex() + "\n"
return ret

def ethereum_sign_tx(self, args):
from ethjsonrpc import EthJsonRpc
from ethjsonrpc.utils import hex_to_dec
Expand Down Expand Up @@ -538,6 +547,7 @@ class Commands(object):
get_address.help = 'Get bitcoin address in base58 encoding'
get_xpub.help = 'Get xpub'
ethereum_get_address.help = 'Get Ethereum address in hex encoding'
ethereum_sign_msg.help = 'Sign Ethereum message'
ethereum_sign_tx.help = 'Sign (and optionally publish) Ethereum transaction'
ethereum_eip712.help = 'Verify and sign an Ethereum eip-712 message'
eos_get_public_key.help = 'Get EOS public key'
Expand Down Expand Up @@ -586,6 +596,11 @@ class Commands(object):
(('-d', '--show-display'), {'action': 'store_true', 'default': False}),
)

ethereum_sign_msg.arguments = (
(('-n', '-address'), {'type': str, 'help': 'BIP-32 path to signing key'}),
(('message',), {'type': str}),
)

ethereum_sign_tx.arguments = (
(('-a', '--host'), {'type': str, 'help': 'RPC port of ethereum node for automatic gas/nonce estimation', 'default': 'localhost:8545'}),
(('-c', '--chain-id'), {'type' : int, 'help': 'EIP-155 chain id (replay protection)', 'default': None}),
Expand Down
21 changes: 18 additions & 3 deletions keepkeylib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,9 +597,6 @@ def ethereum_sign_typed_data_hash(self, n, ds_hash, m_hash=None):
response = self.call(msg)
return response




@expect(eth_proto.EthereumTypedDataSignature)
def e712_types_values(self, n, types_prop, ptype_prop, value_prop, typevals):
msg = eth_proto.Ethereum712TypesValues(
Expand All @@ -613,6 +610,24 @@ def e712_types_values(self, n, types_prop, ptype_prop, value_prop, typevals):
response = self.call(msg)
return response

@expect(eth_proto.EthereumMessageSignature)
def ethereum_sign_message(self, n, message):
n = self._convert_prime(n)
msg = eth_proto.EthereumSignMessage(
address_n=n,
message=message
)
response = self.call(msg)
return response

def ethereum_verify_message(self, addr, signature, message):
msg = eth_proto.EthereumVerifyMessage(
address=addr,
signature=signature,
message=message
)
response = self.call(msg)
return response

@session
def ethereum_sign_tx(self, n, nonce, gas_limit, value, gas_price=None, max_fee_per_gas=None, max_priority_fee_per_gas=None, to=None, to_n=None, address_type=None, data=None, chain_id=None):
Expand Down
1 change: 1 addition & 0 deletions tests/common.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# This file is part of the TREZOR project.
#
# Copyright (C) 2022 markrypto
# Copyright (C) 2012-2016 Marek Palatinus <[email protected]>
# Copyright (C) 2012-2016 Pavol Rusnak <[email protected]>
#
Expand Down
109 changes: 109 additions & 0 deletions tests/test_msg_ethereum_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#
# Copyright (C) 2022 markrypto <[email protected]>
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library. If not, see <http://www.gnu.org/licenses/>.


import unittest
import common
import binascii

from keepkeylib import tools


class TestMsgEthereumMessage(common.KeepKeyTest):
def test_ethereum_sign_message(self):
self.setup_mnemonic_nopin_nopassphrase()

retval = self.client.ethereum_sign_message(
n = tools.parse_path("m/44'/60'/0'/0/0"),
message = b'\xee\xf8\x1f\x6d\x25\x17\xf4\x20\xfc\x0f\x59\x68\x4f\xb3\xd4\xcb\x9e\xbd\xf0\xbb\x3a\x8f\x60\x75\xb9\xc5\xe1\xf3\x21\x02\x31\xf0'

)
self.assertEqual(retval.address.hex(), '3f2329c9adfbccd9a84f52c906e936a42da18cb8')
self.assertEqual(binascii.hexlify(retval.signature), '040e7fb8c22e401828380daac1cff745dc7f8a6993009f06c3de83ef63ba33de54a735cbbba174b2527b85a116205e4cda442d439ecb784006d960a612900e3c1b')

def test_ethereum_sign_message_from_metamask(self):
# This test data is what is used on the Shapeshift native wallet
self.setup_mnemonic_nopin_nopassphrase()

retval = self.client.ethereum_sign_message(
n = tools.parse_path("m/44'/60'/0'/0/0"),
message = bytes("Hello, world!", 'utf8')
)
self.assertEqual(retval.address.hex(), '3f2329c9adfbccd9a84f52c906e936a42da18cb8')
self.assertEqual(binascii.hexlify(retval.signature), '111128bb8685b85843d423fa4844f2b4521b6e5aae8a5f7e1cc09bf9da116d5e27df6c7abb170853ca874fd9c4b413dd35a3c63e5a7d47594391a758b09d000f1b')

def test_ethereum_verify_message(self):
self.setup_mnemonic_nopin_nopassphrase()

retval = self.client.ethereum_verify_message(
addr = b'\x3f\x23\x29\xc9\xad\xfb\xcc\xd9\xa8\x4f\x52\xc9\x06\xe9\x36\xa4\x2d\xa1\x8c\xb8',
signature = b'KCuM\xde\\=>X8\xa7\\\xbf\xdb\xa7.u\xb3\x159\x7f\xb5\xd9X\x01\x96\x1a\xf0N\xa5\xf2*R\x8f\xa4.\xc8\x8an~\xaa\xdb[\xe3\x97:\x1b\x8cqI\x97L\x8a \xf3\xac\x18\xa5F/\xf5\x8e^\xba\x1b',
message = bytes("Good evening markrypto, want to play a game?", 'utf8')
)

retval = self.client.ethereum_sign_message(
n = tools.parse_path("m/44'/60'/0'/0/0"),
message = bytes("Good evening markrypto, want to play a game?", 'utf8')
)
self.assertEqual(retval.address.hex(), '3f2329c9adfbccd9a84f52c906e936a42da18cb8')
self.assertEqual(binascii.hexlify(retval.signature), '4b43754dde5c3d3e5838a75cbfdba72e75b315397fb5d95801961af04ea5f22a528fa42ec88a6e7eaadb5be3973a1b8c7149974c8a20f3ac18a5462ff58e5eba1b')

def test_ethereum_sign_message_from_nativedata(self):
# This test data is what is used on the Shapeshift native wallet
self.setup_mnemonic_nopin_nopassphrase()

retval = self.client.ethereum_sign_message(
n = tools.parse_path("m/44'/60'/0'/0/0"),
message = bytes("Hello world 111", 'utf8')
)
self.assertEqual(retval.address.hex(), '3f2329c9adfbccd9a84f52c906e936a42da18cb8')
self.assertEqual(binascii.hexlify(retval.signature), '05a0edb4b98fe6b6ed270bf55aef84ddcb641512e19e340bf9eed3427854a7a4734fe45551dc24f1843cf2c823a73aa2454e3785eb15120573c522cc114e472d1c')

def test_ethereum_sign_bytes(self):
self.setup_mnemonic_nopin_nopassphrase()

retval = self.client.ethereum_sign_message(
n = tools.parse_path("m/44'/60'/0'/0/0"),
message = b'\x1d\xf3\xd1\x0b\xe9\x35\xab\xc6\x3b\x65\x61\xcb\x61\x48\xb7\x45'
)

self.assertEqual(retval.address.hex(), '3f2329c9adfbccd9a84f52c906e936a42da18cb8')
self.assertEqual(binascii.hexlify(retval.signature), 'fc44af700a747a68b1b79170dd46fb5aad2ffe2aee2a6a9ef653a29350967daf1bf62ffb84a3523356baff572d1b1285a14036212e69a26ca194adb53a0e22a61b')

def test_ethereum_verify_message(self):
self.setup_mnemonic_nopin_nopassphrase()

retval = self.client.ethereum_verify_message(
addr = b'\x3f\x23\x29\xc9\xad\xfb\xcc\xd9\xa8\x4f\x52\xc9\x06\xe9\x36\xa4\x2d\xa1\x8c\xb8',
signature = b'KCuM\xde\\=>X8\xa7\\\xbf\xdb\xa7.u\xb3\x159\x7f\xb5\xd9X\x01\x96\x1a\xf0N\xa5\xf2*R\x8f\xa4.\xc8\x8an~\xaa\xdb[\xe3\x97:\x1b\x8cqI\x97L\x8a \xf3\xac\x18\xa5F/\xf5\x8e^\xba\x1b',
message = bytes("Good evening markrypto, want to play a game?", 'utf8')
)

self.assertEqual(retval.message, 'Message verified')

def test_ethereum_verify_bytes(self):
self.setup_mnemonic_nopin_nopassphrase()

retval = self.client.ethereum_verify_message(
addr = b'\x3f\x23\x29\xc9\xad\xfb\xcc\xd9\xa8\x4f\x52\xc9\x06\xe9\x36\xa4\x2d\xa1\x8c\xb8',
signature = b'\xfcD\xafp\ntzh\xb1\xb7\x91p\xddF\xfbZ\xad/\xfe*\xee*j\x9e\xf6S\xa2\x93P\x96}\xaf\x1b\xf6/\xfb\x84\xa3R3V\xba\xffW-\x1b\x12\x85\xa1@6!.i\xa2l\xa1\x94\xad\xb5:\x0e"\xa6\x1b',
message = b'\x1d\xf3\xd1\x0b\xe9\x35\xab\xc6\x3b\x65\x61\xcb\x61\x48\xb7\x45'
)

self.assertEqual(retval.message, 'Message verified')

if __name__ == "__main__":
unittest.main()

0 comments on commit 85a7e37

Please sign in to comment.