feat: eip712 tests
@@ -7,7 +7,7 @@ from speculos.client import SpeculosClient, ApduException
|
||||
|
||||
from ethereum_client.ethereum_cmd_builder import EthereumCommandBuilder, InsType
|
||||
from ethereum_client.exception import DeviceException
|
||||
from ethereum_client.transaction import PersonalTransaction, Transaction
|
||||
from ethereum_client.transaction import EIP712, PersonalTransaction, Transaction
|
||||
from ethereum_client.plugin import ERC20_Information, Plugin
|
||||
from ethereum_client.utils import parse_sign_response
|
||||
|
||||
@@ -173,6 +173,29 @@ class EthereumCommand:
|
||||
result.append(v)
|
||||
result.append(r)
|
||||
result.append(s)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def sign_eip712(self, bip32_path: str, transaction: EIP712, result: List = list()) -> None:
|
||||
try:
|
||||
chunk: bytes = self.builder.sign_eip712(bip32_path=bip32_path, transaction=transaction)
|
||||
|
||||
with self.client.apdu_exchange_nowait(cla=chunk[0], ins=chunk[1],
|
||||
p1=chunk[2], p2=chunk[3],
|
||||
data=chunk[5:]) as exchange:
|
||||
yield exchange
|
||||
response: bytes = exchange.receive()
|
||||
|
||||
except ApduException as error:
|
||||
raise DeviceException(error_code=error.sw, ins=InsType.INS_SIGN_EIP712)
|
||||
|
||||
# response = V (1) || R (32) || S (32)
|
||||
assert len(response) == 65
|
||||
v, r, s = parse_sign_response(response)
|
||||
|
||||
result.append(v)
|
||||
result.append(r)
|
||||
result.append(s)
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
||||
@@ -3,7 +3,7 @@ import logging
|
||||
import struct
|
||||
from typing import List, Tuple, Union, Iterator, cast
|
||||
|
||||
from ethereum_client.transaction import PersonalTransaction, Transaction
|
||||
from ethereum_client.transaction import EIP712, PersonalTransaction, Transaction
|
||||
from ethereum_client.plugin import ERC20_Information, Plugin
|
||||
from ethereum_client.utils import bip32_path_from_string
|
||||
|
||||
@@ -320,6 +320,41 @@ class EthereumCommandBuilder:
|
||||
p2=0x00,
|
||||
cdata=cdata)
|
||||
|
||||
def sign_eip712(self, bip32_path: str, transaction: EIP712) -> bytes:
|
||||
"""Command builder for INS_SIGN_EIP712.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bip32_path : str
|
||||
String representation of BIP32 path.
|
||||
transaction : EIP712
|
||||
Domain hash -> 32 bytes
|
||||
Message hash -> 32 bytes
|
||||
|
||||
Yields
|
||||
-------
|
||||
bytes
|
||||
APDU command chunk for INS_SIGN_EIP712.
|
||||
|
||||
"""
|
||||
bip32_paths: List[bytes] = bip32_path_from_string(bip32_path)
|
||||
|
||||
cdata: bytes = b"".join([
|
||||
len(bip32_paths).to_bytes(1, byteorder="big"),
|
||||
*bip32_paths
|
||||
])
|
||||
|
||||
|
||||
tx: bytes = transaction.serialize()
|
||||
|
||||
cdata = cdata + tx
|
||||
|
||||
return self.serialize(cla=self.CLA,
|
||||
ins=InsType.INS_SIGN_EIP712,
|
||||
p1=0x00,
|
||||
p2=0x00,
|
||||
cdata=cdata)
|
||||
|
||||
|
||||
def simple_personal_sign_tx(self, bip32_path: str, transaction: PersonalTransaction) -> bytes:
|
||||
"""Command builder for INS_SIGN_PERSONAL_TX.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Union
|
||||
|
||||
from ethereum_client.utils import write_varint, UINT64_MAX
|
||||
from ethereum_client.utils import apdu_as_string, write_varint, UINT64_MAX
|
||||
|
||||
|
||||
class TransactionError(Exception):
|
||||
@@ -83,3 +83,14 @@ class Transaction:
|
||||
self.chainID,
|
||||
|
||||
])
|
||||
|
||||
class EIP712:
|
||||
def __init__(self, domain_hash: str, msg_hash: str) -> None:
|
||||
self.domain_hash = apdu_as_string(domain_hash)
|
||||
self.msg_hash = apdu_as_string(msg_hash)
|
||||
|
||||
def serialize(self) -> bytes:
|
||||
return b"".join([
|
||||
self.domain_hash,
|
||||
self.msg_hash
|
||||
])
|
||||
|
After Width: | Height: | Size: 414 B |
|
After Width: | Height: | Size: 503 B |
|
After Width: | Height: | Size: 497 B |
|
After Width: | Height: | Size: 485 B |
|
After Width: | Height: | Size: 486 B |
|
After Width: | Height: | Size: 540 B |
|
After Width: | Height: | Size: 520 B |
|
After Width: | Height: | Size: 517 B |
|
After Width: | Height: | Size: 501 B |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 414 B |
|
After Width: | Height: | Size: 503 B |
|
After Width: | Height: | Size: 497 B |
|
After Width: | Height: | Size: 485 B |
|
After Width: | Height: | Size: 486 B |
|
After Width: | Height: | Size: 540 B |
|
After Width: | Height: | Size: 520 B |
|
After Width: | Height: | Size: 517 B |
|
After Width: | Height: | Size: 501 B |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 449 B |
|
After Width: | Height: | Size: 473 B |
|
After Width: | Height: | Size: 890 B |
|
After Width: | Height: | Size: 551 B |
|
After Width: | Height: | Size: 919 B |
|
After Width: | Height: | Size: 562 B |
|
After Width: | Height: | Size: 463 B |
|
After Width: | Height: | Size: 473 B |
|
After Width: | Height: | Size: 890 B |
|
After Width: | Height: | Size: 551 B |
|
After Width: | Height: | Size: 919 B |
|
After Width: | Height: | Size: 562 B |
|
After Width: | Height: | Size: 463 B |
|
After Width: | Height: | Size: 499 B |
|
After Width: | Height: | Size: 499 B |
179
tests/speculos/test_eip712.py
Normal file
@@ -0,0 +1,179 @@
|
||||
from time import sleep
|
||||
|
||||
from ethereum_client.utils import compare_screenshot, save_screenshot, PATH_IMG, parse_sign_response
|
||||
from ethereum_client.transaction import EIP712
|
||||
import ethereum_client
|
||||
|
||||
def test_sign_eip_712_hashed_msg(cmd):
|
||||
result: list = []
|
||||
|
||||
bip32_path="44'/60'/0'/0'/0"
|
||||
transaction = EIP712(
|
||||
domain_hash="c24f499b8c957196651b13edd64aaccc3980009674b2aea0966c8a56ba81278e",
|
||||
msg_hash="9d96be8a7cca396e711a3ba356bd9878df02a726d753ddb6cda3c507d888bc77"
|
||||
)
|
||||
|
||||
with cmd.sign_eip712(bip32_path=bip32_path, transaction=transaction, result=result) as ex:
|
||||
sleep(0.5)
|
||||
|
||||
if cmd.model == "nanos":
|
||||
# Sign typed message
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00000.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Domain hash 1/4, 2/4, 3/4, 4/4
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00001.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00002.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00003.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00004.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Message hash 1/4, 2/4, 3/4, 4/4
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00005.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00006.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00007.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00008.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Sign message
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00009.png")
|
||||
cmd.client.press_and_release('both')
|
||||
|
||||
if cmd.model == "nanox" or cmd.model == "nanosp":
|
||||
# Sign typed message
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00000.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Domain hash 1/2, 2/2
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00001.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00002.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Message hash 1/2, 2/2
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00003.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00004.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Sign message
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg/00005.png")
|
||||
cmd.client.press_and_release('both')
|
||||
|
||||
v, r, s = result
|
||||
|
||||
assert v == 0x1B #27
|
||||
assert r.hex() == "b1cf3dd6f2902ae9b181e158cc07f6ee6e6c456360b18842ece0b947dec89f07"
|
||||
assert s.hex() == "5372a9b1a495b76ccd75347b6f591867859fb73aa05a546b79c81073ddff5e8a"
|
||||
|
||||
def test_sign_eip_712_hashed_msg_reject(cmd):
|
||||
result: list = []
|
||||
|
||||
bip32_path="44'/60'/0'/0'/0"
|
||||
transaction = EIP712(
|
||||
domain_hash="c24f499b8c957196651b13edd64aaccc3980009674b2aea0966c8a56ba81278e",
|
||||
msg_hash="9d96be8a7cca396e711a3ba356bd9878df02a726d753ddb6cda3c507d888bc77"
|
||||
)
|
||||
|
||||
try:
|
||||
with cmd.sign_eip712(bip32_path=bip32_path, transaction=transaction, result=result) as ex:
|
||||
sleep(0.5)
|
||||
|
||||
if cmd.model == "nanos":
|
||||
# Sign typed message
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00000.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Domain hash 1/4, 2/4, 3/4, 4/4
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00001.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00002.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00003.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00004.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Message hash 1/4, 2/4, 3/4, 4/4
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00005.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00006.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00007.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00008.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Sign message
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00009.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Cancel signature
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00010.png")
|
||||
cmd.client.press_and_release('both')
|
||||
|
||||
if cmd.model == "nanox" or cmd.model == "nanosp":
|
||||
# Sign typed message
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00000.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Domain hash 1/2, 2/2
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00001.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00002.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Message hash 1/2, 2/2
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00003.png")
|
||||
cmd.client.press_and_release('right')
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00004.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Sign message
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00005.png")
|
||||
cmd.client.press_and_release('right')
|
||||
|
||||
# Cancel signature
|
||||
compare_screenshot(cmd, f"screenshots/eip712/{PATH_IMG[cmd.model]}/sign_eip_712_hashed_msg_reject/00006.png")
|
||||
cmd.client.press_and_release('both')
|
||||
|
||||
except ethereum_client.exception.errors.DenyError as error:
|
||||
assert error.args[0] == '0x6985'
|
||||
|
||||
def test_sign_eip_712_bad_domain(cmd):
|
||||
result: list = []
|
||||
|
||||
bip32_path="44'/60'/0'/0'/0"
|
||||
transaction = EIP712(
|
||||
domain_hash="deadbeef",
|
||||
msg_hash="9d96be8a7cca396e711a3ba356bd9878df02a726d753ddb6cda3c507d888bc77"
|
||||
)
|
||||
|
||||
try:
|
||||
with cmd.sign_eip712(bip32_path=bip32_path, transaction=transaction, result=result) as ex:
|
||||
pass
|
||||
|
||||
except ethereum_client.exception.errors.UnknownDeviceError as error:
|
||||
assert error.args[0] == '0x6a80'
|
||||
|
||||
def test_sign_eip_712_bad_msg(cmd):
|
||||
result: list = []
|
||||
|
||||
bip32_path="44'/60'/0'/0'/0"
|
||||
transaction = EIP712(
|
||||
domain_hash="c24f499b8c957196651b13edd64aaccc3980009674b2aea0966c8a56ba81278e",
|
||||
msg_hash="deadbeef"
|
||||
)
|
||||
|
||||
try:
|
||||
with cmd.sign_eip712(bip32_path=bip32_path, transaction=transaction, result=result) as ex:
|
||||
pass
|
||||
|
||||
except ethereum_client.exception.errors.UnknownDeviceError as error:
|
||||
assert error.args[0] == '0x6a80'
|
||||