145 lines
5.4 KiB
Python
145 lines
5.4 KiB
Python
from typing import Optional, Tuple
|
|
import pytest
|
|
|
|
from ledger_app_clients.ethereum.client import EthAppClient, StatusWord
|
|
import ledger_app_clients.ethereum.response_parser as ResponseParser
|
|
|
|
from ragger.error import ExceptionRAPDU
|
|
from ragger.firmware import Firmware
|
|
from ragger.backend import BackendInterface
|
|
from ragger.navigator import Navigator, NavInsID
|
|
from ragger.bip import calculate_public_key_and_chaincode, CurveChoice
|
|
|
|
from constants import ROOT_SNAPSHOT_PATH
|
|
|
|
|
|
@pytest.fixture(name="with_chaincode", params=[True, False])
|
|
def with_chaincode_fixture(request) -> bool:
|
|
return request.param
|
|
|
|
|
|
@pytest.fixture(name="chain", params=[None, 1, 2, 5, 137])
|
|
def chain_fixture(request) -> Optional[int]:
|
|
return request.param
|
|
|
|
|
|
def get_moves(firmware: Firmware,
|
|
chain: Optional[int] = None,
|
|
reject: bool = False):
|
|
moves = []
|
|
|
|
if firmware.is_nano:
|
|
moves += [NavInsID.RIGHT_CLICK]
|
|
if firmware.device == "nanos":
|
|
moves += [NavInsID.RIGHT_CLICK] * 3
|
|
else:
|
|
moves += [NavInsID.RIGHT_CLICK]
|
|
if reject:
|
|
moves += [NavInsID.RIGHT_CLICK]
|
|
moves += [NavInsID.BOTH_CLICK]
|
|
else:
|
|
moves += [NavInsID.USE_CASE_REVIEW_TAP]
|
|
if chain is not None and chain > 1:
|
|
moves += [NavInsID.USE_CASE_ADDRESS_CONFIRMATION_TAP]
|
|
if reject:
|
|
moves += [NavInsID.USE_CASE_ADDRESS_CONFIRMATION_CANCEL]
|
|
else:
|
|
moves += [NavInsID.USE_CASE_ADDRESS_CONFIRMATION_CONFIRM]
|
|
|
|
return moves
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"path, suffix",
|
|
[
|
|
("m/44'/60'/0'/0/0", "60"),
|
|
("m/44'/700'/1'/0/0", "700")
|
|
],
|
|
)
|
|
def test_get_pk_rejected(firmware: Firmware,
|
|
backend: BackendInterface,
|
|
navigator: Navigator,
|
|
path,
|
|
suffix):
|
|
app_client = EthAppClient(backend)
|
|
|
|
with pytest.raises(ExceptionRAPDU) as e:
|
|
with app_client.get_public_addr(bip32_path=path):
|
|
navigator.navigate_and_compare(ROOT_SNAPSHOT_PATH,
|
|
f"get_pk_rejected_{suffix}",
|
|
get_moves(firmware, reject=True))
|
|
assert e.value.status == StatusWord.CONDITION_NOT_SATISFIED
|
|
|
|
|
|
def test_get_pk(firmware: Firmware,
|
|
backend: BackendInterface,
|
|
navigator: Navigator,
|
|
with_chaincode: bool,
|
|
chain: Optional[int]):
|
|
app_client = EthAppClient(backend)
|
|
|
|
with app_client.get_public_addr(chaincode=with_chaincode, chain_id=chain):
|
|
navigator.navigate_and_compare(ROOT_SNAPSHOT_PATH,
|
|
f"get_pk_{chain}",
|
|
get_moves(firmware, chain=chain))
|
|
pk, _, chaincode = ResponseParser.pk_addr(app_client.response().data, with_chaincode)
|
|
ref_pk, ref_chaincode = calculate_public_key_and_chaincode(curve=CurveChoice.Secp256k1,
|
|
path="m/44'/60'/0'/0/0")
|
|
assert pk.hex() == ref_pk
|
|
if with_chaincode:
|
|
assert chaincode.hex() == ref_chaincode
|
|
|
|
|
|
def test_get_pk2(firmware: Firmware,
|
|
backend: BackendInterface,
|
|
navigator: Navigator):
|
|
app_client = EthAppClient(backend)
|
|
|
|
path="m/44'/700'/1'/0/0"
|
|
with app_client.get_public_addr(bip32_path=path, chaincode=True):
|
|
navigator.navigate_and_compare(ROOT_SNAPSHOT_PATH,
|
|
"get_pk_700",
|
|
get_moves(firmware))
|
|
pk, _, chaincode = ResponseParser.pk_addr(app_client.response().data, True)
|
|
ref_pk, ref_chaincode = calculate_public_key_and_chaincode(curve=CurveChoice.Secp256k1,
|
|
path=path)
|
|
assert pk.hex() == ref_pk
|
|
assert chaincode.hex() == ref_chaincode
|
|
|
|
|
|
def test_get_public_key(backend: BackendInterface):
|
|
app_client = EthAppClient(backend)
|
|
|
|
with app_client.get_public_addr(bip32_path="m/44'/60'/1'/0/0", display=False, chaincode=True):
|
|
pass
|
|
|
|
response = app_client.response()
|
|
assert response.status == 0x9000
|
|
|
|
# response = pub_key_len (1)
|
|
# pub_key (var)
|
|
# eth_addr_len (1)
|
|
# eth_addr (var)
|
|
# chain_code (var)
|
|
buffer, pub_key_len, pub_key = _pop_size_prefixed_buf_from_buf(response.data)
|
|
buffer, eth_addr_len, eth_addr = _pop_size_prefixed_buf_from_buf(buffer)
|
|
buffer, chain_code = _pop_sized_buf_from_buffer(buffer, 32)
|
|
|
|
assert len(response.data) == 2 + pub_key_len + eth_addr_len + 32
|
|
assert pub_key == b'\x04\xea\x02&\x91\xc7\x87\x00\xd2\xc3\xa0\xc7E\xbe\xa4\xf2\xb8\xe5\xe3\x13\x97j\x10B\xf6\xa1Vc\\\xb2\x05\xda\x1a\xcb\xfe\x04*\nZ\x89eyn6"E\x89\x0eT\xbd-\xbex\xec\x1e\x18df\xf2\xe9\xd0\xf5\xd5\xd8\xdf'
|
|
assert eth_addr == b'463e4e114AA57F54f2Fd2C3ec03572C6f75d84C2'
|
|
assert chain_code == b'\xaf\x89\xcd)\xea${8I\xec\xc80\xc2\xc8\x94\\e1\xd6P\x87\x07?\x9f\xd09\x00\xa0\xea\xa7\x96\xc8'
|
|
|
|
|
|
def _pop_sized_buf_from_buffer(buffer: bytes, size: int) -> Tuple[bytes, bytes]:
|
|
"""Parse buffer and returns: remainder, data[size]"""
|
|
|
|
return buffer[size:], buffer[0:size]
|
|
|
|
|
|
def _pop_size_prefixed_buf_from_buf(buffer:bytes) -> Tuple[bytes, int, bytes]:
|
|
""" Parse buffer and returns: remainder, data_len, data """
|
|
|
|
data_len = buffer[0]
|
|
return buffer[1+data_len:], data_len, buffer[1:data_len+1]
|