Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/asn1crypto/crl.py: 50%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

197 statements  

1# coding: utf-8 

2 

3""" 

4ASN.1 type classes for certificate revocation lists (CRL). Exports the 

5following items: 

6 

7 - CertificateList() 

8 

9Other type classes are defined that help compose the types listed above. 

10""" 

11 

12from __future__ import unicode_literals, division, absolute_import, print_function 

13 

14import hashlib 

15 

16from .algos import SignedDigestAlgorithm 

17from .core import ( 

18 Boolean, 

19 Enumerated, 

20 GeneralizedTime, 

21 Integer, 

22 ObjectIdentifier, 

23 OctetBitString, 

24 ParsableOctetString, 

25 Sequence, 

26 SequenceOf, 

27) 

28from .x509 import ( 

29 AuthorityInfoAccessSyntax, 

30 AuthorityKeyIdentifier, 

31 CRLDistributionPoints, 

32 DistributionPointName, 

33 GeneralNames, 

34 Name, 

35 ReasonFlags, 

36 Time, 

37) 

38 

39 

40# The structures in this file are taken from https://tools.ietf.org/html/rfc5280 

41 

42 

43class Version(Integer): 

44 _map = { 

45 0: 'v1', 

46 1: 'v2', 

47 2: 'v3', 

48 } 

49 

50 

51class IssuingDistributionPoint(Sequence): 

52 _fields = [ 

53 ('distribution_point', DistributionPointName, {'explicit': 0, 'optional': True}), 

54 ('only_contains_user_certs', Boolean, {'implicit': 1, 'default': False}), 

55 ('only_contains_ca_certs', Boolean, {'implicit': 2, 'default': False}), 

56 ('only_some_reasons', ReasonFlags, {'implicit': 3, 'optional': True}), 

57 ('indirect_crl', Boolean, {'implicit': 4, 'default': False}), 

58 ('only_contains_attribute_certs', Boolean, {'implicit': 5, 'default': False}), 

59 ] 

60 

61 

62class TBSCertListExtensionId(ObjectIdentifier): 

63 _map = { 

64 '2.5.29.18': 'issuer_alt_name', 

65 '2.5.29.20': 'crl_number', 

66 '2.5.29.27': 'delta_crl_indicator', 

67 '2.5.29.28': 'issuing_distribution_point', 

68 '2.5.29.35': 'authority_key_identifier', 

69 '2.5.29.46': 'freshest_crl', 

70 '1.3.6.1.5.5.7.1.1': 'authority_information_access', 

71 } 

72 

73 

74class TBSCertListExtension(Sequence): 

75 _fields = [ 

76 ('extn_id', TBSCertListExtensionId), 

77 ('critical', Boolean, {'default': False}), 

78 ('extn_value', ParsableOctetString), 

79 ] 

80 

81 _oid_pair = ('extn_id', 'extn_value') 

82 _oid_specs = { 

83 'issuer_alt_name': GeneralNames, 

84 'crl_number': Integer, 

85 'delta_crl_indicator': Integer, 

86 'issuing_distribution_point': IssuingDistributionPoint, 

87 'authority_key_identifier': AuthorityKeyIdentifier, 

88 'freshest_crl': CRLDistributionPoints, 

89 'authority_information_access': AuthorityInfoAccessSyntax, 

90 } 

91 

92 

93class TBSCertListExtensions(SequenceOf): 

94 _child_spec = TBSCertListExtension 

95 

96 

97class CRLReason(Enumerated): 

98 _map = { 

99 0: 'unspecified', 

100 1: 'key_compromise', 

101 2: 'ca_compromise', 

102 3: 'affiliation_changed', 

103 4: 'superseded', 

104 5: 'cessation_of_operation', 

105 6: 'certificate_hold', 

106 8: 'remove_from_crl', 

107 9: 'privilege_withdrawn', 

108 10: 'aa_compromise', 

109 } 

110 

111 @property 

112 def human_friendly(self): 

113 """ 

114 :return: 

115 A unicode string with revocation description that is suitable to 

116 show to end-users. Starts with a lower case letter and phrased in 

117 such a way that it makes sense after the phrase "because of" or 

118 "due to". 

119 """ 

120 

121 return { 

122 'unspecified': 'an unspecified reason', 

123 'key_compromise': 'a compromised key', 

124 'ca_compromise': 'the CA being compromised', 

125 'affiliation_changed': 'an affiliation change', 

126 'superseded': 'certificate supersession', 

127 'cessation_of_operation': 'a cessation of operation', 

128 'certificate_hold': 'a certificate hold', 

129 'remove_from_crl': 'removal from the CRL', 

130 'privilege_withdrawn': 'privilege withdrawl', 

131 'aa_compromise': 'the AA being compromised', 

132 }[self.native] 

133 

134 

135class CRLEntryExtensionId(ObjectIdentifier): 

136 _map = { 

137 '2.5.29.21': 'crl_reason', 

138 '2.5.29.23': 'hold_instruction_code', 

139 '2.5.29.24': 'invalidity_date', 

140 '2.5.29.29': 'certificate_issuer', 

141 } 

142 

143 

144class CRLEntryExtension(Sequence): 

145 _fields = [ 

146 ('extn_id', CRLEntryExtensionId), 

147 ('critical', Boolean, {'default': False}), 

148 ('extn_value', ParsableOctetString), 

149 ] 

150 

151 _oid_pair = ('extn_id', 'extn_value') 

152 _oid_specs = { 

153 'crl_reason': CRLReason, 

154 'hold_instruction_code': ObjectIdentifier, 

155 'invalidity_date': GeneralizedTime, 

156 'certificate_issuer': GeneralNames, 

157 } 

158 

159 

160class CRLEntryExtensions(SequenceOf): 

161 _child_spec = CRLEntryExtension 

162 

163 

164class RevokedCertificate(Sequence): 

165 _fields = [ 

166 ('user_certificate', Integer), 

167 ('revocation_date', Time), 

168 ('crl_entry_extensions', CRLEntryExtensions, {'optional': True}), 

169 ] 

170 

171 _processed_extensions = False 

172 _critical_extensions = None 

173 _crl_reason_value = None 

174 _invalidity_date_value = None 

175 _certificate_issuer_value = None 

176 _issuer_name = False 

177 

178 def _set_extensions(self): 

179 """ 

180 Sets common named extensions to private attributes and creates a list 

181 of critical extensions 

182 """ 

183 

184 self._critical_extensions = set() 

185 

186 for extension in self['crl_entry_extensions']: 

187 name = extension['extn_id'].native 

188 attribute_name = '_%s_value' % name 

189 if hasattr(self, attribute_name): 

190 setattr(self, attribute_name, extension['extn_value'].parsed) 

191 if extension['critical'].native: 

192 self._critical_extensions.add(name) 

193 

194 self._processed_extensions = True 

195 

196 @property 

197 def critical_extensions(self): 

198 """ 

199 Returns a set of the names (or OID if not a known extension) of the 

200 extensions marked as critical 

201 

202 :return: 

203 A set of unicode strings 

204 """ 

205 

206 if not self._processed_extensions: 

207 self._set_extensions() 

208 return self._critical_extensions 

209 

210 @property 

211 def crl_reason_value(self): 

212 """ 

213 This extension indicates the reason that a certificate was revoked. 

214 

215 :return: 

216 None or a CRLReason object 

217 """ 

218 

219 if self._processed_extensions is False: 

220 self._set_extensions() 

221 return self._crl_reason_value 

222 

223 @property 

224 def invalidity_date_value(self): 

225 """ 

226 This extension indicates the suspected date/time the private key was 

227 compromised or the certificate became invalid. This would usually be 

228 before the revocation date, which is when the CA processed the 

229 revocation. 

230 

231 :return: 

232 None or a GeneralizedTime object 

233 """ 

234 

235 if self._processed_extensions is False: 

236 self._set_extensions() 

237 return self._invalidity_date_value 

238 

239 @property 

240 def certificate_issuer_value(self): 

241 """ 

242 This extension indicates the issuer of the certificate in question, 

243 and is used in indirect CRLs. CRL entries without this extension are 

244 for certificates issued from the last seen issuer. 

245 

246 :return: 

247 None or an x509.GeneralNames object 

248 """ 

249 

250 if self._processed_extensions is False: 

251 self._set_extensions() 

252 return self._certificate_issuer_value 

253 

254 @property 

255 def issuer_name(self): 

256 """ 

257 :return: 

258 None, or an asn1crypto.x509.Name object for the issuer of the cert 

259 """ 

260 

261 if self._issuer_name is False: 

262 self._issuer_name = None 

263 if self.certificate_issuer_value: 

264 for general_name in self.certificate_issuer_value: 

265 if general_name.name == 'directory_name': 

266 self._issuer_name = general_name.chosen 

267 break 

268 return self._issuer_name 

269 

270 

271class RevokedCertificates(SequenceOf): 

272 _child_spec = RevokedCertificate 

273 

274 

275class TbsCertList(Sequence): 

276 _fields = [ 

277 ('version', Version, {'optional': True}), 

278 ('signature', SignedDigestAlgorithm), 

279 ('issuer', Name), 

280 ('this_update', Time), 

281 ('next_update', Time, {'optional': True}), 

282 ('revoked_certificates', RevokedCertificates, {'optional': True}), 

283 ('crl_extensions', TBSCertListExtensions, {'explicit': 0, 'optional': True}), 

284 ] 

285 

286 

287class CertificateList(Sequence): 

288 _fields = [ 

289 ('tbs_cert_list', TbsCertList), 

290 ('signature_algorithm', SignedDigestAlgorithm), 

291 ('signature', OctetBitString), 

292 ] 

293 

294 _processed_extensions = False 

295 _critical_extensions = None 

296 _issuer_alt_name_value = None 

297 _crl_number_value = None 

298 _delta_crl_indicator_value = None 

299 _issuing_distribution_point_value = None 

300 _authority_key_identifier_value = None 

301 _freshest_crl_value = None 

302 _authority_information_access_value = None 

303 _issuer_cert_urls = None 

304 _delta_crl_distribution_points = None 

305 _sha1 = None 

306 _sha256 = None 

307 

308 def _set_extensions(self): 

309 """ 

310 Sets common named extensions to private attributes and creates a list 

311 of critical extensions 

312 """ 

313 

314 self._critical_extensions = set() 

315 

316 for extension in self['tbs_cert_list']['crl_extensions']: 

317 name = extension['extn_id'].native 

318 attribute_name = '_%s_value' % name 

319 if hasattr(self, attribute_name): 

320 setattr(self, attribute_name, extension['extn_value'].parsed) 

321 if extension['critical'].native: 

322 self._critical_extensions.add(name) 

323 

324 self._processed_extensions = True 

325 

326 @property 

327 def critical_extensions(self): 

328 """ 

329 Returns a set of the names (or OID if not a known extension) of the 

330 extensions marked as critical 

331 

332 :return: 

333 A set of unicode strings 

334 """ 

335 

336 if not self._processed_extensions: 

337 self._set_extensions() 

338 return self._critical_extensions 

339 

340 @property 

341 def issuer_alt_name_value(self): 

342 """ 

343 This extension allows associating one or more alternative names with 

344 the issuer of the CRL. 

345 

346 :return: 

347 None or an x509.GeneralNames object 

348 """ 

349 

350 if self._processed_extensions is False: 

351 self._set_extensions() 

352 return self._issuer_alt_name_value 

353 

354 @property 

355 def crl_number_value(self): 

356 """ 

357 This extension adds a monotonically increasing number to the CRL and is 

358 used to distinguish different versions of the CRL. 

359 

360 :return: 

361 None or an Integer object 

362 """ 

363 

364 if self._processed_extensions is False: 

365 self._set_extensions() 

366 return self._crl_number_value 

367 

368 @property 

369 def delta_crl_indicator_value(self): 

370 """ 

371 This extension indicates a CRL is a delta CRL, and contains the CRL 

372 number of the base CRL that it is a delta from. 

373 

374 :return: 

375 None or an Integer object 

376 """ 

377 

378 if self._processed_extensions is False: 

379 self._set_extensions() 

380 return self._delta_crl_indicator_value 

381 

382 @property 

383 def issuing_distribution_point_value(self): 

384 """ 

385 This extension includes information about what types of revocations 

386 and certificates are part of the CRL. 

387 

388 :return: 

389 None or an IssuingDistributionPoint object 

390 """ 

391 

392 if self._processed_extensions is False: 

393 self._set_extensions() 

394 return self._issuing_distribution_point_value 

395 

396 @property 

397 def authority_key_identifier_value(self): 

398 """ 

399 This extension helps in identifying the public key with which to 

400 validate the authenticity of the CRL. 

401 

402 :return: 

403 None or an AuthorityKeyIdentifier object 

404 """ 

405 

406 if self._processed_extensions is False: 

407 self._set_extensions() 

408 return self._authority_key_identifier_value 

409 

410 @property 

411 def freshest_crl_value(self): 

412 """ 

413 This extension is used in complete CRLs to indicate where a delta CRL 

414 may be located. 

415 

416 :return: 

417 None or a CRLDistributionPoints object 

418 """ 

419 

420 if self._processed_extensions is False: 

421 self._set_extensions() 

422 return self._freshest_crl_value 

423 

424 @property 

425 def authority_information_access_value(self): 

426 """ 

427 This extension is used to provide a URL with which to download the 

428 certificate used to sign this CRL. 

429 

430 :return: 

431 None or an AuthorityInfoAccessSyntax object 

432 """ 

433 

434 if self._processed_extensions is False: 

435 self._set_extensions() 

436 return self._authority_information_access_value 

437 

438 @property 

439 def issuer(self): 

440 """ 

441 :return: 

442 An asn1crypto.x509.Name object for the issuer of the CRL 

443 """ 

444 

445 return self['tbs_cert_list']['issuer'] 

446 

447 @property 

448 def authority_key_identifier(self): 

449 """ 

450 :return: 

451 None or a byte string of the key_identifier from the authority key 

452 identifier extension 

453 """ 

454 

455 if not self.authority_key_identifier_value: 

456 return None 

457 

458 return self.authority_key_identifier_value['key_identifier'].native 

459 

460 @property 

461 def issuer_cert_urls(self): 

462 """ 

463 :return: 

464 A list of unicode strings that are URLs that should contain either 

465 an individual DER-encoded X.509 certificate, or a DER-encoded CMS 

466 message containing multiple certificates 

467 """ 

468 

469 if self._issuer_cert_urls is None: 

470 self._issuer_cert_urls = [] 

471 if self.authority_information_access_value: 

472 for entry in self.authority_information_access_value: 

473 if entry['access_method'].native == 'ca_issuers': 

474 location = entry['access_location'] 

475 if location.name != 'uniform_resource_identifier': 

476 continue 

477 url = location.native 

478 if url.lower()[0:7] == 'http://': 

479 self._issuer_cert_urls.append(url) 

480 return self._issuer_cert_urls 

481 

482 @property 

483 def delta_crl_distribution_points(self): 

484 """ 

485 Returns delta CRL URLs - only applies to complete CRLs 

486 

487 :return: 

488 A list of zero or more DistributionPoint objects 

489 """ 

490 

491 if self._delta_crl_distribution_points is None: 

492 self._delta_crl_distribution_points = [] 

493 

494 if self.freshest_crl_value is not None: 

495 for distribution_point in self.freshest_crl_value: 

496 distribution_point_name = distribution_point['distribution_point'] 

497 # RFC 5280 indicates conforming CA should not use the relative form 

498 if distribution_point_name.name == 'name_relative_to_crl_issuer': 

499 continue 

500 # This library is currently only concerned with HTTP-based CRLs 

501 for general_name in distribution_point_name.chosen: 

502 if general_name.name == 'uniform_resource_identifier': 

503 self._delta_crl_distribution_points.append(distribution_point) 

504 

505 return self._delta_crl_distribution_points 

506 

507 @property 

508 def signature(self): 

509 """ 

510 :return: 

511 A byte string of the signature 

512 """ 

513 

514 return self['signature'].native 

515 

516 @property 

517 def sha1(self): 

518 """ 

519 :return: 

520 The SHA1 hash of the DER-encoded bytes of this certificate list 

521 """ 

522 

523 if self._sha1 is None: 

524 self._sha1 = hashlib.sha1(self.dump()).digest() 

525 return self._sha1 

526 

527 @property 

528 def sha256(self): 

529 """ 

530 :return: 

531 The SHA-256 hash of the DER-encoded bytes of this certificate list 

532 """ 

533 

534 if self._sha256 is None: 

535 self._sha256 = hashlib.sha256(self.dump()).digest() 

536 return self._sha256