240 lines
8.5 KiB
Python
240 lines
8.5 KiB
Python
import fnmatch
|
|
import os
|
|
import pytest
|
|
import time
|
|
from configparser import ConfigParser
|
|
from functools import partial
|
|
from pathlib import Path
|
|
from ragger.backend import BackendInterface
|
|
from ragger.firmware import Firmware
|
|
from ragger.navigator import Navigator, NavInsID
|
|
import json
|
|
from typing import Optional
|
|
from constants import ROOT_SNAPSHOT_PATH
|
|
|
|
import ledger_app_clients.ethereum.response_parser as ResponseParser
|
|
from ledger_app_clients.ethereum.client import EthAppClient
|
|
from ledger_app_clients.ethereum.eip712 import InputData
|
|
from ledger_app_clients.ethereum.settings import SettingID, settings_toggle
|
|
|
|
|
|
class SnapshotsConfig:
|
|
test_name: str
|
|
idx: int
|
|
|
|
def __init__(self, test_name: str, idx: int = 0):
|
|
self.test_name = test_name
|
|
self.idx = idx
|
|
|
|
|
|
BIP32_PATH = "m/44'/60'/0'/0/0"
|
|
snaps_config: Optional[SnapshotsConfig] = None
|
|
|
|
|
|
def eip712_json_path() -> str:
|
|
return "%s/eip712_input_files" % (os.path.dirname(__file__))
|
|
|
|
|
|
def input_files() -> list[str]:
|
|
files = []
|
|
for file in os.scandir(eip712_json_path()):
|
|
if fnmatch.fnmatch(file, "*-data.json"):
|
|
files.append(file.path)
|
|
return sorted(files)
|
|
|
|
|
|
@pytest.fixture(params=input_files())
|
|
def input_file(request) -> str:
|
|
return Path(request.param)
|
|
|
|
|
|
@pytest.fixture(params=[True, False])
|
|
def verbose(request) -> bool:
|
|
return request.param
|
|
|
|
|
|
@pytest.fixture(params=[False, True])
|
|
def filtering(request) -> bool:
|
|
return request.param
|
|
|
|
|
|
def test_eip712_legacy(firmware: Firmware,
|
|
backend: BackendInterface,
|
|
navigator: Navigator):
|
|
app_client = EthAppClient(backend)
|
|
with app_client.eip712_sign_legacy(
|
|
BIP32_PATH,
|
|
bytes.fromhex('6137beb405d9ff777172aa879e33edb34a1460e701802746c5ef96e741710e59'),
|
|
bytes.fromhex('eb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8')):
|
|
moves = list()
|
|
if firmware.device.startswith("nano"):
|
|
moves += [NavInsID.RIGHT_CLICK]
|
|
if firmware.device == "nanos":
|
|
screens_per_hash = 4
|
|
else:
|
|
screens_per_hash = 2
|
|
moves += [NavInsID.RIGHT_CLICK] * screens_per_hash * 2
|
|
moves += [NavInsID.BOTH_CLICK]
|
|
else:
|
|
moves += [NavInsID.USE_CASE_REVIEW_TAP] * 2
|
|
moves += [NavInsID.USE_CASE_REVIEW_CONFIRM]
|
|
navigator.navigate(moves)
|
|
|
|
v, r, s = ResponseParser.signature(app_client.response().data)
|
|
|
|
assert v == bytes.fromhex("1c")
|
|
assert r == bytes.fromhex("ea66f747173762715751c889fea8722acac3fc35db2c226d37a2e58815398f64")
|
|
assert s == bytes.fromhex("52d8ba9153de9255da220ffd36762c0b027701a3b5110f0a765f94b16a9dfb55")
|
|
|
|
|
|
def autonext(fw: Firmware, nav: Navigator):
|
|
moves = list()
|
|
if fw.device.startswith("nano"):
|
|
moves = [NavInsID.RIGHT_CLICK]
|
|
else:
|
|
moves = [NavInsID.USE_CASE_REVIEW_TAP]
|
|
if snaps_config is not None:
|
|
nav.navigate_and_compare(ROOT_SNAPSHOT_PATH,
|
|
snaps_config.test_name,
|
|
moves,
|
|
screen_change_before_first_instruction=False,
|
|
screen_change_after_last_instruction=False,
|
|
snap_start_idx=snaps_config.idx)
|
|
snaps_config.idx += 1
|
|
else:
|
|
nav.navigate(moves,
|
|
screen_change_before_first_instruction=False,
|
|
screen_change_after_last_instruction=False)
|
|
|
|
|
|
def eip712_new_common(fw: Firmware,
|
|
nav: Navigator,
|
|
app_client: EthAppClient,
|
|
json_data: dict,
|
|
filters: Optional[dict],
|
|
verbose: bool):
|
|
assert InputData.process_data(app_client,
|
|
json_data,
|
|
filters,
|
|
partial(autonext, fw, nav))
|
|
with app_client.eip712_sign_new(BIP32_PATH):
|
|
moves = list()
|
|
if fw.device.startswith("nano"):
|
|
# need to skip the message hash
|
|
if not verbose and filters is None:
|
|
moves = [NavInsID.RIGHT_CLICK] * 2
|
|
moves += [NavInsID.BOTH_CLICK]
|
|
else:
|
|
time.sleep(1.5)
|
|
# need to skip the message hash
|
|
if not verbose and filters is None:
|
|
moves += [NavInsID.USE_CASE_REVIEW_TAP]
|
|
moves += [NavInsID.USE_CASE_REVIEW_CONFIRM]
|
|
if snaps_config is not None:
|
|
nav.navigate_and_compare(ROOT_SNAPSHOT_PATH,
|
|
snaps_config.test_name,
|
|
moves,
|
|
snap_start_idx=snaps_config.idx)
|
|
snaps_config.idx += 1
|
|
else:
|
|
nav.navigate(moves)
|
|
return ResponseParser.signature(app_client.response().data)
|
|
|
|
|
|
def test_eip712_new(firmware: Firmware,
|
|
backend: BackendInterface,
|
|
navigator: Navigator,
|
|
input_file: Path,
|
|
verbose: bool,
|
|
filtering: bool):
|
|
app_client = EthAppClient(backend)
|
|
if firmware.device == "nanos":
|
|
pytest.skip("Not supported on LNS")
|
|
else:
|
|
test_path = "%s/%s" % (input_file.parent, "-".join(input_file.stem.split("-")[:-1]))
|
|
conf_file = "%s.ini" % (test_path)
|
|
|
|
filters = None
|
|
if filtering:
|
|
try:
|
|
with open("%s-filter.json" % (test_path)) as f:
|
|
filters = json.load(f)
|
|
except (IOError, json.decoder.JSONDecodeError) as e:
|
|
pytest.skip("Filter file error: %s" % (e.strerror))
|
|
|
|
config = ConfigParser()
|
|
config.read(conf_file)
|
|
|
|
# sanity check
|
|
assert "signature" in config.sections()
|
|
assert "v" in config["signature"]
|
|
assert "r" in config["signature"]
|
|
assert "s" in config["signature"]
|
|
|
|
if verbose:
|
|
settings_toggle(firmware, navigator, [SettingID.VERBOSE_EIP712])
|
|
|
|
with open(input_file) as file:
|
|
v, r, s = eip712_new_common(firmware,
|
|
navigator,
|
|
app_client,
|
|
json.load(file),
|
|
filters,
|
|
verbose)
|
|
|
|
assert v == bytes.fromhex(config["signature"]["v"])
|
|
assert r == bytes.fromhex(config["signature"]["r"])
|
|
assert s == bytes.fromhex(config["signature"]["s"])
|
|
|
|
|
|
def test_eip712_address_substitution(firmware: Firmware,
|
|
backend: BackendInterface,
|
|
navigator: Navigator,
|
|
verbose: bool):
|
|
global snaps_config
|
|
|
|
app_client = EthAppClient(backend)
|
|
if firmware.device == "nanos":
|
|
pytest.skip("Not supported on LNS")
|
|
else:
|
|
test_name = "eip712_address_substitution"
|
|
if verbose:
|
|
test_name += "_verbose"
|
|
snaps_config = SnapshotsConfig(test_name)
|
|
with open("%s/address_substitution.json" % (eip712_json_path())) as file:
|
|
data = json.load(file)
|
|
|
|
app_client.provide_token_metadata("DAI",
|
|
bytes.fromhex(data["message"]["token"][2:]),
|
|
18,
|
|
1)
|
|
|
|
challenge = ResponseParser.challenge(app_client.get_challenge().data)
|
|
app_client.provide_domain_name(challenge,
|
|
"vitalik.eth",
|
|
bytes.fromhex(data["message"]["to"][2:]))
|
|
|
|
if verbose:
|
|
settings_toggle(firmware, navigator, [SettingID.VERBOSE_EIP712])
|
|
filters = None
|
|
else:
|
|
filters = {
|
|
"name": "Token test",
|
|
"fields": {
|
|
"amount": "Amount",
|
|
"token": "Token",
|
|
"to": "To",
|
|
}
|
|
}
|
|
|
|
v, r, s = eip712_new_common(firmware,
|
|
navigator,
|
|
app_client,
|
|
data,
|
|
filters,
|
|
verbose)
|
|
|
|
assert v == bytes.fromhex("1b")
|
|
assert r == bytes.fromhex("d4a0e058251cdc3845aaa5eb8409d8a189ac668db7c55a64eb3121b0db7fd8c0")
|
|
assert s == bytes.fromhex("3221800e4f45272c6fa8fafda5e94c848d1a4b90c442aa62afa8e8d6a9af0f00")
|