Skip to content

Commit

Permalink
Python client refactor
Browse files Browse the repository at this point in the history
Fixed too many arguments CI issue
  • Loading branch information
relatko committed Sep 27, 2024
1 parent ccaf8b7 commit 69c3416
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 144 deletions.
48 changes: 25 additions & 23 deletions tests/application_client/flow_command_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Generator, Optional, List
from contextlib import contextmanager
from bip_utils import Bip32Utils # type: ignore[import]
from dataclasses import dataclass

from ragger.backend.interface import BackendInterface, RAPDU
from ragger.bip import CurveChoice
Expand Down Expand Up @@ -70,6 +71,12 @@ class HashType(str, Enum):
# SHA3-256
HASH_SHA3 = "sha-3"

@dataclass(frozen=True)
class CryptoOptions:
"""Stores curve and hash"""
curve: CurveChoice
hash_t: HashType


def _pack_derivation(derivation_path: str) -> bytes:
""" Pack derivation path in bytes """
Expand All @@ -91,33 +98,32 @@ def _pack_derivation(derivation_path: str) -> bytes:
return path_bytes


def _pack_crypto_option(curve: CurveChoice, hash_t: HashType) -> bytes:
def _pack_crypto_option(crypto_options: CryptoOptions) -> bytes:
""" Pack crypto (curve + hash) options in bytes """

path_bytes: bytes = bytes()

if hash_t == HashType.HASH_SHA2:
if crypto_options.hash_t == HashType.HASH_SHA2:
hash_value = 1
elif hash_t == HashType.HASH_SHA3:
elif crypto_options.hash_t == HashType.HASH_SHA3:
hash_value = 3
else:
raise ValueError(f'Wrong Hash "{hash_t}"')
raise ValueError(f'Wrong Hash "{crypto_options.hash_t}"')
path_bytes += int(hash_value).to_bytes(1, byteorder='little')

if curve == CurveChoice.Nist256p1:
if crypto_options.curve == CurveChoice.Nist256p1:
curve_value = 2
elif curve == CurveChoice.Secp256k1:
elif crypto_options.curve == CurveChoice.Secp256k1:
curve_value = 3
else:
raise ValueError(f'Wrong Cruve "{curve}"')
raise ValueError(f'Wrong Cruve "{crypto_options.curve}"')
path_bytes += int(curve_value).to_bytes(1, byteorder='little')

return path_bytes


def _format_apdu_data(
curve: CurveChoice = CurveChoice.Secp256k1,
hash_t: HashType = HashType.HASH_SHA2,
crypto_options: CryptoOptions,
path: str = "",
address: str = "",
slot: int = MAX_SLOTS,
Expand All @@ -135,7 +141,7 @@ def _format_apdu_data(
# Consider empty slot, force option to 0
data_path += int(0).to_bytes(2, 'little')
else:
data_path += _pack_crypto_option(curve, hash_t)
data_path += _pack_crypto_option(crypto_options)

return data_path

Expand All @@ -149,7 +155,7 @@ def __init__(self, backend: BackendInterface) -> None:
def get_generic(self) -> RAPDU:
""" APDU generic version """

data_path= _format_apdu_data(address="00")
data_path = _format_apdu_data(CryptoOptions(CurveChoice.Secp256k1, HashType.HASH_SHA2), address="00")
return self.backend.exchange(cla=ClaType.CLA_GEN,
ins=InsType.GENERIC,
data=data_path)
Expand Down Expand Up @@ -182,12 +188,11 @@ def set_slot(
slot: int,
address: str,
path: str,
curve: CurveChoice,
hash_t: HashType,
crypto_options: CryptoOptions,
) -> Generator[None, None, None]:
""" APDU set slot """

data_path= _format_apdu_data(curve, hash_t, path, address, slot)
data_path= _format_apdu_data(crypto_options, path, address, slot)
with self.backend.exchange_async(cla=ClaType.CLA_APP,
ins=InsType.SET_SLOT,
data=data_path) as response:
Expand All @@ -196,12 +201,11 @@ def set_slot(
def get_public_key_no_confirmation(
self,
path: str,
curve: CurveChoice,
hash_t: HashType,
crypto_options: CryptoOptions,
) -> RAPDU:
""" APDU get public key - no confirmation """

data_path= _format_apdu_data(curve, hash_t, path)
data_path= _format_apdu_data(crypto_options, path)
return self.backend.exchange(cla=ClaType.CLA_APP,
ins=InsType.GET_PUBKEY,
p1=P1.P1_NO_CONFIRM,
Expand All @@ -211,12 +215,11 @@ def get_public_key_no_confirmation(
def get_public_key_with_confirmation(
self,
path: str,
curve: CurveChoice,
hash_t: HashType,
crypto_options: CryptoOptions,
) -> Generator[None, None, None]:
""" APDU get public key - with confirmation """

data_path= _format_apdu_data(curve, hash_t, path)
data_path= _format_apdu_data(crypto_options, path)
with self.backend.exchange_async(cla=ClaType.CLA_APP,
ins=InsType.GET_PUBKEY,
p1=P1.P1_CONFIRM,
Expand All @@ -227,14 +230,13 @@ def get_public_key_with_confirmation(
def sign_tx(
self,
path: str,
curve: CurveChoice,
crypto_options: CryptoOptions,
transaction: bytes,
hash_t: HashType,
hint: str = ""
) -> Generator[None, None, None]:
""" APDU sign transaction """

data_path = _format_apdu_data(curve, hash_t, path)
data_path = _format_apdu_data(crypto_options, path)
self.backend.exchange(cla=ClaType.CLA_APP,
ins=InsType.SIGN,
p1=P1.P1_INIT,
Expand Down
7 changes: 3 additions & 4 deletions tests/test_app_mainmenu.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from application_client.flow_command_sender import FlowCommandSender, HashType
from application_client.flow_command_sender import FlowCommandSender, HashType, CryptoOptions

from ragger.navigator import NavIns, NavInsID
from ragger.bip import CurveChoice
Expand Down Expand Up @@ -55,12 +55,11 @@ def test_app_mainmenu(firmware, backend, navigator, test_name):
screen_change_before_first_instruction=False)

# Send the APDU - Set slot
hash_t = HashType.HASH_SHA2
crypto_options = CryptoOptions(CurveChoice.Secp256k1, HashType.HASH_SHA2)
address = "e467b9dd11fa00df"
path = "m/44'/539'/513'/0/0"
curve = CurveChoice.Secp256k1
part += 1
util_set_slot(client, firmware, navigator, f"{test_name}/part{part}", 0, curve, hash_t, address, path)
util_set_slot(client, firmware, navigator, f"{test_name}/part{part}", 0, crypto_options, address, path)

# Navigate in the main menu, click "View address"
if firmware.device.startswith("nano"):
Expand Down
41 changes: 18 additions & 23 deletions tests/test_pubkey_cmd.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from application_client.flow_command_sender import FlowCommandSender, Errors, HashType
from application_client.flow_command_sender import FlowCommandSender, Errors, HashType, CryptoOptions
from application_client.flow_response_unpacker import unpack_get_public_key_response

from ragger.bip import calculate_public_key_and_chaincode, CurveChoice
Expand Down Expand Up @@ -34,7 +34,7 @@ def test_get_public_key_no_confirm(backend):
# Send the APDU and check the results
for path in path_list:
for curve in curve_list:
_ = util_check_pub_key(client, path, curve)
_ = util_check_pub_key(client, path, CryptoOptions(curve, HashType.HASH_SHA2))


def test_get_public_key_slot(firmware, backend, navigator, test_name):
Expand All @@ -46,29 +46,32 @@ def test_get_public_key_slot(firmware, backend, navigator, test_name):
slot = 0
curve0 = CurveChoice.Secp256k1
curve1 = CurveChoice.Nist256p1
options0 = CryptoOptions(curve0, HashType.HASH_SHA2)
options1 = CryptoOptions(curve0, HashType.HASH_SHA3)
options2 = CryptoOptions(curve1, HashType.HASH_SHA2)
address = "e467b9dd11fa00de"
path0 = "m/44'/539'/513'/0/0"
path1 = "m/44'/539'/513'/0/1"

# Send the APDU and check the results

# Call get_public_key when slot is empty
_ = util_check_pub_key(client, path0, curve0)
_ = util_check_pub_key(client, path0, options0)

part = 0
# Set_slot to some other path
util_set_slot(client, firmware, navigator, f"{test_name}/part{part}", slot, curve0, HashType.HASH_SHA2, address, path1)
util_set_slot(client, firmware, navigator, f"{test_name}/part{part}", slot, options0, address, path1)

# Call get_public_key for different path values
path_list = [path0, path1]
for path in path_list:
_ = util_check_pub_key(client, path, curve0)
_ = util_check_pub_key(client, path, options0)

# Call get_public_key for other path - but hashes do not match - does not matter
_ = util_check_pub_key(client, path1, curve0, HashType.HASH_SHA3)
_ = util_check_pub_key(client, path1, options1)

# Call get_public_key for other path - but curves do not match - warning
_ = util_check_pub_key(client, path1, curve1)
_ = util_check_pub_key(client, path1, options2)

# Clean Slot
part += 1
Expand All @@ -83,14 +86,8 @@ def test_get_public_key_expert(self, firmware, backend, navigator, test_name):
client = FlowCommandSender(backend)
# Test parameters
test_cfg = [
{
"curve": CurveChoice.Secp256k1,
"hash": HashType.HASH_SHA2,
},
{
"curve": CurveChoice.Nist256p1,
"hash": HashType.HASH_SHA3,
},
CryptoOptions(CurveChoice.Secp256k1, HashType.HASH_SHA2),
CryptoOptions(CurveChoice.Nist256p1, HashType.HASH_SHA3),
]
path = "m/44'/539'/513'/0/0"

Expand All @@ -99,7 +96,7 @@ def test_get_public_key_expert(self, firmware, backend, navigator, test_name):

# Send the APDU and check the results
for cfg in test_cfg:
_ = util_check_pub_key(client, path, cfg["curve"], cfg["hash"])
_ = util_check_pub_key(client, path, cfg)


def test_get_public_key_confirm_accepted(firmware, backend, navigator, test_name):
Expand All @@ -109,11 +106,10 @@ def test_get_public_key_confirm_accepted(firmware, backend, navigator, test_name
client = FlowCommandSender(backend)
# Test parameters
path = "m/44'/539'/0'/0/0"
curve = CurveChoice.Secp256k1
hash_t = HashType.HASH_SHA2
options = CryptoOptions(CurveChoice.Secp256k1, HashType.HASH_SHA2)

# Send the APDU (Asynchronous)
with client.get_public_key_with_confirmation(path, curve, hash_t):
with client.get_public_key_with_confirmation(path, options):
util_navigate(firmware, navigator, test_name, "APPROVE_PUBKEY")

# Check the status (Asynchronous)
Expand All @@ -123,7 +119,7 @@ def test_get_public_key_confirm_accepted(firmware, backend, navigator, test_name
# Parse the response
public_key = unpack_get_public_key_response(response.data)
# Compute the reference data
ref_public_key, _ = calculate_public_key_and_chaincode(curve, path, OPTIONAL.CUSTOM_SEED)
ref_public_key, _ = calculate_public_key_and_chaincode(options.curve, path, OPTIONAL.CUSTOM_SEED)
# Check expected value
assert public_key == ref_public_key

Expand All @@ -135,12 +131,11 @@ def test_get_public_key_confirm_refused(firmware, backend, navigator, test_name)
client = FlowCommandSender(backend)
# Test parameters
path = "m/44'/1'/0'/0/0"
curve = CurveChoice.Secp256k1
hash_t = HashType.HASH_SHA2
options = CryptoOptions(CurveChoice.Secp256k1, HashType.HASH_SHA2)

# Send the APDU (Asynchronous)
with pytest.raises(ExceptionRAPDU) as err:
with client.get_public_key_with_confirmation(path, curve, hash_t):
with client.get_public_key_with_confirmation(path, options):
util_navigate(firmware, navigator, test_name, "REJECT_PUBKEY")

# Assert we have received a refusal
Expand Down
Loading

0 comments on commit 69c3416

Please sign in to comment.