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

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 

23import json 

24 

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 

32 

33 

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 }) 

72 

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 

80 

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) 

91 

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) 

136 

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}) 

143 

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) 

148 

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) 

196 

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) 

207 

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) 

218 

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) 

229 

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) 

242 

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) 

253 

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) 

264 

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) 

276 

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) 

288 

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) 

300 

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) 

312 

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)