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

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. 

22 

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 

31 

32import logging 

33 

34logging.disable(logging.CRITICAL) 

35 

36 

37class TestHSM2DongleSignAuthorizedLegacy(TestHSM2DongleBase): 

38 def setUp(self): 

39 super().setUp() 

40 

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 } 

68 

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" 

74 

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 ) 

84 

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) 

101 

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 ) 

108 

109 self.assertEqual( 

110 (False, expected_response), 

111 self.do_sign_auth(spec) 

112 ) 

113 self.assert_exchange(spec["requests"]) 

114 

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 ) 

125 

126 self.assertEqual( 

127 (False, -4), 

128 self.do_sign_auth(spec) 

129 ) 

130 self.assert_exchange(spec["requests"]) 

131 

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 ) 

139 

140 with self.assertRaises(HSM2DongleError): 

141 self.do_sign_auth(spec) 

142 

143 self.assert_exchange(spec["requests"]) 

144 

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 ) 

152 

153 self.assertEqual( 

154 (False, -4), 

155 self.do_sign_auth(spec) 

156 ) 

157 

158 self.assert_exchange(spec["requests"]) 

159 

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 ) 

180 

181 self.assertEqual( 

182 (False, expected_response), 

183 self.do_sign_auth(spec) 

184 ) 

185 self.assert_exchange(spec["requests"]) 

186 

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 ) 

194 

195 with self.assertRaises(HSM2DongleError): 

196 self.do_sign_auth(spec) 

197 self.assert_exchange(spec["requests"]) 

198 

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) 

212 

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 ) 

219 

220 self.assertEqual( 

221 (False, expected_response), 

222 self.do_sign_auth(spec) 

223 ) 

224 self.assert_exchange(spec["requests"]) 

225 

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 ) 

233 

234 with self.assertRaises(HSM2DongleError): 

235 self.do_sign_auth(spec) 

236 self.assert_exchange(spec["requests"]) 

237 

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) 

250 

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 ) 

257 

258 self.assertEqual( 

259 (False, expected_response), 

260 self.do_sign_auth(spec) 

261 ) 

262 

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 ) 

270 

271 with self.assertRaises(HSM2DongleError): 

272 self.do_sign_auth(spec) 

273 

274 

275class TestHSM2DongleSGXSignAuthorizedLegacy(TestHSM2DongleSignAuthorizedLegacy): 

276 def get_test_mode(self): 

277 return HSM2DongleTestMode.SGX