Coverage for tests/ledger/hsm2dongle_cmds/test_hsm2dongle_sign_auth_legacy.py: 100%
91 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
1# The MIT License (MIT)
2#
3# Copyright (c) 2021 RSK Labs Ltd
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy of
6# this software and associated documentation files (the "Software"), to deal in
7# the Software without restriction, including without limitation the rights to
8# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9# of the Software, and to permit persons to whom the Software is furnished to do
10# so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
23from unittest.mock import Mock, patch, call
24from parameterized import parameterized
25from tests.ledger.test_hsm2dongle import TestHSM2DongleBase, HSM2DongleTestMode
26from ledger.hsm2dongle import (
27 HSM2DongleError,
28 SighashComputationMode,
29)
30from ledgerblue.commException import CommException
32import logging
34logging.disable(logging.CRITICAL)
37class TestHSM2DongleSignAuthorizedLegacy(TestHSM2DongleBase):
38 def setUp(self):
39 super().setUp()
41 self.LEGACY_SPEC = {
42 "exchanges": [
43 "q-path >02 01 11223344 D2040000",
44 "a-tx0 <02 02 0C",
45 "q-tx0 >02 02 0F000000 00 0000 AABBCCDDEE",
46 "a-tx1 <02 02 03",
47 "q-tx1 >02 02 FF7788",
48 "a-rc0 <02 04 04",
49 "q-rc0 >02 04 00112233",
50 "a-rc1 <02 04 06",
51 "q-rc1 >02 04 445566778899",
52 "a-mp0 <02 08 04",
53 "q-mp0 >02 08 03 03 3344",
54 "a-mp1 <02 08 03",
55 "q-mp1 >02 08 55 02 66",
56 "a-mp2 <02 08 07",
57 "q-mp2 >02 08 77 05 aabbccddee",
58 "a-sig <02 81 AABBCCDD",
59 ],
60 "mode": SighashComputationMode.LEGACY,
61 "tx": "aabbccddeeff7788",
62 "input": 1234,
63 "ws": None,
64 "ov": None,
65 "receipt": "00112233445566778899",
66 "mp": ["334455", "6677", "aabbccddee"],
67 }
69 @patch("ledger.hsm2dongle.HSM2DongleSignature")
70 def test_ok(self, HSM2DongleSignatureMock):
71 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
72 spec = self.process_sign_auth_spec({**self.LEGACY_SPEC, "keyid": key_id})
73 HSM2DongleSignatureMock.return_value = "the-signature"
75 self.assertEqual(
76 (True, "the-signature"),
77 self.do_sign_auth(spec)
78 )
79 self.assert_exchange(spec["requests"])
80 self.assertEqual(
81 [call(bytes.fromhex("aabbccdd"))],
82 HSM2DongleSignatureMock.call_args_list,
83 )
85 @parameterized.expand([
86 ("data_size", 0x6A87, -4),
87 ("state", 0x6A89, -4),
88 ("node_version", 0x6A92, -4),
89 ("shared_prefix_too_big", 0x6A93, -4),
90 ("receipt_hash_mismatch", 0x6A94, -4),
91 ("node_chaining_mismatch", 0x6A95, -4),
92 ("receipt_root_mismatch", 0x6A96, -4),
93 ("unknown", 0x6AFF, -10),
94 ("unexpected", [0, 0, 0xFF], -10),
95 ])
96 def test_mp_invalid(self, _, device_error, expected_response):
97 if type(device_error) == int:
98 last_response = CommException("msg", device_error)
99 else:
100 last_response = bytes(device_error)
102 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
103 spec = self.process_sign_auth_spec(
104 {**self.LEGACY_SPEC, "keyid": key_id},
105 stop="a-mp2",
106 replace=last_response
107 )
109 self.assertEqual(
110 (False, expected_response),
111 self.do_sign_auth(spec)
112 )
113 self.assert_exchange(spec["requests"])
115 @parameterized.expand([
116 ("too_many_nodes", ["aa"]*256),
117 ("node_too_big", ["aa"]*100 + ["bb"*256]),
118 ])
119 def test_mp_too_big(self, _, receipt_merkle_proof):
120 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
121 spec = self.process_sign_auth_spec(
122 {**self.LEGACY_SPEC, "keyid": key_id, "mp": receipt_merkle_proof},
123 stop="q-mp0"
124 )
126 self.assertEqual(
127 (False, -4),
128 self.do_sign_auth(spec)
129 )
130 self.assert_exchange(spec["requests"])
132 def test_mp_unexpected_exc(self):
133 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
134 spec = self.process_sign_auth_spec(
135 {**self.LEGACY_SPEC, "keyid": key_id},
136 stop="a-mp2",
137 replace=CommException("msg", 0xFFFF),
138 )
140 with self.assertRaises(HSM2DongleError):
141 self.do_sign_auth(spec)
143 self.assert_exchange(spec["requests"])
145 def test_mp_invalid_format(self):
146 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
147 spec = self.process_sign_auth_spec(
148 {**self.LEGACY_SPEC, "keyid": key_id, "mp": ["334455", "6677", "not-a-hex"]},
149 stop="q-mp0",
150 replace=None,
151 )
153 self.assertEqual(
154 (False, -4),
155 self.do_sign_auth(spec)
156 )
158 self.assert_exchange(spec["requests"])
160 @parameterized.expand([
161 ("data_size", 0x6A87, -3),
162 ("state", 0x6A89, -3),
163 ("rlp", 0x6A8A, -3),
164 ("rlp_int", 0x6A8B, -3),
165 ("rlp_depth", 0x6A8C, -3),
166 ("unknown", 0x6AFF, -10),
167 ("unexpected", [0, 0, 0xFF], -10),
168 ])
169 def test_receipt_invalid(self, _, device_error, expected_response):
170 if type(device_error) == int:
171 last_response = CommException("msg", device_error)
172 else:
173 last_response = bytes(device_error)
174 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
175 spec = self.process_sign_auth_spec(
176 {**self.LEGACY_SPEC, "keyid": key_id},
177 stop="a-rc1",
178 replace=last_response
179 )
181 self.assertEqual(
182 (False, expected_response),
183 self.do_sign_auth(spec)
184 )
185 self.assert_exchange(spec["requests"])
187 def test_receipt_unexpected_error_exc(self):
188 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
189 spec = self.process_sign_auth_spec(
190 {**self.LEGACY_SPEC, "keyid": key_id},
191 stop="a-rc1",
192 replace=CommException("", 0xFFFF)
193 )
195 with self.assertRaises(HSM2DongleError):
196 self.do_sign_auth(spec)
197 self.assert_exchange(spec["requests"])
199 @parameterized.expand([
200 ("input", 0x6A88, -2),
201 ("tx_hash_mismatch", 0x6A8D, -2),
202 ("tx_version", 0x6A8E, -2),
203 ("invalid_sighash_computation_mode", 0x6A97, -2),
204 ("unknown", 0x6AFF, -10),
205 ("unexpected", [0, 0, 0xFF], -10),
206 ])
207 def test_btctx_invalid(self, _, device_error, expected_response):
208 if type(device_error) == int:
209 last_response = CommException("msg", device_error)
210 else:
211 last_response = bytes(device_error)
213 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
214 spec = self.process_sign_auth_spec(
215 {**self.LEGACY_SPEC, "keyid": key_id},
216 stop="a-tx1",
217 replace=last_response
218 )
220 self.assertEqual(
221 (False, expected_response),
222 self.do_sign_auth(spec)
223 )
224 self.assert_exchange(spec["requests"])
226 def test_btctx_unexpected_error_exc(self):
227 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
228 spec = self.process_sign_auth_spec(
229 {**self.LEGACY_SPEC, "keyid": key_id},
230 stop="a-rc0",
231 replace=CommException("", 0xFF)
232 )
234 with self.assertRaises(HSM2DongleError):
235 self.do_sign_auth(spec)
236 self.assert_exchange(spec["requests"])
238 @parameterized.expand([
239 ("data_size", 0x6A87, -1),
240 ("data_size_auth", 0x6A90, -1),
241 ("data_size_noauth", 0x6A91, -1),
242 ("unknown", 0x6AFF, -10),
243 ("unexpected", [0, 0, 0xFF], -10),
244 ])
245 def test_path_invalid(self, _, device_error, expected_response):
246 if type(device_error) == int:
247 last_response = CommException("msg", device_error)
248 else:
249 last_response = bytes(device_error)
251 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
252 spec = self.process_sign_auth_spec(
253 {**self.LEGACY_SPEC, "keyid": key_id},
254 stop="a-tx0",
255 replace=last_response
256 )
258 self.assertEqual(
259 (False, expected_response),
260 self.do_sign_auth(spec)
261 )
263 def test_path_unexpected_error_exc(self):
264 key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")})
265 spec = self.process_sign_auth_spec(
266 {**self.LEGACY_SPEC, "keyid": key_id},
267 stop="a-tx0",
268 replace=CommException("", 0xFFFF)
269 )
271 with self.assertRaises(HSM2DongleError):
272 self.do_sign_auth(spec)
275class TestHSM2DongleSGXSignAuthorizedLegacy(TestHSM2DongleSignAuthorizedLegacy):
276 def get_test_mode(self):
277 return HSM2DongleTestMode.SGX