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

129 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 

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, HSMCertificateRoot, HSMCertificateElement 

30 

31 

32class TestHSMCertificate(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_v1.HSMCertificate.ELEMENT_FACTORY') 

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 root_of_trust = HSMCertificateRoot(root_pubkey) 

244 device_privkey = ec.PrivateKey() 

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

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

247 

248 att_msg = 'ff' + att_pubkey 

249 att_sig = device_privkey.ecdsa_serialize( 

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

251 

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

253 device_sig = root_privkey.ecdsa_serialize( 

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

255 

256 cert = HSMCertificate({ 

257 "version": 1, 

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

259 "elements": [ 

260 { 

261 "name": "attestation", 

262 "message": att_msg, 

263 "signature": att_sig, 

264 "signed_by": "device" 

265 }, 

266 { 

267 "name": "device", 

268 "message": device_msg, 

269 "signature": device_sig, 

270 "signed_by": "root" 

271 }] 

272 }) 

273 

274 self.assertEqual({ 

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

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

277 }, cert.validate_and_get_values(root_of_trust)) 

278 

279 def test_validate_and_get_values_invalid_element(self): 

280 root_privkey = ec.PrivateKey() 

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

282 root_of_trust = HSMCertificateRoot(root_pubkey) 

283 device_privkey = ec.PrivateKey() 

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

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

286 

287 att_msg = 'ff' + att_pubkey 

288 att_sig = 'aa' * 65 

289 

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

291 device_sig = root_privkey.ecdsa_serialize( 

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

293 

294 cert = HSMCertificate({ 

295 "version": 1, 

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

297 "elements": [ 

298 { 

299 "name": "attestation", 

300 "message": att_msg, 

301 "signature": att_sig, 

302 "signed_by": "device" 

303 }, 

304 { 

305 "name": "device", 

306 "message": device_msg, 

307 "signature": device_sig, 

308 "signed_by": "root" 

309 }] 

310 }) 

311 

312 self.assertEqual({ 

313 'attestation': (False, 'attestation'), 

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

315 }, cert.validate_and_get_values(root_of_trust)) 

316 

317 def test_validate_and_get_values_invalid_elements(self): 

318 att_privkey = ec.PrivateKey() 

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

320 att_sig = 'aa' * 65 

321 

322 device_privkey = ec.PrivateKey() 

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

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

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

326 device_sig = 'bb' * 65 

327 

328 cert = HSMCertificate({ 

329 "version": 1, 

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

331 "elements": [ 

332 { 

333 "name": "attestation", 

334 "message": att_msg, 

335 "signature": att_sig, 

336 "signed_by": "device" 

337 }, 

338 { 

339 "name": "device", 

340 "message": device_msg, 

341 "signature": device_sig, 

342 "signed_by": "root" 

343 }] 

344 }) 

345 

346 self.assertEqual({ 

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

348 'device': (False, 'device') 

349 }, cert.validate_and_get_values(device_pubkey)) 

350 

351 def test_add_element_ok(self): 

352 cert = HSMCertificate() 

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

354 

355 cert.add_element(HSMCertificateElement({ 

356 "name": "device", 

357 "message": 'cc', 

358 "signature": 'dd', 

359 "signed_by": "root" 

360 })) 

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

362 { 

363 "name": "device", 

364 "message": 'cc', 

365 "signature": 'dd', 

366 "signed_by": "root" 

367 } 

368 ]}, cert.to_dict()) 

369 

370 def test_add_element_invalid_element(self): 

371 cert = HSMCertificate() 

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

373 with self.assertRaises(ValueError): 

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

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

376 

377 def test_add_target_ok(self): 

378 cert = HSMCertificate({ 

379 "version": 1, 

380 "targets": [], 

381 "elements": [ 

382 { 

383 "name": "attestation", 

384 "message": 'aa', 

385 "signature": 'bb', 

386 "signed_by": "device" 

387 }, 

388 { 

389 "name": "device", 

390 "message": 'cc', 

391 "signature": 'dd', 

392 "signed_by": "root" 

393 }] 

394 }) 

395 cert.add_target('attestation') 

396 cert.add_target('device') 

397 self.assertEqual({ 

398 "version": 1, 

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

400 "elements": [ 

401 { 

402 "name": "attestation", 

403 "message": 'aa', 

404 "signature": 'bb', 

405 "signed_by": "device" 

406 }, 

407 { 

408 "name": "device", 

409 "message": 'cc', 

410 "signature": 'dd', 

411 "signed_by": "root" 

412 }] 

413 }, cert.to_dict()) 

414 

415 def test_add_target_not_in_elements(self): 

416 cert = HSMCertificate({ 

417 "version": 1, 

418 "targets": [], 

419 "elements": [ 

420 { 

421 "name": "attestation", 

422 "message": 'aa', 

423 "signature": 'bb', 

424 "signed_by": "device" 

425 }, 

426 { 

427 "name": "device", 

428 "message": 'cc', 

429 "signature": 'dd', 

430 "signed_by": "root" 

431 }] 

432 }) 

433 cert.add_target('attestation') 

434 with self.assertRaises(ValueError): 

435 cert.add_target('ui') 

436 self.assertEqual({ 

437 "version": 1, 

438 "targets": ["attestation"], 

439 "elements": [ 

440 { 

441 "name": "attestation", 

442 "message": 'aa', 

443 "signature": 'bb', 

444 "signed_by": "device" 

445 }, 

446 { 

447 "name": "device", 

448 "message": 'cc', 

449 "signature": 'dd', 

450 "signed_by": "root" 

451 }] 

452 }, cert.to_dict()) 

453 

454 def test_clear_targets(self): 

455 cert = HSMCertificate({ 

456 "version": 1, 

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

458 "elements": [ 

459 { 

460 "name": "attestation", 

461 "message": 'aa', 

462 "signature": 'bb', 

463 "signed_by": "device" 

464 }, 

465 { 

466 "name": "device", 

467 "message": 'cc', 

468 "signature": 'dd', 

469 "signed_by": "root" 

470 }] 

471 }) 

472 cert.clear_targets() 

473 self.assertEqual({ 

474 "version": 1, 

475 "targets": [], 

476 "elements": [ 

477 { 

478 "name": "attestation", 

479 "message": 'aa', 

480 "signature": 'bb', 

481 "signed_by": "device" 

482 }, 

483 { 

484 "name": "device", 

485 "message": 'cc', 

486 "signature": 'dd', 

487 "signed_by": "root" 

488 }] 

489 }, cert.to_dict()) 

490 

491 def test_save_to_jsonfile_ok(self): 

492 cert = HSMCertificate({ 

493 "version": 1, 

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

495 "elements": [ 

496 { 

497 "name": "attestation", 

498 "message": 'aa', 

499 "signature": 'bb', 

500 "signed_by": "device" 

501 }, 

502 { 

503 "name": "device", 

504 "message": 'cc', 

505 "signature": 'dd', 

506 "signed_by": "root" 

507 }] 

508 }) 

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

510 cert.save_to_jsonfile('file-path') 

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

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

513 ' "targets": [\n' 

514 ' "attestation",\n' 

515 ' "device"\n ],\n' 

516 ' "elements": [\n' 

517 ' {\n' 

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

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

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

521 ' "signed_by": "device"\n' 

522 ' },\n' 

523 ' {\n' 

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

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

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

527 ' "signed_by": "root"\n' 

528 ' }\n' 

529 ' ]\n' 

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

531 

532 def test_save_to_jsonfile_write_error(self): 

533 cert = HSMCertificate({ 

534 "version": 1, 

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

536 "elements": [ 

537 { 

538 "name": "attestation", 

539 "message": 'aa', 

540 "signature": 'bb', 

541 "signed_by": "device" 

542 }, 

543 { 

544 "name": "device", 

545 "message": 'cc', 

546 "signature": 'dd', 

547 "signed_by": "root" 

548 }] 

549 }) 

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

551 file_mock.side_effect = Exception() 

552 with self.assertRaises(Exception): 

553 cert.save_to_jsonfile('file-path') 

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

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

556 

557 def test_from_jsonfile_ok(self): 

558 cert_dict = { 

559 "version": 1, 

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

561 "elements": [ 

562 { 

563 "name": "attestation", 

564 "message": 'aa', 

565 "signature": 'bb', 

566 "signed_by": "device" 

567 }, 

568 { 

569 "name": "device", 

570 "message": 'cc', 

571 "signature": 'dd', 

572 "signed_by": "root" 

573 }] 

574 } 

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

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

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

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

579 

580 def test_from_jsonfile_error(self): 

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

582 with self.assertRaises(ValueError): 

583 HSMCertificate.from_jsonfile('file-path') 

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