forked from LedgerHQ/app-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test_sign_cmd.py
141 lines (122 loc) · 7.14 KB
/
test_sign_cmd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import pytest
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 ROOT_SCREENSHOT_PATH, 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):
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
# The path used for this entire test
path: str = "m/44'/1'/0'/0/0"
# First we need to get the public key of the device in order to build the transaction
rapdu = client.get_public_key(path=path)
_, public_key, _, _ = unpack_get_public_key_response(rapdu.data)
# Create the transaction that will be sent to the device for signing
transaction = Transaction(
nonce=1,
to="0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
value=666,
memo="For u EthDev"
).serialize()
# Send the sign device instruction.
# 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):
# 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)
# The device as yielded the result, parse it and ensure that the signature is correct
response = client.get_async_response().data
_, der_sig, _ = unpack_sign_tx_response(response)
assert check_signature_validity(public_key, der_sig, transaction)
# 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):
# 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)
_, public_key, _, _ = unpack_get_public_key_response(rapdu.data)
transaction = Transaction(
nonce=1,
to="0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
value=666,
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.")
).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)
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):
# 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",
value=666,
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