Coverage for tests/admin/test_certificate.py: 100%

137 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 

24import os 

25import secp256k1 as ec 

26 

27from unittest import TestCase 

28from unittest.mock import call, patch, mock_open 

29from admin.certificate import HSMCertificate, HSMCertificateElement 

30 

31 

32class TestCertificate(TestCase): 

33 def test_create_valid_certificate_ok(self): 

34 cert = HSMCertificate({ 

35 "version": 1, 

36 "targets": ["attestation", "device"], 

37 "elements": [ 

38 { 

39 "name": "attestation", 

40 "message": 'aa', 

41 "signature": 'bb', 

42 "signed_by": "device" 

43 }, 

44 { 

45 "name": "device", 

46 "message": 'cc', 

47 "signature": 'dd', 

48 "signed_by": "root" 

49 }] 

50 }) 

51 self.assertEqual({ 

52 "version": 1, 

53 "targets": ["attestation", "device"], 

54 "elements": [ 

55 { 

56 "name": "attestation", 

57 "message": 'aa', 

58 "signature": 'bb', 

59 "signed_by": "device" 

60 }, 

61 { 

62 "name": "device", 

63 "message": 'cc', 

64 "signature": 'dd', 

65 "signed_by": "root" 

66 }] 

67 }, cert.to_dict()) 

68 

69 def test_create_empty_certificate_ok(self): 

70 cert = HSMCertificate() 

71 self.assertEqual({'version': 1, 'targets': [], 'elements': []}, cert.to_dict()) 

72 

73 def test_create_certificate_invalid_version(self): 

74 with self.assertRaises(ValueError): 

75 HSMCertificate({ 

76 "version": 99, 

77 "targets": ["attestation", "device"], 

78 "elements": [ 

79 { 

80 "name": "attestation", 

81 "message": 'aa', 

82 "signature": 'bb', 

83 "signed_by": "device" 

84 }, 

85 { 

86 "name": "device", 

87 "message": 'cc', 

88 "signature": 'dd', 

89 "signed_by": "root" 

90 }] 

91 }) 

92 

93 def test_create_certificate_no_version(self): 

94 with self.assertRaises(ValueError): 

95 HSMCertificate({ 

96 "targets": ["attestation", "device"], 

97 "elements": [ 

98 { 

99 "name": "attestation", 

100 "message": 'aa', 

101 "signature": 'bb', 

102 "signed_by": "device" 

103 }, 

104 { 

105 "name": "device", 

106 "message": 'cc', 

107 "signature": 'dd', 

108 "signed_by": "root" 

109 }] 

110 }) 

111 

112 def test_create_certificate_missing_targets(self): 

113 with self.assertRaises(ValueError): 

114 HSMCertificate({ 

115 "version": 1, 

116 "elements": [ 

117 { 

118 "name": "attestation", 

119 "message": 'aa', 

120 "signature": 'bb', 

121 "signed_by": "device" 

122 }, 

123 { 

124 "name": "device", 

125 "message": 'cc', 

126 "signature": 'dd', 

127 "signed_by": "root" 

128 }] 

129 }) 

130 

131 def test_create_certificate_invalid_targets(self): 

132 with self.assertRaises(ValueError): 

133 HSMCertificate({ 

134 "version": 1, 

135 "targets": "invalid-targets", 

136 "elements": [ 

137 { 

138 "name": "attestation", 

139 "message": 'aa', 

140 "signature": 'bb', 

141 "signed_by": "device" 

142 }, 

143 { 

144 "name": "device", 

145 "message": 'cc', 

146 "signature": 'dd', 

147 "signed_by": "root" 

148 }] 

149 }) 

150 

151 def test_create_certificate_missing_elements(self): 

152 with self.assertRaises(ValueError): 

153 HSMCertificate({ 

154 "version": 1, 

155 "targets": ["attestation", "device"] 

156 }) 

157 

158 @patch('admin.certificate.HSMCertificateElement') 

159 def test_create_certificate_invalid_element(self, certElementMock): 

160 certElementMock.side_effect = ValueError() 

161 with self.assertRaises(ValueError): 

162 HSMCertificate({ 

163 "version": 1, 

164 "targets": ["attestation", "device"], 

165 "elements": [ 

166 { 

167 "name": "attestation", 

168 "message": 'aa', 

169 "signature": 'bb', 

170 "signed_by": "device" 

171 }, 

172 { 

173 "name": "device", 

174 "message": 'cc', 

175 "signature": 'dd', 

176 "signed_by": "root" 

177 }] 

178 }) 

179 

180 def test_create_certificate_target_not_in_elements(self): 

181 with self.assertRaises(ValueError): 

182 HSMCertificate({ 

183 "version": 1, 

184 "targets": ["attestation", "device", "ui"], 

185 "elements": [ 

186 { 

187 "name": "attestation", 

188 "message": 'aa', 

189 "signature": 'bb', 

190 "signed_by": "device" 

191 }, 

192 { 

193 "name": "device", 

194 "message": 'cc', 

195 "signature": 'dd', 

196 "signed_by": "root" 

197 }] 

198 }) 

199 

200 def test_create_certificate_elements_without_path_to_root(self): 

201 with self.assertRaises(ValueError): 

202 HSMCertificate({ 

203 "version": 1, 

204 "targets": ["attestation", "device"], 

205 "elements": [ 

206 { 

207 "name": "attestation", 

208 "message": 'aa', 

209 "signature": 'bb', 

210 "signed_by": "attestation" 

211 }, 

212 { 

213 "name": "device", 

214 "message": 'cc', 

215 "signature": 'dd', 

216 "signed_by": "root" 

217 }] 

218 }) 

219 

220 def test_create_certificate_signer_not_in_elements(self): 

221 with self.assertRaises(ValueError): 

222 HSMCertificate({ 

223 "version": 1, 

224 "targets": ["attestation", "device"], 

225 "elements": [ 

226 { 

227 "name": "attestation", 

228 "message": 'aa', 

229 "signature": 'bb', 

230 "signed_by": "signer" 

231 }, 

232 { 

233 "name": "device", 

234 "message": 'cc', 

235 "signature": 'dd', 

236 "signed_by": "root" 

237 }] 

238 }) 

239 

240 def test_validate_and_get_values_ok(self): 

241 root_privkey = ec.PrivateKey() 

242 root_pubkey = root_privkey.pubkey.serialize(compressed=False).hex() 

243 device_privkey = ec.PrivateKey() 

244 device_pubkey = device_privkey.pubkey.serialize(compressed=False).hex() 

245 att_pubkey = ec.PrivateKey().pubkey.serialize(compressed=False).hex() 

246 

247 att_msg = 'ff' + att_pubkey 

248 att_sig = device_privkey.ecdsa_serialize( 

249 device_privkey.ecdsa_sign(bytes.fromhex(att_msg))).hex() 

250 

251 device_msg = os.urandom(16).hex() + device_pubkey 

252 device_sig = root_privkey.ecdsa_serialize( 

253 root_privkey.ecdsa_sign(bytes.fromhex(device_msg))).hex() 

254 

255 cert = HSMCertificate({ 

256 "version": 1, 

257 "targets": ["attestation", "device"], 

258 "elements": [ 

259 { 

260 "name": "attestation", 

261 "message": att_msg, 

262 "signature": att_sig, 

263 "signed_by": "device" 

264 }, 

265 { 

266 "name": "device", 

267 "message": device_msg, 

268 "signature": device_sig, 

269 "signed_by": "root" 

270 }] 

271 }) 

272 

273 self.assertEqual({ 

274 'attestation': (True, att_pubkey, None), 

275 'device': (True, device_pubkey, None) 

276 }, cert.validate_and_get_values(root_pubkey)) 

277 

278 def test_create_and_get_values_invalid_pubkey(self): 

279 root_privkey = ec.PrivateKey() 

280 device_privkey = ec.PrivateKey() 

281 device_pubkey = device_privkey.pubkey.serialize(compressed=False).hex() 

282 att_pubkey = ec.PrivateKey().pubkey.serialize(compressed=False).hex() 

283 

284 att_msg = 'ff' + att_pubkey 

285 att_sig = device_privkey.ecdsa_serialize( 

286 device_privkey.ecdsa_sign(bytes.fromhex(att_msg))).hex() 

287 

288 device_msg = os.urandom(16).hex() + device_pubkey 

289 device_sig = root_privkey.ecdsa_serialize( 

290 root_privkey.ecdsa_sign(bytes.fromhex(device_msg))).hex() 

291 

292 cert = HSMCertificate({ 

293 "version": 1, 

294 "targets": ["attestation", "device"], 

295 "elements": [ 

296 { 

297 "name": "attestation", 

298 "message": att_msg, 

299 "signature": att_sig, 

300 "signed_by": "device" 

301 }, 

302 { 

303 "name": "device", 

304 "message": device_msg, 

305 "signature": device_sig, 

306 "signed_by": "root" 

307 }] 

308 }) 

309 

310 self.assertEqual({ 

311 'attestation': (False, 'root'), 

312 'device': (False, 'root') 

313 }, cert.validate_and_get_values('invalid-pubkey')) 

314 

315 def test_validate_and_get_values_invalid_element(self): 

316 root_privkey = ec.PrivateKey() 

317 root_pubkey = root_privkey.pubkey.serialize(compressed=False).hex() 

318 device_privkey = ec.PrivateKey() 

319 device_pubkey = device_privkey.pubkey.serialize(compressed=False).hex() 

320 att_pubkey = ec.PrivateKey().pubkey.serialize(compressed=False).hex() 

321 

322 att_msg = 'ff' + att_pubkey 

323 att_sig = 'aa' * 65 

324 

325 device_msg = os.urandom(16).hex() + device_pubkey 

326 device_sig = root_privkey.ecdsa_serialize( 

327 root_privkey.ecdsa_sign(bytes.fromhex(device_msg))).hex() 

328 

329 cert = HSMCertificate({ 

330 "version": 1, 

331 "targets": ["attestation", "device"], 

332 "elements": [ 

333 { 

334 "name": "attestation", 

335 "message": att_msg, 

336 "signature": att_sig, 

337 "signed_by": "device" 

338 }, 

339 { 

340 "name": "device", 

341 "message": device_msg, 

342 "signature": device_sig, 

343 "signed_by": "root" 

344 }] 

345 }) 

346 

347 self.assertEqual({ 

348 'attestation': (False, 'attestation'), 

349 'device': (True, device_pubkey, None) 

350 }, cert.validate_and_get_values(root_pubkey)) 

351 

352 def test_validate_and_get_values_invalid_elements(self): 

353 att_privkey = ec.PrivateKey() 

354 att_msg = os.urandom(66).hex() 

355 att_sig = 'aa' * 65 

356 

357 device_privkey = ec.PrivateKey() 

358 device_pubkey = device_privkey.pubkey.serialize(compressed=False).hex() 

359 device_msg = os.urandom(16).hex() + \ 

360 att_privkey.pubkey.serialize(compressed=False).hex() 

361 device_sig = 'bb' * 65 

362 

363 cert = HSMCertificate({ 

364 "version": 1, 

365 "targets": ["attestation", "device"], 

366 "elements": [ 

367 { 

368 "name": "attestation", 

369 "message": att_msg, 

370 "signature": att_sig, 

371 "signed_by": "device" 

372 }, 

373 { 

374 "name": "device", 

375 "message": device_msg, 

376 "signature": device_sig, 

377 "signed_by": "root" 

378 }] 

379 }) 

380 

381 self.assertEqual({ 

382 'attestation': (False, 'device'), 

383 'device': (False, 'device') 

384 }, cert.validate_and_get_values(device_pubkey)) 

385 

386 def test_add_element_ok(self): 

387 cert = HSMCertificate() 

388 self.assertEqual({'version': 1, 'targets': [], 'elements': []}, cert.to_dict()) 

389 

390 cert.add_element(HSMCertificateElement({ 

391 "name": "device", 

392 "message": 'cc', 

393 "signature": 'dd', 

394 "signed_by": "root" 

395 })) 

396 self.assertEqual({'version': 1, 'targets': [], 'elements': [ 

397 { 

398 "name": "device", 

399 "message": 'cc', 

400 "signature": 'dd', 

401 "signed_by": "root" 

402 } 

403 ]}, cert.to_dict()) 

404 

405 def test_add_element_invalid_element(self): 

406 cert = HSMCertificate() 

407 self.assertEqual({'version': 1, 'targets': [], 'elements': []}, cert.to_dict()) 

408 with self.assertRaises(ValueError): 

409 cert.add_element('not-an-element') 

410 self.assertEqual({'version': 1, 'targets': [], 'elements': []}, cert.to_dict()) 

411 

412 def test_add_target_ok(self): 

413 cert = HSMCertificate({ 

414 "version": 1, 

415 "targets": [], 

416 "elements": [ 

417 { 

418 "name": "attestation", 

419 "message": 'aa', 

420 "signature": 'bb', 

421 "signed_by": "device" 

422 }, 

423 { 

424 "name": "device", 

425 "message": 'cc', 

426 "signature": 'dd', 

427 "signed_by": "root" 

428 }] 

429 }) 

430 cert.add_target('attestation') 

431 cert.add_target('device') 

432 self.assertEqual({ 

433 "version": 1, 

434 "targets": ["attestation", "device"], 

435 "elements": [ 

436 { 

437 "name": "attestation", 

438 "message": 'aa', 

439 "signature": 'bb', 

440 "signed_by": "device" 

441 }, 

442 { 

443 "name": "device", 

444 "message": 'cc', 

445 "signature": 'dd', 

446 "signed_by": "root" 

447 }] 

448 }, cert.to_dict()) 

449 

450 def test_add_target_not_in_elements(self): 

451 cert = HSMCertificate({ 

452 "version": 1, 

453 "targets": [], 

454 "elements": [ 

455 { 

456 "name": "attestation", 

457 "message": 'aa', 

458 "signature": 'bb', 

459 "signed_by": "device" 

460 }, 

461 { 

462 "name": "device", 

463 "message": 'cc', 

464 "signature": 'dd', 

465 "signed_by": "root" 

466 }] 

467 }) 

468 cert.add_target('attestation') 

469 with self.assertRaises(ValueError): 

470 cert.add_target('ui') 

471 self.assertEqual({ 

472 "version": 1, 

473 "targets": ["attestation"], 

474 "elements": [ 

475 { 

476 "name": "attestation", 

477 "message": 'aa', 

478 "signature": 'bb', 

479 "signed_by": "device" 

480 }, 

481 { 

482 "name": "device", 

483 "message": 'cc', 

484 "signature": 'dd', 

485 "signed_by": "root" 

486 }] 

487 }, cert.to_dict()) 

488 

489 def test_clear_targets(self): 

490 cert = HSMCertificate({ 

491 "version": 1, 

492 "targets": ["attestation", "device"], 

493 "elements": [ 

494 { 

495 "name": "attestation", 

496 "message": 'aa', 

497 "signature": 'bb', 

498 "signed_by": "device" 

499 }, 

500 { 

501 "name": "device", 

502 "message": 'cc', 

503 "signature": 'dd', 

504 "signed_by": "root" 

505 }] 

506 }) 

507 cert.clear_targets() 

508 self.assertEqual({ 

509 "version": 1, 

510 "targets": [], 

511 "elements": [ 

512 { 

513 "name": "attestation", 

514 "message": 'aa', 

515 "signature": 'bb', 

516 "signed_by": "device" 

517 }, 

518 { 

519 "name": "device", 

520 "message": 'cc', 

521 "signature": 'dd', 

522 "signed_by": "root" 

523 }] 

524 }, cert.to_dict()) 

525 

526 def test_save_to_jsonfile_ok(self): 

527 cert = HSMCertificate({ 

528 "version": 1, 

529 "targets": ["attestation", "device"], 

530 "elements": [ 

531 { 

532 "name": "attestation", 

533 "message": 'aa', 

534 "signature": 'bb', 

535 "signed_by": "device" 

536 }, 

537 { 

538 "name": "device", 

539 "message": 'cc', 

540 "signature": 'dd', 

541 "signed_by": "root" 

542 }] 

543 }) 

544 with patch('builtins.open', mock_open()) as file_mock: 

545 cert.save_to_jsonfile('file-path') 

546 self.assertEqual([call('file-path', 'w')], file_mock.call_args_list) 

547 self.assertEqual([call('{\n "version": 1,\n' 

548 ' "targets": [\n' 

549 ' "attestation",\n' 

550 ' "device"\n ],\n' 

551 ' "elements": [\n' 

552 ' {\n' 

553 ' "name": "attestation",\n' 

554 ' "message": "aa",\n' 

555 ' "signature": "bb",\n' 

556 ' "signed_by": "device"\n' 

557 ' },\n' 

558 ' {\n' 

559 ' "name": "device",\n' 

560 ' "message": "cc",\n' 

561 ' "signature": "dd",\n' 

562 ' "signed_by": "root"\n' 

563 ' }\n' 

564 ' ]\n' 

565 '}\n')], file_mock.return_value.write.call_args_list) 

566 

567 def test_save_to_jsonfile_write_error(self): 

568 cert = HSMCertificate({ 

569 "version": 1, 

570 "targets": ["attestation", "device"], 

571 "elements": [ 

572 { 

573 "name": "attestation", 

574 "message": 'aa', 

575 "signature": 'bb', 

576 "signed_by": "device" 

577 }, 

578 { 

579 "name": "device", 

580 "message": 'cc', 

581 "signature": 'dd', 

582 "signed_by": "root" 

583 }] 

584 }) 

585 with patch('builtins.open', mock_open()) as file_mock: 

586 file_mock.side_effect = Exception() 

587 with self.assertRaises(Exception): 

588 cert.save_to_jsonfile('file-path') 

589 self.assertEqual([call('file-path', 'w')], file_mock.call_args_list) 

590 self.assertFalse(file_mock.return_value.write.called) 

591 

592 def test_from_jsonfile_ok(self): 

593 cert_dict = { 

594 "version": 1, 

595 "targets": ["attestation", "device"], 

596 "elements": [ 

597 { 

598 "name": "attestation", 

599 "message": 'aa', 

600 "signature": 'bb', 

601 "signed_by": "device" 

602 }, 

603 { 

604 "name": "device", 

605 "message": 'cc', 

606 "signature": 'dd', 

607 "signed_by": "root" 

608 }] 

609 } 

610 with patch('builtins.open', mock_open(read_data=json.dumps(cert_dict))) as file: 

611 certificate = HSMCertificate.from_jsonfile('file-path') 

612 self.assertEqual([call('file-path', 'r')], file.call_args_list) 

613 self.assertEqual(cert_dict, certificate.to_dict()) 

614 

615 def test_from_jsonfile_error(self): 

616 with patch('builtins.open', mock_open(read_data='invalid-data')) as file: 

617 with self.assertRaises(ValueError): 

618 HSMCertificate.from_jsonfile('file-path') 

619 self.assertEqual([call('file-path', 'r')], file.call_args_list)