Skip to content

Commit

Permalink
Merge pull request #119 from LedgerHQ/xch/ragger-use-case
Browse files Browse the repository at this point in the history
Use ragger scenario navigator feature
  • Loading branch information
xchapron-ledger authored Apr 11, 2024
2 parents ca59959 + a8b7d7f commit 4e898a0
Show file tree
Hide file tree
Showing 30 changed files with 27 additions and 117 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Diff not rendered.
6 changes: 2 additions & 4 deletions tests/test_app_mainmenu.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from ragger.navigator import NavInsID, NavIns

from utils import ROOT_SCREENSHOT_PATH


# In this test we check the behavior of the device main menu
def test_app_mainmenu(firmware, navigator, test_name):
def test_app_mainmenu(firmware, navigator, test_name, default_screenshot_path):
# Navigate in the main menu
if firmware.device.startswith("nano"):
instructions = [
Expand All @@ -22,5 +20,5 @@ def test_app_mainmenu(firmware, navigator, test_name):
NavInsID.USE_CASE_SETTINGS_NEXT,
NavInsID.USE_CASE_SETTINGS_MULTI_PAGE_EXIT
]
navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, instructions,
navigator.navigate_and_compare(default_screenshot_path, test_name, instructions,
screen_change_before_first_instruction=False)
63 changes: 11 additions & 52 deletions tests/test_pubkey_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from ragger.bip import calculate_public_key_and_chaincode, CurveChoice
from ragger.error import ExceptionRAPDU
from ragger.navigator import NavInsID, NavIns
from utils import ROOT_SCREENSHOT_PATH


# In this test we check that the GET_PUBLIC_KEY works in non-confirmation mode
Expand All @@ -21,27 +20,12 @@ def test_get_public_key_no_confirm(backend):


# In this test we check that the GET_PUBLIC_KEY works in confirmation mode
def test_get_public_key_confirm_accepted(firmware, backend, navigator, test_name):
def test_get_public_key_confirm_accepted(backend, scenario_navigator):
client = BoilerplateCommandSender(backend)
path = "m/44'/1'/0'/0/0"
with client.get_public_key_with_confirmation(path=path):
if firmware.device.startswith("nano"):
navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK,
[NavInsID.BOTH_CLICK],
"Approve",
ROOT_SCREENSHOT_PATH,
test_name)
else:
instructions = [
NavInsID.USE_CASE_REVIEW_TAP,
NavIns(NavInsID.TOUCH, (200, 335)),
NavInsID.USE_CASE_ADDRESS_CONFIRMATION_EXIT_QR,
NavInsID.USE_CASE_ADDRESS_CONFIRMATION_CONFIRM,
NavInsID.USE_CASE_STATUS_DISMISS
]
navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH,
test_name,
instructions)
scenario_navigator.address_review_approve()

response = client.get_async_response().data
_, public_key, _, chain_code = unpack_get_public_key_response(response)

Expand All @@ -51,39 +35,14 @@ def test_get_public_key_confirm_accepted(firmware, backend, navigator, test_name


# In this test we check that the GET_PUBLIC_KEY in confirmation mode replies an error if the user refuses
def test_get_public_key_confirm_refused(firmware, backend, navigator, test_name):
def test_get_public_key_confirm_refused(backend, scenario_navigator):
client = BoilerplateCommandSender(backend)
path = "m/44'/1'/0'/0/0"

if firmware.device.startswith("nano"):
with pytest.raises(ExceptionRAPDU) as e:
with client.get_public_key_with_confirmation(path=path):
navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK,
[NavInsID.BOTH_CLICK],
"Reject",
ROOT_SCREENSHOT_PATH,
test_name)
# Assert that we have received a refusal
assert e.value.status == Errors.SW_DENY
assert len(e.value.data) == 0
else:
instructions_set = [
[
NavInsID.USE_CASE_REVIEW_REJECT,
NavInsID.USE_CASE_STATUS_DISMISS
],
[
NavInsID.USE_CASE_REVIEW_TAP,
NavInsID.USE_CASE_ADDRESS_CONFIRMATION_CANCEL,
NavInsID.USE_CASE_STATUS_DISMISS
]
]
for i, instructions in enumerate(instructions_set):
with pytest.raises(ExceptionRAPDU) as e:
with client.get_public_key_with_confirmation(path=path):
navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH,
test_name + f"/part{i}",
instructions)
# Assert that we have received a refusal
assert e.value.status == Errors.SW_DENY
assert len(e.value.data) == 0
with pytest.raises(ExceptionRAPDU) as e:
with client.get_public_key_with_confirmation(path=path):
scenario_navigator.address_review_reject()

# Assert that we have received a refusal
assert e.value.status == Errors.SW_DENY
assert len(e.value.data) == 0
71 changes: 14 additions & 57 deletions tests/test_sign_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
from application_client.boilerplate_command_sender import BoilerplateCommandSender, Errors
from application_client.boilerplate_response_unpacker import unpack_get_public_key_response, unpack_sign_tx_response
from ragger.error import ExceptionRAPDU
from ragger.navigator import NavInsID
from utils import ROOT_SCREENSHOT_PATH, check_signature_validity
from utils import check_signature_validity

# In this tests we check the behavior of the device when asked to sign a transaction


# In this test se send to the device a transaction to sign and validate it on screen
# The transaction is short and will be sent in one chunk
# We will ensure that the displayed information is correct by using screenshots comparison
def test_sign_tx_short_tx(firmware, backend, navigator, test_name):
def test_sign_tx_short_tx(backend, scenario_navigator):
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
# The path used for this entire test
Expand All @@ -36,19 +35,7 @@ def test_sign_tx_short_tx(firmware, backend, navigator, test_name):
# It will yield the result when the navigation is done
with client.sign_tx(path=path, transaction=transaction):
# Validate the on-screen request by performing the navigation appropriate for this device
if firmware.device.startswith("nano"):
navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK,
[NavInsID.BOTH_CLICK],
"Approve",
ROOT_SCREENSHOT_PATH,
test_name)
else:
navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP,
[NavInsID.USE_CASE_REVIEW_CONFIRM,
NavInsID.USE_CASE_STATUS_DISMISS],
"Hold to sign",
ROOT_SCREENSHOT_PATH,
test_name)
scenario_navigator.review_approve()

# The device as yielded the result, parse it and ensure that the signature is correct
response = client.get_async_response().data
Expand All @@ -59,7 +46,7 @@ def test_sign_tx_short_tx(firmware, backend, navigator, test_name):
# In this test se send to the device a transaction to sign and validate it on screen
# This test is mostly the same as the previous one but with different values.
# In particular the long memo will force the transaction to be sent in multiple chunks
def test_sign_tx_long_tx(firmware, backend, navigator, test_name):
def test_sign_tx_long_tx(backend, scenario_navigator):
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
path: str = "m/44'/1'/0'/0/0"
Expand All @@ -78,27 +65,16 @@ def test_sign_tx_long_tx(firmware, backend, navigator, test_name):
).serialize()

with client.sign_tx(path=path, transaction=transaction):
if firmware.device.startswith("nano"):
navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK,
[NavInsID.BOTH_CLICK],
"Approve",
ROOT_SCREENSHOT_PATH,
test_name)
else:
navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP,
[NavInsID.USE_CASE_REVIEW_CONFIRM,
NavInsID.USE_CASE_STATUS_DISMISS],
"Hold to sign",
ROOT_SCREENSHOT_PATH,
test_name)
scenario_navigator.review_approve()

response = client.get_async_response().data
_, der_sig, _ = unpack_sign_tx_response(response)
assert check_signature_validity(public_key, der_sig, transaction)


# Transaction signature refused test
# The test will ask for a transaction signature that will be refused on screen
def test_sign_tx_refused(firmware, backend, navigator, test_name):
def test_sign_tx_refused(backend, scenario_navigator):
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
path: str = "m/44'/1'/0'/0/0"
Expand All @@ -113,29 +89,10 @@ def test_sign_tx_refused(firmware, backend, navigator, test_name):
memo="This transaction will be refused by the user"
).serialize()

if firmware.device.startswith("nano"):
with pytest.raises(ExceptionRAPDU) as e:
with client.sign_tx(path=path, transaction=transaction):
navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK,
[NavInsID.BOTH_CLICK],
"Reject",
ROOT_SCREENSHOT_PATH,
test_name)

# Assert that we have received a refusal
assert e.value.status == Errors.SW_DENY
assert len(e.value.data) == 0
else:
for i in range(3):
instructions = [NavInsID.USE_CASE_REVIEW_TAP] * i
instructions += [NavInsID.USE_CASE_REVIEW_REJECT,
NavInsID.USE_CASE_CHOICE_CONFIRM,
NavInsID.USE_CASE_STATUS_DISMISS]
with pytest.raises(ExceptionRAPDU) as e:
with client.sign_tx(path=path, transaction=transaction):
navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH,
test_name + f"/part{i}",
instructions)
# Assert that we have received a refusal
assert e.value.status == Errors.SW_DENY
assert len(e.value.data) == 0
with pytest.raises(ExceptionRAPDU) as e:
with client.sign_tx(path=path, transaction=transaction):
scenario_navigator.review_reject()

# Assert that we have received a refusal
assert e.value.status == Errors.SW_DENY
assert len(e.value.data) == 0
4 changes: 0 additions & 4 deletions tests/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from pathlib import Path
from hashlib import sha256
from sha3 import keccak_256

Expand All @@ -7,9 +6,6 @@
from ecdsa.util import sigdecode_der


ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve()


# Check if a signature of a given message is valid
def check_signature_validity(public_key: bytes, signature: bytes, message: bytes) -> bool:
pk: VerifyingKey = VerifyingKey.from_string(
Expand Down

0 comments on commit 4e898a0

Please sign in to comment.