blob: a2be5bfd34abc2d3ab02a75f896cfbc6258f1697 [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
14from .algos import SignedDigestAlgorithm
15from .core import (
16 Boolean,
17 Enumerated,
18 GeneralizedTime,
19 Integer,
20 ObjectIdentifier,
21 OctetBitString,
wbonde5a1c6e2015-08-03 07:42:28 -040022 ParsableOctetString,
wbonde91513e2015-06-03 14:52:18 -040023 Sequence,
24 SequenceOf,
25)
26from .x509 import (
wbonda0d45482015-07-13 22:10:20 -040027 AuthorityInfoAccessSyntax,
wbonde91513e2015-06-03 14:52:18 -040028 AuthorityKeyIdentifier,
29 CRLDistributionPoints,
30 DistributionPointName,
wbonde91513e2015-06-03 14:52:18 -040031 GeneralNames,
32 Name,
33 ReasonFlags,
34 Time,
35)
36
37
38
39# The structures in this file are taken from https://tools.ietf.org/html/rfc5280
40
41
42class Version(Integer):
43 _map = {
44 0: 'v1',
45 1: 'v2',
46 2: 'v3',
47 }
48
49
50class IssuingDistributionPoint(Sequence):
51 _fields = [
52 ('distribution_point', DistributionPointName, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
53 ('only_contains_user_certs', Boolean, {'tag_type': 'implicit', 'tag': 1, 'default': False}),
54 ('only_contains_ca_certs', Boolean, {'tag_type': 'implicit', 'tag': 2, 'default': False}),
55 ('only_some_reasons', ReasonFlags, {'tag_type': 'implicit', 'tag': 3, 'optional': True}),
56 ('indirect_crl', Boolean, {'tag_type': 'implicit', 'tag': 4, 'default': False}),
57 ('only_contains_attribute_certs', Boolean, {'tag_type': 'implicit', 'tag': 5, 'default': False}),
58 ]
59
60
wbonde91513e2015-06-03 14:52:18 -040061class TBSCertListExtensionId(ObjectIdentifier):
62 _map = {
63 '2.5.29.18': 'issuer_alt_name',
64 '2.5.29.20': 'crl_number',
65 '2.5.29.27': 'delta_crl_indicator',
66 '2.5.29.28': 'issuing_distribution_point',
67 '2.5.29.35': 'authority_key_identifier',
68 '2.5.29.46': 'freshest_crl',
69 '1.3.6.1.5.5.7.1.1': 'authority_information_access',
70 }
71
72
73class TBSCertListExtension(Sequence):
74 _fields = [
75 ('extn_id', TBSCertListExtensionId),
76 ('critical', Boolean, {'default': False}),
wbonde5a1c6e2015-08-03 07:42:28 -040077 ('extn_value', ParsableOctetString),
wbonde91513e2015-06-03 14:52:18 -040078 ]
79
80 _oid_pair = ('extn_id', 'extn_value')
81 _oid_specs = {
82 'issuer_alt_name': GeneralNames,
83 'crl_number': Integer,
84 'delta_crl_indicator': Integer,
85 'issuing_distribution_point': IssuingDistributionPoint,
86 'authority_key_identifier': AuthorityKeyIdentifier,
87 'freshest_crl': CRLDistributionPoints,
88 'authority_information_access': AuthorityInfoAccessSyntax,
89 }
90
91
92class TBSCertListExtensions(SequenceOf):
93 _child_spec = TBSCertListExtension
94
95
96class CRLReason(Enumerated):
97 _map = {
98 0: 'unspecified',
99 1: 'key_compromise',
100 2: 'ca_compromise',
101 3: 'affiliation_changed',
102 4: 'superseded',
103 5: 'cessation_of_operation',
104 6: 'certificate_hold',
105 8: 'remove_from_crl',
106 9: 'privilege_withdrawn',
107 10: 'aa_compromise',
108 }
109
wbond2f1eb262015-07-20 08:52:34 -0400110 @property
111 def human_friendly(self):
112 """
113 :return:
114 A unicode string with revocation description that is suitable to
115 show to end-users. Starts with a lower case letter and phrased in
116 such a way that it makes sense after the phrase "because of" or
117 "due to".
118 """
119
120 return {
121 'unspecified': 'an unspecified reason',
122 'key_compromise': 'a compromised key',
123 'ca_compromise': 'the CA being compromised',
124 'affiliation_changed': 'an affiliation change',
125 'superseded': 'certificate supersession',
126 'cessation_of_operation': 'a cessation of operation',
127 'certificate_hold': 'a certificate hold',
128 'remove_from_crl': 'removal from the CRL',
129 'privilege_withdrawn': 'privilege withdrawl',
130 'aa_compromise': 'the AA being compromised',
131 }[self.native]
132
wbonde91513e2015-06-03 14:52:18 -0400133
134class CRLEntryExtensionId(ObjectIdentifier):
135 _map = {
136 '2.5.29.21': 'crl_reason',
wbond0b7ffe72015-07-27 15:57:44 -0400137 '2.5.29.23': 'hold_instruction_code',
wbonde91513e2015-06-03 14:52:18 -0400138 '2.5.29.24': 'invalidity_date',
139 '2.5.29.29': 'certificate_issuer',
140 }
141
142
143class CRLEntryExtension(Sequence):
144 _fields = [
145 ('extn_id', CRLEntryExtensionId),
146 ('critical', Boolean, {'default': False}),
wbonde5a1c6e2015-08-03 07:42:28 -0400147 ('extn_value', ParsableOctetString),
wbonde91513e2015-06-03 14:52:18 -0400148 ]
149
150 _oid_pair = ('extn_id', 'extn_value')
151 _oid_specs = {
152 'crl_reason': CRLReason,
wbond0b7ffe72015-07-27 15:57:44 -0400153 'hold_instruction_code': ObjectIdentifier,
wbonde91513e2015-06-03 14:52:18 -0400154 'invalidity_date': GeneralizedTime,
155 'certificate_issuer': GeneralNames,
156 }
157
158
159class CRLEntryExtensions(SequenceOf):
160 _child_spec = CRLEntryExtension
161
162
163class RevokedCertificate(Sequence):
164 _fields = [
165 ('user_certificate', Integer),
166 ('revocation_date', Time),
167 ('crl_entry_extensions', CRLEntryExtensions, {'optional': True}),
168 ]
169
wbond598c2282015-07-20 08:54:22 -0400170 _processed_extensions = False
171 _critical_extensions = None
172 _crl_reason_value = None
173 _invalidity_date_value = None
174 _certificate_issuer_value = None
wbond5fb733f2015-07-21 14:57:59 -0400175 _issuer_name = False
wbond598c2282015-07-20 08:54:22 -0400176
177 def _set_extensions(self):
178 """
179 Sets common named extensions to private attributes and creates a list
180 of critical extensions
181 """
182
wbond2fde6452015-07-23 10:54:13 -0400183 self._critical_extensions = set()
wbond598c2282015-07-20 08:54:22 -0400184
185 for extension in self['crl_entry_extensions']:
186 name = extension['extn_id'].native
187 attribute_name = '_%s_value' % name
188 if hasattr(self, attribute_name):
189 setattr(self, attribute_name, extension['extn_value'].parsed)
190 if extension['critical'].native:
wbond2fde6452015-07-23 10:54:13 -0400191 self._critical_extensions.add(name)
wbond598c2282015-07-20 08:54:22 -0400192
193 self._processed_extensions = True
194
195 @property
196 def critical_extensions(self):
197 """
wbond2fde6452015-07-23 10:54:13 -0400198 Returns a set of the names (or OID if not a known extension) of the
wbond598c2282015-07-20 08:54:22 -0400199 extensions marked as critical
200
201 :return:
wbond2fde6452015-07-23 10:54:13 -0400202 A set of unicode strings
wbond598c2282015-07-20 08:54:22 -0400203 """
204
205 if not self._processed_extensions:
206 self._set_extensions()
207 return self._critical_extensions
208
209 @property
210 def crl_reason_value(self):
211 """
212 This extension indicates the reason that a certificate was revoked.
213
214 :return:
215 None or a CRLReason object
216 """
217
218 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400219 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400220 return self._crl_reason_value
221
222 @property
223 def invalidity_date_value(self):
224 """
225 This extension indicates the suspected date/time the private key was
226 compromised or the certificate became invalid. This would usually be
227 before the revocation date, which is when the CA processed the
228 revocation.
229
230 :return:
231 None or a GeneralizedTime object
232 """
233
234 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400235 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400236 return self._invalidity_date_value
237
238 @property
239 def certificate_issuer_value(self):
240 """
241 This extension indicates the issuer of the certificate in question,
242 and is used in indirect CRLs. CRL entries without this extension are
243 for certificates issued from the last seen issuer.
244
245 :return:
246 None or an x509.GeneralNames object
247 """
248
249 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400250 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400251 return self._certificate_issuer_value
252
wbond5fb733f2015-07-21 14:57:59 -0400253 @property
254 def issuer_name(self):
255 """
256 :return:
257 None, or an asn1crypto.x509.Name object for the issuer of the cert
258 """
259
260 if self._issuer_name is False:
261 self._issuer_name = None
262 if self.certificate_issuer_value:
263 for general_name in self.certificate_issuer_value:
264 if general_name.name == 'directory_name':
265 self._issuer_name = general_name.chosen
266 break
267 return self._issuer_name
268
wbonde91513e2015-06-03 14:52:18 -0400269
270class RevokedCertificates(SequenceOf):
271 _child_spec = RevokedCertificate
272
273
274class TbsCertList(Sequence):
275 _fields = [
276 ('version', Version, {'optional': True}),
277 ('signature', SignedDigestAlgorithm),
278 ('issuer', Name),
279 ('this_update', Time),
280 ('next_update', Time),
281 ('revoked_certificates', RevokedCertificates, {'optional': True}),
282 ('crl_extensions', TBSCertListExtensions, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
283 ]
284
285
286class CertificateList(Sequence):
287 _fields = [
288 ('tbs_cert_list', TbsCertList),
wbond4a2627f2015-07-21 14:56:48 -0400289 ('signature_algorithm', SignedDigestAlgorithm),
wbonde91513e2015-06-03 14:52:18 -0400290 ('signature', OctetBitString),
291 ]
wbond598c2282015-07-20 08:54:22 -0400292
293 _processed_extensions = False
294 _critical_extensions = None
wbonda613f812015-07-20 10:13:47 -0400295 _issuer_alt_name_value = None
296 _crl_number_value = None
297 _delta_crl_indicator_value = None
298 _issuing_distribution_point_value = None
299 _authority_key_identifier_value = None
300 _freshest_crl_value = None
301 _authority_information_access_value = None
wbond5fb733f2015-07-21 14:57:59 -0400302 _issuer_cert_urls = None
303 _delta_crl_distribution_points = None
wbond598c2282015-07-20 08:54:22 -0400304
305 def _set_extensions(self):
306 """
307 Sets common named extensions to private attributes and creates a list
308 of critical extensions
309 """
310
wbond2fde6452015-07-23 10:54:13 -0400311 self._critical_extensions = set()
wbond598c2282015-07-20 08:54:22 -0400312
313 for extension in self['tbs_cert_list']['crl_extensions']:
314 name = extension['extn_id'].native
315 attribute_name = '_%s_value' % name
316 if hasattr(self, attribute_name):
317 setattr(self, attribute_name, extension['extn_value'].parsed)
318 if extension['critical'].native:
wbond2fde6452015-07-23 10:54:13 -0400319 self._critical_extensions.add(name)
wbond598c2282015-07-20 08:54:22 -0400320
321 self._processed_extensions = True
322
323 @property
324 def critical_extensions(self):
325 """
wbond2fde6452015-07-23 10:54:13 -0400326 Returns a set of the names (or OID if not a known extension) of the
wbond598c2282015-07-20 08:54:22 -0400327 extensions marked as critical
328
329 :return:
wbond2fde6452015-07-23 10:54:13 -0400330 A set of unicode strings
wbond598c2282015-07-20 08:54:22 -0400331 """
332
333 if not self._processed_extensions:
334 self._set_extensions()
335 return self._critical_extensions
336
337 @property
338 def issuer_alt_name_value(self):
339 """
340 This extension allows associating one or more alternative names with
341 the issuer of the CRL.
342
343 :return:
344 None or an x509.GeneralNames object
345 """
346
347 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400348 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400349 return self._issuer_alt_name_value
350
351 @property
352 def crl_number_value(self):
353 """
354 This extension adds a monotonically increasing number to the CRL and is
355 used to distinguish different versions of the CRL.
356
357 :return:
358 None or an Integer object
359 """
360
361 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400362 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400363 return self._crl_number_value
364
365 @property
366 def delta_crl_indicator_value(self):
367 """
368 This extension indicates a CRL is a delta CRL, and contains the CRL
369 number of the base CRL that it is a delta from.
370
371 :return:
372 None or an Integer object
373 """
374
375 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400376 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400377 return self._delta_crl_indicator_value
378
379 @property
380 def issuing_distribution_point_value(self):
381 """
382 This extension includes information about what types of revocations
383 and certificates are part of the CRL.
384
385 :return:
386 None or an IssuingDistributionPoint object
387 """
388
389 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400390 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400391 return self._issuing_distribution_point_value
392
393 @property
394 def authority_key_identifier_value(self):
395 """
396 This extension helps in identifying the public key with which to
397 validate the authenticity of the CRL.
398
399 :return:
400 None or an AuthorityKeyIdentifier object
401 """
402
403 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400404 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400405 return self._authority_key_identifier_value
406
407 @property
408 def freshest_crl_value(self):
409 """
410 This extension is used in complete CRLs to indicate where a delta CRL
411 may be located.
412
413 :return:
414 None or a CRLDistributionPoints object
415 """
416
417 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400418 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400419 return self._freshest_crl_value
420
421 @property
422 def authority_information_access_value(self):
423 """
424 This extension is used to provide a URL with which to download the
425 certificate used to sign this CRL.
426
427 :return:
428 None or an AuthorityInfoAccessSyntax object
429 """
430
431 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400432 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400433 return self._authority_information_access_value
wbond5fb733f2015-07-21 14:57:59 -0400434
435 @property
wbond7a5c0152015-07-24 08:37:53 -0400436 def issuer(self):
437 """
438 :return:
439 An asn1crypto.x509.Name object for the issuer of the CRL
440 """
441
442 return self['tbs_cert_list']['issuer']
443
444 @property
wbond5fb733f2015-07-21 14:57:59 -0400445 def authority_key_identifier(self):
446 """
447 :return:
448 None or a byte string of the key_identifier from the authority key
449 identifier extension
450 """
451
452 if not self.authority_key_identifier_value:
453 return None
454
455 return self.authority_key_identifier_value['key_identifier'].native
456
457 @property
458 def issuer_cert_urls(self):
459 """
460 :return:
461 A list of unicode strings that are URLs that should contain either
wbondd913db52015-08-06 12:44:15 -0400462 an individual DER-encoded X.509 certificate, or a DER-encoded CMS
wbond5fb733f2015-07-21 14:57:59 -0400463 message containing multiple certificates
464 """
465
466 if self._issuer_cert_urls is None:
467 self._issuer_cert_urls = []
468 if self.authority_information_access_value:
469 for entry in self.authority_information_access_value:
470 if entry['access_method'].native == 'ca_issuers':
471 location = entry['access_location']
472 if location.name != 'uniform_resource_identifier':
473 continue
474 url = location.native
475 if url.lower()[0:7] == 'http://':
476 self._issuer_cert_urls.append(url)
477 return self._issuer_cert_urls
478
479 @property
480 def delta_crl_distribution_points(self):
481 """
482 Returns delta CRL URLs - only applies to complete CRLs
483
484 :return:
485 A list of zero or more DistributionPoint objects
486 """
487
488 if self._delta_crl_distribution_points is None:
489 self._delta_crl_distribution_points = []
490
491 if self.freshest_crl_value is not None:
492 for distribution_point in self.freshest_crl_value:
493 distribution_point_name = distribution_point['distribution_point']
wbondd913db52015-08-06 12:44:15 -0400494 # RFC 5280 indicates conforming CA should not use the relative form
wbond5fb733f2015-07-21 14:57:59 -0400495 if distribution_point_name.name == 'name_relative_to_crl_issuer':
496 continue
497 # This library is currently only concerned with HTTP-based CRLs
498 for general_name in distribution_point_name.chosen:
499 if general_name.name == 'uniform_resource_identifier':
500 self._delta_crl_distribution_points.append(distribution_point)
501
502 return self._delta_crl_distribution_points