Skip to content

Commit

Permalink
Adapt ragger tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cedelavergne-ledger committed Jan 23, 2025
1 parent 2f76784 commit 7b7d59d
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 37 deletions.
1 change: 0 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from ragger.conftest import configuration

###########################
### CONFIGURATION START ###
Expand Down
2 changes: 1 addition & 1 deletion tests/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ disable = C0114, # missing-module-docstring
R0913, # too-many-arguments
R0917 # too-many-positional-arguments

max-line-length=100
max-line-length=120
extension-pkg-whitelist=hid

[pycodestyle]
Expand Down
31 changes: 25 additions & 6 deletions tests/test_app_mainmenu.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
from ragger.firmware import Firmware
from ragger.navigator import NavInsID, NavIns
from ragger.navigator import Navigator, NavInsID, NavIns


# In this test we check the behavior of the device main menu
def test_app_mainmenu(firmware, navigator, test_name, default_screenshot_path):
def test_app_mainmenu(firmware: Firmware,
navigator: Navigator,
test_name: str,
default_screenshot_path: str) -> None:
# Navigate in the main menu
instructions = []
if firmware.is_nano:
instructions = [
instructions += [
NavInsID.RIGHT_CLICK,
NavInsID.BOTH_CLICK,
NavInsID.BOTH_CLICK,
NavInsID.RIGHT_CLICK,
NavInsID.BOTH_CLICK,
NavInsID.RIGHT_CLICK,
NavInsID.RIGHT_CLICK,
NavInsID.BOTH_CLICK,
NavInsID.RIGHT_CLICK,
NavInsID.BOTH_CLICK,
NavInsID.RIGHT_CLICK,
NavInsID.BOTH_CLICK,
NavInsID.RIGHT_CLICK,
NavInsID.RIGHT_CLICK,
NavInsID.BOTH_CLICK,
NavInsID.RIGHT_CLICK,
NavInsID.RIGHT_CLICK
]
elif firmware is Firmware.STAX:
instructions = [
instructions += [
NavInsID.USE_CASE_HOME_SETTINGS,
NavIns(NavInsID.TOUCH, (200, 113)),
NavIns(NavInsID.TOUCH, (200, 261)),
Expand All @@ -22,7 +39,7 @@ def test_app_mainmenu(firmware, navigator, test_name, default_screenshot_path):
NavInsID.USE_CASE_SETTINGS_MULTI_PAGE_EXIT
]
elif firmware is Firmware.FLEX:
instructions = [
instructions += [
NavInsID.USE_CASE_HOME_SETTINGS,
NavIns(NavInsID.TOUCH, (200, 113)),
NavIns(NavInsID.TOUCH, (200, 300)),
Expand All @@ -31,5 +48,7 @@ def test_app_mainmenu(firmware, navigator, test_name, default_screenshot_path):
NavInsID.USE_CASE_SETTINGS_NEXT,
NavInsID.USE_CASE_SETTINGS_MULTI_PAGE_EXIT
]

assert len(instructions) > 0
navigator.navigate_and_compare(default_screenshot_path, test_name, instructions,
screen_change_before_first_instruction=False)
4 changes: 3 additions & 1 deletion tests/test_appname_cmd.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from ragger.backend.interface import BackendInterface

from application_client.boilerplate_command_sender import BoilerplateCommandSender
from application_client.boilerplate_response_unpacker import unpack_get_app_name_response

from utils import verify_name


# In this test we check that the GET_APP_NAME replies the application name
def test_app_name(backend):
def test_app_name(backend: BackendInterface) -> None:
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
# Send the GET_APP_NAME instruction to the app
Expand Down
12 changes: 7 additions & 5 deletions tests/test_error_cmd.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
import pytest

from ragger.error import ExceptionRAPDU
from ragger.backend.interface import BackendInterface

from application_client.boilerplate_command_sender import CLA, InsType, P1, P2, Errors


# Ensure the app returns an error when a bad CLA is used
def test_bad_cla(backend):
def test_bad_cla(backend: BackendInterface) -> None:
with pytest.raises(ExceptionRAPDU) as e:
backend.exchange(cla=CLA + 1, ins=InsType.GET_VERSION)
assert e.value.status == Errors.SW_CLA_NOT_SUPPORTED


# Ensure the app returns an error when a bad INS is used
def test_bad_ins(backend):
def test_bad_ins(backend: BackendInterface) -> None:
with pytest.raises(ExceptionRAPDU) as e:
backend.exchange(cla=CLA, ins=0xff)
assert e.value.status == Errors.SW_INS_NOT_SUPPORTED


# Ensure the app returns an error when a bad P1 or P2 is used
def test_wrong_p1p2(backend):
def test_wrong_p1p2(backend: BackendInterface) -> None:
with pytest.raises(ExceptionRAPDU) as e:
backend.exchange(cla=CLA, ins=InsType.GET_VERSION, p1=P1.P1_START + 1, p2=P2.P2_LAST)
assert e.value.status == Errors.SW_WRONG_P1P2
Expand All @@ -35,7 +37,7 @@ def test_wrong_p1p2(backend):


# Ensure the app returns an error when a bad data length is used
def test_wrong_data_length(backend):
def test_wrong_data_length(backend: BackendInterface) -> None:
# APDUs must be at least 4 bytes: CLA, INS, P1, P2.
with pytest.raises(ExceptionRAPDU) as e:
backend.exchange_raw(bytes.fromhex("E00300"))
Expand All @@ -47,7 +49,7 @@ def test_wrong_data_length(backend):


# Ensure there is no state confusion when trying wrong APDU sequences
def test_invalid_state(backend):
def test_invalid_state(backend: BackendInterface) -> None:
with pytest.raises(ExceptionRAPDU) as e:
backend.exchange(cla=CLA,
ins=InsType.SIGN_TX,
Expand Down
4 changes: 3 additions & 1 deletion tests/test_name_version.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from ragger.backend.interface import BackendInterface

from application_client.boilerplate_command_sender import BoilerplateCommandSender
from application_client.boilerplate_response_unpacker import unpack_get_app_and_version_response

from utils import verify_version, verify_name

# Test a specific APDU asking BOLOS (and not the app) the name and version of the current app
def test_get_app_and_version(backend, backend_name):
def test_get_app_and_version(backend: BackendInterface) -> None:
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
# Send the special instruction to BOLOS
Expand Down
23 changes: 16 additions & 7 deletions tests/test_pubkey_cmd.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import pytest

from application_client.boilerplate_command_sender import BoilerplateCommandSender, Errors
from application_client.boilerplate_response_unpacker import unpack_get_public_key_response
from ragger.bip import calculate_public_key_and_chaincode, CurveChoice
from ragger.error import ExceptionRAPDU
from ragger.navigator import NavInsID, NavIns
from ragger.backend.interface import BackendInterface
from ragger.navigator.navigation_scenario import NavigateWithScenario

from application_client.boilerplate_command_sender import BoilerplateCommandSender, Errors
from application_client.boilerplate_response_unpacker import unpack_get_public_key_response


# In this test we check that the GET_PUBLIC_KEY works in non-confirmation mode
def test_get_public_key_no_confirm(backend):
for path in ["m/44'/1'/0'/0/0", "m/44'/1'/0/0/0", "m/44'/1'/911'/0/0", "m/44'/1'/255/255/255", "m/44'/1'/2147483647/0/0/0/0/0/0/0"]:
def test_get_public_key_no_confirm(backend: BackendInterface) -> None:
path_list = [
"m/44'/1'/0'/0/0",
"m/44'/1'/0/0/0",
"m/44'/1'/911'/0/0",
"m/44'/1'/255/255/255",
"m/44'/1'/2147483647/0/0/0/0/0/0/0"
]
for path in path_list:
client = BoilerplateCommandSender(backend)
response = client.get_public_key(path=path).data
_, public_key, _, chain_code = unpack_get_public_key_response(response)
Expand All @@ -20,7 +29,7 @@ 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(backend, scenario_navigator):
def test_get_public_key_confirm_accepted(backend: BackendInterface, scenario_navigator: NavigateWithScenario) -> None:
client = BoilerplateCommandSender(backend)
path = "m/44'/1'/0'/0/0"
with client.get_public_key_with_confirmation(path=path):
Expand All @@ -35,7 +44,7 @@ def test_get_public_key_confirm_accepted(backend, scenario_navigator):


# 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(backend, scenario_navigator):
def test_get_public_key_confirm_refused(backend: BackendInterface, scenario_navigator: NavigateWithScenario) -> None:
client = BoilerplateCommandSender(backend)
path = "m/44'/1'/0'/0/0"

Expand Down
33 changes: 19 additions & 14 deletions tests/test_sign_cmd.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import pytest

from ragger.backend.interface import BackendInterface
from ragger.error import ExceptionRAPDU
from ragger.firmware import Firmware
from ragger.navigator import Navigator, NavInsID
from ragger.navigator.navigation_scenario import NavigateWithScenario

from application_client.boilerplate_transaction import Transaction
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 check_signature_validity

# In this tests we check the behavior of the device when asked to sign a transaction
Expand All @@ -13,7 +17,7 @@
# In this test we 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(backend, scenario_navigator):
def test_sign_tx_short_tx(backend: BackendInterface, scenario_navigator: NavigateWithScenario) -> None:
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
# The path used for this entire test
Expand Down Expand Up @@ -47,10 +51,12 @@ def test_sign_tx_short_tx(backend, scenario_navigator):
# In this test we send to the device a transaction to trig a blind-signing flow
# 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_blind_sign(firmware, navigator, backend, scenario_navigator, test_name, default_screenshot_path):
if firmware.is_nano:
pytest.skip("Not supported on Nano devices")

def test_sign_tx_short_tx_blind_sign(firmware: Firmware,
backend: BackendInterface,
navigator: Navigator,
scenario_navigator: NavigateWithScenario,
test_name: str,
default_screenshot_path: str) -> None:
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
# The path used for this entire test
Expand All @@ -69,12 +75,13 @@ def test_sign_tx_short_tx_blind_sign(firmware, navigator, backend, scenario_navi
).serialize()

# Send the sign device instruction.
valid_instruction = [NavInsID.RIGHT_CLICK] if firmware.is_nano else [NavInsID.USE_CASE_CHOICE_REJECT]
# As it requires on-screen validation, the function is asynchronous.
# It will yield the result when the navigation is done
with client.sign_tx(path=path, transaction=transaction):
navigator.navigate_and_compare(default_screenshot_path,
test_name+"/part1",
[NavInsID.USE_CASE_CHOICE_REJECT],
valid_instruction,
screen_change_after_last_instruction=False)

# Validate the on-screen request by performing the navigation appropriate for this device
Expand All @@ -88,7 +95,7 @@ def test_sign_tx_short_tx_blind_sign(firmware, navigator, backend, scenario_navi
# 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(backend, scenario_navigator):
def test_sign_tx_long_tx(backend: BackendInterface, scenario_navigator: NavigateWithScenario) -> None:
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
path: str = "m/44'/1'/0'/0/0"
Expand All @@ -103,7 +110,8 @@ def test_sign_tx_long_tx(backend, scenario_navigator):
memo=("This is a very long memo. "
"It will force the app client to send the serialized transaction to be sent in chunk. "
"As the maximum chunk size is 255 bytes we will make this memo greater than 255 characters. "
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam.")
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, "
"dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam.")
).serialize()

with client.sign_tx(path=path, transaction=transaction):
Expand All @@ -116,14 +124,11 @@ def test_sign_tx_long_tx(backend, scenario_navigator):

# Transaction signature refused test
# The test will ask for a transaction signature that will be refused on screen
def test_sign_tx_refused(backend, scenario_navigator):
def test_sign_tx_refused(backend: BackendInterface, scenario_navigator: NavigateWithScenario) -> None:
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
path: str = "m/44'/1'/0'/0/0"

rapdu = client.get_public_key(path=path)
_, pub_key, _, _ = unpack_get_public_key_response(rapdu.data)

transaction = Transaction(
nonce=1,
to="0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
Expand Down
4 changes: 3 additions & 1 deletion tests/test_version_cmd.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from ragger.backend.interface import BackendInterface

from application_client.boilerplate_command_sender import BoilerplateCommandSender
from application_client.boilerplate_response_unpacker import unpack_get_version_response

from utils import verify_version

# In this test we check the behavior of the device when asked to provide the app version
def test_version(backend):
def test_version(backend: BackendInterface) -> None:
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
# Send the GET_VERSION instruction
Expand Down

0 comments on commit 7b7d59d

Please sign in to comment.