Coverage for tests/admin/test_attestation.py: 100%
170 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-05 20:41 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-05 20:41 +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.
23import json
25from types import SimpleNamespace
26from unittest import TestCase
27from unittest.mock import Mock, call, patch, mock_open
28from admin.attestation import do_attestation
29from admin.certificate import HSMCertificate
30from admin.misc import AdminError
31from admin.rsk_client import RskClientError
34@patch("sys.stdout.write")
35@patch("time.sleep")
36@patch("admin.attestation.do_unlock")
37@patch("admin.attestation.get_hsm")
38@patch("admin.attestation.HSMCertificate.from_jsonfile")
39class TestAttestation(TestCase):
40 def setupMocks(self, from_jsonfile, get_hsm):
41 from_jsonfile.return_value = HSMCertificate({
42 "version": 1,
43 "targets": ["attestation", "device"],
44 "elements": [
45 {
46 "name": "attestation",
47 "message": '11' * 32,
48 "signature": '22' * 32,
49 "signed_by": "device"
50 },
51 {
52 "name": "device",
53 "message": '33' * 32,
54 "signature": '44' * 32,
55 "signed_by": "root"
56 }]
57 })
58 get_hsm.return_value = Mock()
59 hsm = get_hsm.return_value
60 hsm.get_ui_attestation = Mock(return_value={
61 'message': 'aa' * 32,
62 'signature': 'bb' * 32,
63 'app_hash': 'cc' * 32
64 })
65 hsm.exit_menu = Mock()
66 hsm.disconnect = Mock()
67 hsm.get_signer_attestation = Mock(return_value={
68 'message': 'dd' * 32,
69 'signature': 'ee' * 32,
70 'app_hash': 'ff' * 32
71 })
73 def setupDefaultOptions(self):
74 options = SimpleNamespace()
75 options.output_file_path = 'out-path'
76 options.attestation_certificate_file_path = 'att-cert-path'
77 options.verbose = False
78 options.attestation_ud_source = 'aa' * 32
79 return options
81 @patch('admin.attestation.RskClient')
82 def test_attestation_ok_provided_ud_value(self,
83 RskClient,
84 from_jsonfile,
85 get_hsm,
86 *_):
87 self.setupMocks(from_jsonfile, get_hsm)
88 options = self.setupDefaultOptions()
89 with patch('builtins.open', mock_open()) as file_mock:
90 do_attestation(options)
92 self.assertEqual([call(options.attestation_ud_source)],
93 get_hsm.return_value.get_ui_attestation.call_args_list)
94 self.assertEqual([], RskClient.call_args_list)
95 self.assertEqual([call(options.attestation_certificate_file_path)],
96 from_jsonfile.call_args_list)
97 self.assertEqual([call(options.verbose), call(options.verbose)],
98 get_hsm.call_args_list)
99 self.assertEqual([call(options.output_file_path, 'w')], file_mock.call_args_list)
100 self.assertEqual([call("%s\n" % json.dumps({
101 'version': 1,
102 'targets': [
103 'ui',
104 'signer'
105 ],
106 'elements': [
107 {
108 "name": "attestation",
109 "message": '11' * 32,
110 "signature": '22' * 32,
111 "signed_by": "device"
112 },
113 {
114 "name": "device",
115 "message": '33' * 32,
116 "signature": '44' * 32,
117 "signed_by": "root"
118 },
119 {
120 'name': 'ui',
121 'message': 'aa' * 32,
122 'signature': 'bb' * 32,
123 'signed_by': 'attestation',
124 'tweak': 'cc' * 32
125 },
126 {
127 'name': 'signer',
128 'message': 'dd' * 32,
129 'signature': 'ee' * 32,
130 'signed_by': 'attestation',
131 'tweak': 'ff' * 32
132 }
133 ]
134 }, indent=2))],
135 file_mock.return_value.write.call_args_list)
137 @patch('admin.attestation.RskClient')
138 def test_attestation_ok_get_ud_value(self, RskClient, from_jsonfile, get_hsm, *_):
139 self.setupMocks(from_jsonfile, get_hsm)
140 RskClient.return_value = Mock()
141 rsk_client = RskClient.return_value
142 rsk_client.get_block_by_number = Mock(return_value={'hash': '0x' + 'bb' * 32})
144 options = self.setupDefaultOptions()
145 options.attestation_ud_source = 'an-url'
146 with patch('builtins.open', mock_open()) as file_mock:
147 do_attestation(options)
149 self.assertEqual([call(options.attestation_certificate_file_path)],
150 from_jsonfile.call_args_list)
151 self.assertEqual([call('an-url')], RskClient.call_args_list)
152 self.assertTrue(rsk_client.get_block_by_number.called)
153 self.assertNotEqual([call(options.attestation_ud_source)],
154 get_hsm.return_value.get_ui_attestation.call_args_list)
155 self.assertEqual([call('bb' * 32)],
156 get_hsm.return_value.get_ui_attestation.call_args_list)
157 self.assertEqual([call(options.verbose), call(options.verbose)],
158 get_hsm.call_args_list)
159 self.assertEqual([call(options.output_file_path, 'w')], file_mock.call_args_list)
160 self.assertEqual([call("%s\n" % json.dumps({
161 'version': 1,
162 'targets': [
163 'ui',
164 'signer'
165 ],
166 'elements': [
167 {
168 "name": "attestation",
169 "message": '11' * 32,
170 "signature": '22' * 32,
171 "signed_by": "device"
172 },
173 {
174 "name": "device",
175 "message": '33' * 32,
176 "signature": '44' * 32,
177 "signed_by": "root"
178 },
179 {
180 'name': 'ui',
181 'message': 'aa' * 32,
182 'signature': 'bb' * 32,
183 'signed_by': 'attestation',
184 'tweak': 'cc' * 32
185 },
186 {
187 'name': 'signer',
188 'message': 'dd' * 32,
189 'signature': 'ee' * 32,
190 'signed_by': 'attestation',
191 'tweak': 'ff' * 32
192 }
193 ]
194 }, indent=2))],
195 file_mock.return_value.write.call_args_list)
197 def test_attestation_no_output_file(self, from_jsonfile, get_hsm, *_):
198 self.setupMocks(from_jsonfile, get_hsm)
199 options = self.setupDefaultOptions()
200 options.output_file_path = None
201 with patch('builtins.open', mock_open()) as file_mock:
202 with self.assertRaises(AdminError):
203 do_attestation(options)
204 self.assertFalse(from_jsonfile.called)
205 self.assertFalse(get_hsm.called)
206 self.assertFalse(file_mock.return_value.write.called)
208 def test_attestation_no_att_cert_file(self, from_jsonfile, get_hsm, *_):
209 self.setupMocks(from_jsonfile, get_hsm)
210 options = self.setupDefaultOptions()
211 options.attestation_certificate_file_path = None
212 with patch('builtins.open', mock_open()) as file_mock:
213 with self.assertRaises(AdminError):
214 do_attestation(options)
215 self.assertFalse(from_jsonfile.called)
216 self.assertFalse(get_hsm.called)
217 self.assertFalse(file_mock.return_value.write.called)
219 def test_attestation_invalid_jsonfile(self, from_jsonfile, get_hsm, *_):
220 self.setupMocks(from_jsonfile, get_hsm)
221 from_jsonfile.side_effect = AdminError()
222 options = self.setupDefaultOptions()
223 with patch('builtins.open', mock_open()) as file_mock:
224 with self.assertRaises(AdminError):
225 do_attestation(options)
226 self.assertTrue(from_jsonfile.called)
227 self.assertFalse(get_hsm.called)
228 self.assertFalse(file_mock.return_value.write.called)
230 @patch('admin.attestation.RskClient')
231 def test_attestation_rsk_client_error(self, RskClient, from_jsonfile, get_hsm, *_):
232 self.setupMocks(from_jsonfile, get_hsm)
233 RskClient.side_effect = RskClientError('error-msg')
234 options = self.setupDefaultOptions()
235 options.attestation_ud_source = 'an-url'
236 with patch('builtins.open', mock_open()) as file_mock:
237 with self.assertRaises(AdminError):
238 do_attestation(options)
239 self.assertTrue(from_jsonfile.called)
240 self.assertFalse(get_hsm.called)
241 self.assertFalse(file_mock.return_value.write.called)
243 def test_attestation_unlock_error(self, from_jsonfile, get_hsm, do_unlock, *_):
244 self.setupMocks(from_jsonfile, get_hsm)
245 do_unlock.side_effect = Exception()
246 options = self.setupDefaultOptions()
247 with patch('builtins.open', mock_open()) as file_mock:
248 with self.assertRaises(AdminError):
249 do_attestation(options)
250 self.assertTrue(from_jsonfile.called)
251 self.assertFalse(get_hsm.called)
252 self.assertFalse(file_mock.return_value.write.called)
254 def test_attestation_get_hsm_error(self, from_jsonfile, get_hsm, *_):
255 self.setupMocks(from_jsonfile, get_hsm)
256 get_hsm.side_effect = Exception()
257 options = self.setupDefaultOptions()
258 with patch('builtins.open', mock_open()) as file_mock:
259 with self.assertRaises(Exception):
260 do_attestation(options)
261 self.assertTrue(from_jsonfile.called)
262 self.assertTrue(get_hsm.called)
263 self.assertFalse(file_mock.return_value.write.called)
265 def test_attestation_get_ui_attestation_error(self, from_jsonfile, get_hsm, *_):
266 self.setupMocks(from_jsonfile, get_hsm)
267 hsm = get_hsm.return_value
268 hsm.get_ui_attestation.side_effect = Exception()
269 options = self.setupDefaultOptions()
270 with patch('builtins.open', mock_open()) as file_mock:
271 with self.assertRaises(AdminError):
272 do_attestation(options)
273 self.assertTrue(from_jsonfile.called)
274 self.assertTrue(get_hsm.called)
275 self.assertFalse(file_mock.return_value.write.called)
277 def test_attestation_get_signer_attestation_error(self, from_jsonfile, get_hsm, *_):
278 self.setupMocks(from_jsonfile, get_hsm)
279 hsm = get_hsm.return_value
280 hsm.get_signer_attestation.side_effect = Exception()
281 options = self.setupDefaultOptions()
282 with patch('builtins.open', mock_open()) as file_mock:
283 with self.assertRaises(AdminError):
284 do_attestation(options)
285 self.assertTrue(from_jsonfile.called)
286 self.assertTrue(get_hsm.called)
287 self.assertFalse(file_mock.return_value.write.called)
289 @patch("admin.attestation.HSMCertificate.add_element")
290 def test_attestation_add_element_error(self, add_element, from_jsonfile, get_hsm, *_):
291 self.setupMocks(from_jsonfile, get_hsm)
292 add_element.side_effect = Exception()
293 options = self.setupDefaultOptions()
294 with patch('builtins.open', mock_open()) as file_mock:
295 with self.assertRaises(Exception):
296 do_attestation(options)
297 self.assertTrue(from_jsonfile.called)
298 self.assertTrue(get_hsm.called)
299 self.assertFalse(file_mock.return_value.write.called)
301 @patch("admin.attestation.HSMCertificate.add_target")
302 def test_attestation_add_target_error(self, add_target, from_jsonfile, get_hsm, *_):
303 self.setupMocks(from_jsonfile, get_hsm)
304 add_target.side_effect = ValueError()
305 options = self.setupDefaultOptions()
306 with patch('builtins.open', mock_open()) as file_mock:
307 with self.assertRaises(ValueError):
308 do_attestation(options)
309 self.assertTrue(from_jsonfile.called)
310 self.assertTrue(get_hsm.called)
311 self.assertFalse(file_mock.return_value.write.called)
313 @patch("admin.attestation.HSMCertificate.save_to_jsonfile")
314 def test_attestation_save_to_jsonfile_error(self,
315 save_to_jsonfile,
316 from_jsonfile,
317 get_hsm,
318 *_):
319 self.setupMocks(from_jsonfile, get_hsm)
320 save_to_jsonfile.side_effect = Exception()
321 options = self.setupDefaultOptions()
322 with patch('builtins.open', mock_open()) as file_mock:
323 with self.assertRaises(Exception):
324 do_attestation(options)
325 self.assertTrue(from_jsonfile.called)
326 self.assertTrue(get_hsm.called)
327 self.assertFalse(file_mock.return_value.write.called)