Now properly handles UI clicks in EIP712 automated tests

This commit is contained in:
Alexandre Paillier
2022-08-02 16:31:11 +02:00
parent 341b7c2adf
commit ca04b5e213
3 changed files with 100 additions and 41 deletions

View File

@@ -6,8 +6,8 @@ from ethereum_client import EthereumClient
ELFS_DIR = (Path(__file__).parent.parent / "elfs").resolve()
FWS = [
Firmware("nanox", "2.0.2"),
Firmware("nanos", "2.1"),
Firmware("nanox", "2.0.2"),
Firmware("nanosp", "1.0.3")
]
@@ -54,4 +54,4 @@ def backend_client(backend_name: str, firmware: Firmware) -> BackendInterface:
# This final fixture will return the properly configured app client, to be used in tests
@pytest.fixture
def app_client(backend_client: BackendInterface) -> EthereumClient:
yield EthereumClient(backend_client)
return EthereumClient(backend_client)

View File

@@ -1,7 +1,10 @@
from contextlib import contextmanager
from typing import Generator
from enum import IntEnum, auto
from typing import Iterator
from ragger.backend import BackendInterface
from ragger.utils import RAPDU
import signal
class InsType(IntEnum):
EIP712_SEND_STRUCT_DEF = 0x1a,
@@ -164,23 +167,38 @@ class EthereumResponseParser:
return v, r, s
class EthereumClient:
_verbose_eip712 = False
def __init__(self, client: BackendInterface, debug: bool = False):
self._client = client
self._debug = debug
self._cmd_builder = EthereumClientCmdBuilder()
self._resp_parser = EthereumResponseParser()
self._click_delay = 1/4
signal.signal(signal.SIGALRM, self._click_signal_timeout)
def _send(self, payload: bytearray) -> None:
self._client.send_raw(payload)
def _send(self, payload: bytearray):
return self._client.exchange_async_raw(payload)
def _recv(self) -> RAPDU:
return self._client.receive()
return self._client._last_async_response
def _click_signal_timeout(self, signum: int, frame):
self._client.right_click()
def _enable_click_until_response(self):
signal.setitimer(signal.ITIMER_REAL,
self._click_delay,
self._click_delay)
def _disable_click_until_response(self):
signal.setitimer(signal.ITIMER_REAL, 0, 0)
def eip712_send_struct_def_struct_name(self, name: str):
self._send(self._cmd_builder.eip712_send_struct_def_struct_name(name))
return self._recv()
with self._send(self._cmd_builder.eip712_send_struct_def_struct_name(name)):
pass
return self._recv().status == 0x9000
def eip712_send_struct_def_struct_field(self,
field_type: EIP712FieldType,
@@ -188,52 +206,77 @@ class EthereumClient:
type_size: int,
array_levels: [],
key_name: str):
self._send(self._cmd_builder.eip712_send_struct_def_struct_field(
with self._send(self._cmd_builder.eip712_send_struct_def_struct_field(
field_type,
type_name,
type_size,
array_levels,
key_name))
key_name)):
pass
return self._recv()
def eip712_send_struct_impl_root_struct(self, name: str):
self._send(self._cmd_builder.eip712_send_struct_impl_root_struct(name))
with self._send(self._cmd_builder.eip712_send_struct_impl_root_struct(name)):
self._enable_click_until_response()
self._disable_click_until_response()
return self._recv()
def eip712_send_struct_impl_array(self, size: int):
self._send(self._cmd_builder.eip712_send_struct_impl_array(size))
with self._send(self._cmd_builder.eip712_send_struct_impl_array(size)):
pass
return self._recv()
def eip712_send_struct_impl_struct_field(self, raw_value: bytes):
ret = None
for apdu in self._cmd_builder.eip712_send_struct_impl_struct_field(raw_value):
self._send(apdu)
# TODO: Do clicks
ret = self._recv()
return ret
with self._send(apdu):
self._enable_click_until_response()
self._disable_click_until_response()
assert self._recv().status == 0x9000
def eip712_sign_new(self, bip32):
self._send(self._cmd_builder.eip712_sign_new(bip32))
return self._recv()
with self._send(self._cmd_builder.eip712_sign_new(bip32)):
if not self._verbose_eip712: # need to skip the message hash
self._client.right_click()
self._client.right_click()
self._client.both_click() # approve signature
resp = self._recv()
assert resp.status == 0x9000
return self._resp_parser.sign(resp.data)
def eip712_sign_legacy(self,
bip32,
domain_hash: bytes,
message_hash: bytes):
self._send(self._cmd_builder.eip712_sign_legacy(bip32,
domain_hash,
message_hash))
self._client.right_click() # sign typed message screen
for _ in range(2): # two hashes (domain + message)
if self._client.firmware.device == "nanos":
screens_per_hash = 4
else:
screens_per_hash = 2
for _ in range(screens_per_hash):
self._client.right_click()
self._client.both_click() # approve signature
with self._send(self._cmd_builder.eip712_sign_legacy(bip32,
domain_hash,
message_hash)):
self._client.right_click() # sign typed message screen
for _ in range(2): # two hashes (domain + message)
if self._client.firmware.device == "nanos":
screens_per_hash = 4
else:
screens_per_hash = 2
for _ in range(screens_per_hash):
self._client.right_click()
self._client.both_click() # approve signature
resp = self._recv()
assert resp.status == 0x9000
return self._resp_parser.sign(resp.data)
def setting_toggle_verbose_eip712(self):
# Go to settings
self._client.right_click()
self._client.right_click()
self._client.both_click()
# Go to verbose eip712
self._client.right_click()
self._client.right_click()
self._client.right_click()
self._client.both_click()
# Go back
self._client.right_click()
self._client.both_click()
self._verbose_eip712 = not self._verbose_eip712

View File

@@ -1,5 +1,7 @@
import pytest
import os
import fnmatch
from typing import List
from ethereum_client import EthereumClient
from eip712 import InputData
@@ -12,6 +14,22 @@ bip32 = [
]
def input_files() -> List[str]:
files = []
for file in os.scandir("./eip712/input_files"):
if fnmatch.fnmatch(file, "*-test.json"):
files.append(file.path)
return sorted(files)
@pytest.fixture(params=input_files())
def input_file(request) -> str:
return request.param
@pytest.fixture(params=[True, False])
def verbose(request) -> bool:
return request.param
def test_eip712_legacy(app_client: EthereumClient):
v, r, s = app_client.eip712_sign_legacy(
bip32,
@@ -24,14 +42,12 @@ def test_eip712_legacy(app_client: EthereumClient):
assert s == bytes.fromhex("52d8ba9153de9255da220ffd36762c0b027701a3b5110f0a765f94b16a9dfb55")
def test_eip712_new(app_client: EthereumClient):
if app_client._client.firmware.device == "nanos": # not supported
return
def test_eip712_new(app_client: EthereumClient, input_file, verbose):
if app_client._client.firmware.device != "nanos": # not supported
print("=====> %s" % (input_file))
# Loop through JSON files
for file in os.scandir("./eip712/input_files"):
if fnmatch.fnmatch(file, "*-test.json"):
print(file.path)
InputData.process_file(app_client, file.path, False)
app_client.eip712_sign_new(bip32)
assert 1 == 1
if verbose:
app_client.setting_toggle_verbose_eip712()
InputData.process_file(app_client, input_file, False)
v, r, s = app_client.eip712_sign_new(bip32)
assert 1 == 1 # TODO: Replace by the actual v,r,s asserts