blob: 84cb1683936b33a0192cdd247a12dd695e03cdfd [file] [log] [blame]
wbonde91513e2015-06-03 14:52:18 -04001# coding: utf-8
wbondea25fc22015-06-19 15:07:04 -04002
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
wbond6b66ab52015-06-21 10:26:45 -040012from __future__ import unicode_literals, division, absolute_import, print_function
wbonde91513e2015-06-03 14:52:18 -040013
wbond9bcb1792015-10-20 00:48:37 -040014import hashlib
15
wbonde91513e2015-06-03 14:52:18 -040016from .algos import SignedDigestAlgorithm
17from .core import (
18 Boolean,
19 Enumerated,
20 GeneralizedTime,
21 Integer,
22 ObjectIdentifier,
23 OctetBitString,
wbonde5a1c6e2015-08-03 07:42:28 -040024 ParsableOctetString,
wbonde91513e2015-06-03 14:52:18 -040025 Sequence,
26 SequenceOf,
27)
28from .x509 import (
wbonda0d45482015-07-13 22:10:20 -040029 AuthorityInfoAccessSyntax,
wbonde91513e2015-06-03 14:52:18 -040030 AuthorityKeyIdentifier,
31 CRLDistributionPoints,
32 DistributionPointName,
wbonde91513e2015-06-03 14:52:18 -040033 GeneralNames,
34 Name,
35 ReasonFlags,
36 Time,
37)
38
39
wbonde91513e2015-06-03 14:52:18 -040040# 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 = [
wbondd62ed9a2017-09-15 07:13:52 -040053 ('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}),
wbonde91513e2015-06-03 14:52:18 -040059 ]
60
61
wbonde91513e2015-06-03 14:52:18 -040062class 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}),
wbonde5a1c6e2015-08-03 07:42:28 -040078 ('extn_value', ParsableOctetString),
wbonde91513e2015-06-03 14:52:18 -040079 ]
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
wbond2f1eb262015-07-20 08:52:34 -0400111 @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
wbonde91513e2015-06-03 14:52:18 -0400134
135class CRLEntryExtensionId(ObjectIdentifier):
136 _map = {
137 '2.5.29.21': 'crl_reason',
wbond0b7ffe72015-07-27 15:57:44 -0400138 '2.5.29.23': 'hold_instruction_code',
wbonde91513e2015-06-03 14:52:18 -0400139 '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}),
wbonde5a1c6e2015-08-03 07:42:28 -0400148 ('extn_value', ParsableOctetString),
wbonde91513e2015-06-03 14:52:18 -0400149 ]
150
151 _oid_pair = ('extn_id', 'extn_value')
152 _oid_specs = {
153 'crl_reason': CRLReason,
wbond0b7ffe72015-07-27 15:57:44 -0400154 'hold_instruction_code': ObjectIdentifier,
wbonde91513e2015-06-03 14:52:18 -0400155 '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
wbond598c2282015-07-20 08:54:22 -0400171 _processed_extensions = False
172 _critical_extensions = None
173 _crl_reason_value = None
174 _invalidity_date_value = None
175 _certificate_issuer_value = None
wbond5fb733f2015-07-21 14:57:59 -0400176 _issuer_name = False
wbond598c2282015-07-20 08:54:22 -0400177
178 def _set_extensions(self):
179 """
180 Sets common named extensions to private attributes and creates a list
181 of critical extensions
182 """
183
wbond2fde6452015-07-23 10:54:13 -0400184 self._critical_extensions = set()
wbond598c2282015-07-20 08:54:22 -0400185
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:
wbond2fde6452015-07-23 10:54:13 -0400192 self._critical_extensions.add(name)
wbond598c2282015-07-20 08:54:22 -0400193
194 self._processed_extensions = True
195
196 @property
197 def critical_extensions(self):
198 """
wbond2fde6452015-07-23 10:54:13 -0400199 Returns a set of the names (or OID if not a known extension) of the
wbond598c2282015-07-20 08:54:22 -0400200 extensions marked as critical
201
202 :return:
wbond2fde6452015-07-23 10:54:13 -0400203 A set of unicode strings
wbond598c2282015-07-20 08:54:22 -0400204 """
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:
wbondad218f92015-07-20 10:43:16 -0400220 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400221 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:
wbondad218f92015-07-20 10:43:16 -0400236 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400237 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:
wbondad218f92015-07-20 10:43:16 -0400251 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400252 return self._certificate_issuer_value
253
wbond5fb733f2015-07-21 14:57:59 -0400254 @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
wbonde91513e2015-06-03 14:52:18 -0400270
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),
Troy Rossbaa00e72017-05-17 16:24:38 -0600281 ('next_update', Time, {'optional': True}),
wbonde91513e2015-06-03 14:52:18 -0400282 ('revoked_certificates', RevokedCertificates, {'optional': True}),
wbondd62ed9a2017-09-15 07:13:52 -0400283 ('crl_extensions', TBSCertListExtensions, {'explicit': 0, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -0400284 ]
285
286
287class CertificateList(Sequence):
288 _fields = [
289 ('tbs_cert_list', TbsCertList),
wbond4a2627f2015-07-21 14:56:48 -0400290 ('signature_algorithm', SignedDigestAlgorithm),
wbonde91513e2015-06-03 14:52:18 -0400291 ('signature', OctetBitString),
292 ]
wbond598c2282015-07-20 08:54:22 -0400293
294 _processed_extensions = False
295 _critical_extensions = None
wbonda613f812015-07-20 10:13:47 -0400296 _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
wbond5fb733f2015-07-21 14:57:59 -0400303 _issuer_cert_urls = None
304 _delta_crl_distribution_points = None
wbond9bcb1792015-10-20 00:48:37 -0400305 _sha1 = None
306 _sha256 = None
wbond598c2282015-07-20 08:54:22 -0400307
308 def _set_extensions(self):
309 """
310 Sets common named extensions to private attributes and creates a list
311 of critical extensions
312 """
313
wbond2fde6452015-07-23 10:54:13 -0400314 self._critical_extensions = set()
wbond598c2282015-07-20 08:54:22 -0400315
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:
wbond2fde6452015-07-23 10:54:13 -0400322 self._critical_extensions.add(name)
wbond598c2282015-07-20 08:54:22 -0400323
324 self._processed_extensions = True
325
326 @property
327 def critical_extensions(self):
328 """
wbond2fde6452015-07-23 10:54:13 -0400329 Returns a set of the names (or OID if not a known extension) of the
wbond598c2282015-07-20 08:54:22 -0400330 extensions marked as critical
331
332 :return:
wbond2fde6452015-07-23 10:54:13 -0400333 A set of unicode strings
wbond598c2282015-07-20 08:54:22 -0400334 """
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:
wbondad218f92015-07-20 10:43:16 -0400351 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400352 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:
wbondad218f92015-07-20 10:43:16 -0400365 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400366 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:
wbondad218f92015-07-20 10:43:16 -0400379 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400380 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:
wbondad218f92015-07-20 10:43:16 -0400393 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400394 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:
wbondad218f92015-07-20 10:43:16 -0400407 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400408 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:
wbondad218f92015-07-20 10:43:16 -0400421 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400422 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:
wbondad218f92015-07-20 10:43:16 -0400435 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400436 return self._authority_information_access_value
wbond5fb733f2015-07-21 14:57:59 -0400437
438 @property
wbond7a5c0152015-07-24 08:37:53 -0400439 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
wbond5fb733f2015-07-21 14:57:59 -0400448 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
wbondd913db52015-08-06 12:44:15 -0400465 an individual DER-encoded X.509 certificate, or a DER-encoded CMS
wbond5fb733f2015-07-21 14:57:59 -0400466 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']
wbondd913db52015-08-06 12:44:15 -0400497 # RFC 5280 indicates conforming CA should not use the relative form
wbond5fb733f2015-07-21 14:57:59 -0400498 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
wbond9bcb1792015-10-20 00:48:37 -0400506
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