diff --git a/test/python/test_instructions.py b/test/python/test_instructions.py index a039f7f4..6d064d6e 100644 --- a/test/python/test_instructions.py +++ b/test/python/test_instructions.py @@ -1,13 +1,15 @@ """Module gathering the baking app instruction tests.""" from pathlib import Path +from typing import Callable, Tuple import pytest from ragger.firmware import Firmware from ragger.navigator import Navigator, NavInsID -from utils.client import TezosClient, Version, Hwm -from utils.account import Account +from utils.client import TezosClient, Version, Hwm, StatusCode +from utils.account import Account, SigScheme from utils.helper import get_current_commit from utils.message import ( + Message, Preattestation, Attestation, AttestationDal, @@ -22,7 +24,6 @@ TESTS_ROOT_DIR ) - def test_review_home( firmware: Firmware, navigator: Navigator, @@ -270,6 +271,37 @@ def test_get_all_hwm( f"Expected test hmw {test_hwm} but got {received_test_hwm}" +def build_preattestation(op_level, op_round, chain_id): + """Build a preattestation.""" + return Preattestation( + op_level=op_level, + op_round=op_round + ).forge(chain_id=chain_id) + +def build_attestation(op_level, op_round, chain_id): + """Build a attestation.""" + return Attestation( + op_level=op_level, + op_round=op_round + ).forge(chain_id=chain_id) + +def build_attestation_dal(op_level, op_round, chain_id): + """Build a attestation_dal.""" + return AttestationDal( + op_level=op_level, + op_round=op_round + ).forge(chain_id=chain_id) + +def build_block(level, current_round, chain_id): + """Build a block.""" + return Block( + header=BlockHeader( + level=level, + fitness=Fitness(current_round=current_round) + ) + ).forge(chain_id=chain_id) + + @pytest.mark.parametrize("account", [DEFAULT_ACCOUNT]) @pytest.mark.parametrize("with_hash", [False, True]) def test_sign_preattestation( @@ -291,10 +323,11 @@ def test_sign_preattestation( test_hwm ) - preattestation = Preattestation( + preattestation = build_preattestation( op_level=1, - op_round=2 - ).forge(chain_id=main_chain_id) + op_round=2, + chain_id=main_chain_id + ) if with_hash: signature = client.sign_message(account, preattestation) @@ -337,10 +370,11 @@ def test_sign_attestation( test_hwm ) - attestation = Attestation( + attestation = build_attestation( op_level=1, - op_round=2 - ).forge(chain_id=main_chain_id) + op_round=2, + chain_id=main_chain_id + ) if with_hash: signature = client.sign_message(account, attestation) @@ -383,10 +417,11 @@ def test_sign_attestation_dal( test_hwm ) - attestation = AttestationDal( + attestation = build_attestation_dal( op_level=1, - op_round=2 - ).forge(chain_id=main_chain_id) + op_round=2, + chain_id=main_chain_id + ) if with_hash: signature = client.sign_message(account, attestation) @@ -429,12 +464,11 @@ def test_sign_block( test_hwm ) - block = Block( - header=BlockHeader( - level=1, - fitness=Fitness(current_round=2) - ) - ).forge(chain_id=main_chain_id) + block = build_block( + level=1, + current_round=2, + chain_id=main_chain_id + ) if with_hash: signature = client.sign_message(account, block) @@ -455,6 +489,104 @@ def test_sign_block( path=test_name ) +PARAMETERS = [ + (build_attestation, (0, 0), build_preattestation, (0, 1), True ), + (build_block, (0, 1), build_attestation_dal, (1, 0), True ), + (build_block, (0, 1), build_attestation_dal, (0, 0), False), + (build_attestation, (1, 0), build_preattestation, (0, 1), False), +] + [ + (build_preattestation, (0, 0), build_preattestation, (0, 0), False), + (build_preattestation, (0, 0), build_block, (0, 0), False), + (build_preattestation, (0, 0), build_attestation, (0, 0), True ), + (build_preattestation, (0, 0), build_attestation_dal, (0, 0), True ), + (build_attestation, (0, 0), build_preattestation, (0, 0), False), + (build_attestation, (0, 0), build_attestation, (0, 0), False), + (build_attestation, (0, 0), build_attestation_dal, (0, 0), False), + (build_attestation, (0, 0), build_block, (0, 0), False), + (build_attestation_dal, (0, 0), build_preattestation, (0, 0), False), + (build_attestation_dal, (0, 0), build_attestation, (0, 0), False), + (build_attestation_dal, (0, 0), build_attestation_dal, (0, 0), False), + (build_attestation_dal, (0, 0), build_block, (0, 0), False), + (build_block, (1, 1), build_preattestation, (1, 1), True ), + (build_block, (1, 1), build_attestation, (1, 1), True ), + (build_block, (1, 1), build_attestation_dal, (1, 1), True ), + (build_block, (1, 1), build_block, (1, 1), False), +] + +@pytest.mark.parametrize( + "message_builder_1, level_round_1, " \ + "message_builder_2, level_round_2, " \ + "success", + PARAMETERS) +@pytest.mark.parametrize("account", [DEFAULT_ACCOUNT]) +def test_sign_level_authorized( + account: Account, + message_builder_1: Callable[[int, int, str], Message], + level_round_1: Tuple[int, int], + message_builder_2: Callable[[int, int, str], Message], + level_round_2: Tuple[int, int], + success: bool, + client: TezosClient, + tezos_navigator: TezosNavigator) -> None: + """Test whether the level/round constraints behave as expected.""" + + main_chain_id = DEFAULT_CHAIN_ID + main_level = 1 + + tezos_navigator.setup_app_context( + account, + main_chain_id, + main_hwm=Hwm(main_level), + test_hwm=Hwm(0) + ) + + level_1, round_1 = level_round_1 + message_1 = message_builder_1( + level_1 + main_level, + round_1, + main_chain_id + ) + level_2, round_2 = level_round_2 + message_2 = message_builder_2( + level_2 + main_level, + round_2, + main_chain_id + ) + + client.sign_message(account, message_1) + + if success: + client.sign_message(account, message_2) + else: + with StatusCode.WRONG_VALUES.expected(): + client.sign_message(account, message_2) + +def test_sign_not_authorized_key( + client: TezosClient, + tezos_navigator: TezosNavigator) -> None: + """Check that signing with a key different from the authorized key is not authorized..""" + + account_1 = DEFAULT_ACCOUNT + account_2 = Account( + "m/9'/12'/13'/8'/78'", + SigScheme.ED25519, + "edsk3eZBgFAf1VtdibfxoCcihxXje9S3th7jdEgVA2kHG82EKYNKNm" + ) + + main_chain_id = DEFAULT_CHAIN_ID + + tezos_navigator.setup_app_context( + account_1, + main_chain_id, + main_hwm=Hwm(0), + test_hwm=Hwm(0) + ) + + attestation = build_attestation(0, 0, main_chain_id) + + with StatusCode.SECURITY.expected(): + client.sign_message(account_2, attestation) + # Data generated by the old application itself HMAC_TEST_SET = [ diff --git a/test/python/utils/client.py b/test/python/utils/client.py index fc259b5a..57d9c875 100644 --- a/test/python/utils/client.py +++ b/test/python/utils/client.py @@ -1,7 +1,8 @@ """Module providing a tezos client.""" -from typing import Tuple, Optional +from typing import Tuple, Optional, Generator from enum import IntEnum +from contextlib import contextmanager from ragger.utils import RAPDU from ragger.backend import BackendInterface @@ -142,7 +143,30 @@ class Index(IntEnum): class StatusCode(IntEnum): """Class representing the status code.""" - OK = 0x9000 + OK = 0x9000 + WRONG_PARAM = 0x6b00 + WRONG_LENGTH = 0x6c00 + INVALID_INS = 0x6d00 + WRONG_LENGTH_FOR_INS = 0x917e + REJECT = 0x6985 + PARSE_ERROR = 0x9405 + REFERENCED_DATA_NOT_FOUND = 0x6a88 + WRONG_VALUES = 0x6a80 + SECURITY = 0x6982 + HID_REQUIRED = 0x6983 + CLASS = 0x6e00 + MEMORY_ERROR = 0x9200 + + @contextmanager + def expected(self) -> Generator[None, None, None]: + """Fail if the right RAPDU code exception is not raise.""" + try: + yield + assert False, f"Expect fail with { self.name } but succeed" + except ExceptionRAPDU as e: + failing_code = StatusCode(e.status) + assert self == failing_code, \ + f"Expect fail with { self.name } but fail with { failing_code.name }" MAX_APDU_SIZE: int = 235