blob: b83d99eae161891b6a3ffb0b26dbb9994e846325 [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,
22 OctetString,
23 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}),
77 ('extn_value', OctetString),
78 ]
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',
137 '2.5.29.24': 'invalidity_date',
138 '2.5.29.29': 'certificate_issuer',
139 }
140
141
142class CRLEntryExtension(Sequence):
143 _fields = [
144 ('extn_id', CRLEntryExtensionId),
145 ('critical', Boolean, {'default': False}),
146 ('extn_value', OctetString),
147 ]
148
149 _oid_pair = ('extn_id', 'extn_value')
150 _oid_specs = {
151 'crl_reason': CRLReason,
152 'invalidity_date': GeneralizedTime,
153 'certificate_issuer': GeneralNames,
154 }
155
156
157class CRLEntryExtensions(SequenceOf):
158 _child_spec = CRLEntryExtension
159
160
161class RevokedCertificate(Sequence):
162 _fields = [
163 ('user_certificate', Integer),
164 ('revocation_date', Time),
165 ('crl_entry_extensions', CRLEntryExtensions, {'optional': True}),
166 ]
167
wbond598c2282015-07-20 08:54:22 -0400168 _processed_extensions = False
169 _critical_extensions = None
170 _crl_reason_value = None
171 _invalidity_date_value = None
172 _certificate_issuer_value = None
wbond5fb733f2015-07-21 14:57:59 -0400173 _issuer_name = False
wbond598c2282015-07-20 08:54:22 -0400174
175 def _set_extensions(self):
176 """
177 Sets common named extensions to private attributes and creates a list
178 of critical extensions
179 """
180
wbond2fde6452015-07-23 10:54:13 -0400181 self._critical_extensions = set()
wbond598c2282015-07-20 08:54:22 -0400182
183 for extension in self['crl_entry_extensions']:
184 name = extension['extn_id'].native
185 attribute_name = '_%s_value' % name
186 if hasattr(self, attribute_name):
187 setattr(self, attribute_name, extension['extn_value'].parsed)
188 if extension['critical'].native:
wbond2fde6452015-07-23 10:54:13 -0400189 self._critical_extensions.add(name)
wbond598c2282015-07-20 08:54:22 -0400190
191 self._processed_extensions = True
192
193 @property
194 def critical_extensions(self):
195 """
wbond2fde6452015-07-23 10:54:13 -0400196 Returns a set of the names (or OID if not a known extension) of the
wbond598c2282015-07-20 08:54:22 -0400197 extensions marked as critical
198
199 :return:
wbond2fde6452015-07-23 10:54:13 -0400200 A set of unicode strings
wbond598c2282015-07-20 08:54:22 -0400201 """
202
203 if not self._processed_extensions:
204 self._set_extensions()
205 return self._critical_extensions
206
207 @property
208 def crl_reason_value(self):
209 """
210 This extension indicates the reason that a certificate was revoked.
211
212 :return:
213 None or a CRLReason object
214 """
215
216 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400217 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400218 return self._crl_reason_value
219
220 @property
221 def invalidity_date_value(self):
222 """
223 This extension indicates the suspected date/time the private key was
224 compromised or the certificate became invalid. This would usually be
225 before the revocation date, which is when the CA processed the
226 revocation.
227
228 :return:
229 None or a GeneralizedTime object
230 """
231
232 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400233 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400234 return self._invalidity_date_value
235
236 @property
237 def certificate_issuer_value(self):
238 """
239 This extension indicates the issuer of the certificate in question,
240 and is used in indirect CRLs. CRL entries without this extension are
241 for certificates issued from the last seen issuer.
242
243 :return:
244 None or an x509.GeneralNames object
245 """
246
247 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400248 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400249 return self._certificate_issuer_value
250
wbond5fb733f2015-07-21 14:57:59 -0400251 @property
252 def issuer_name(self):
253 """
254 :return:
255 None, or an asn1crypto.x509.Name object for the issuer of the cert
256 """
257
258 if self._issuer_name is False:
259 self._issuer_name = None
260 if self.certificate_issuer_value:
261 for general_name in self.certificate_issuer_value:
262 if general_name.name == 'directory_name':
263 self._issuer_name = general_name.chosen
264 break
265 return self._issuer_name
266
wbonde91513e2015-06-03 14:52:18 -0400267
268class RevokedCertificates(SequenceOf):
269 _child_spec = RevokedCertificate
270
271
272class TbsCertList(Sequence):
273 _fields = [
274 ('version', Version, {'optional': True}),
275 ('signature', SignedDigestAlgorithm),
276 ('issuer', Name),
277 ('this_update', Time),
278 ('next_update', Time),
279 ('revoked_certificates', RevokedCertificates, {'optional': True}),
280 ('crl_extensions', TBSCertListExtensions, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
281 ]
282
283
284class CertificateList(Sequence):
285 _fields = [
286 ('tbs_cert_list', TbsCertList),
wbond4a2627f2015-07-21 14:56:48 -0400287 ('signature_algorithm', SignedDigestAlgorithm),
wbonde91513e2015-06-03 14:52:18 -0400288 ('signature', OctetBitString),
289 ]
wbond598c2282015-07-20 08:54:22 -0400290
291 _processed_extensions = False
292 _critical_extensions = None
wbonda613f812015-07-20 10:13:47 -0400293 _issuer_alt_name_value = None
294 _crl_number_value = None
295 _delta_crl_indicator_value = None
296 _issuing_distribution_point_value = None
297 _authority_key_identifier_value = None
298 _freshest_crl_value = None
299 _authority_information_access_value = None
wbond5fb733f2015-07-21 14:57:59 -0400300 _issuer_cert_urls = None
301 _delta_crl_distribution_points = None
wbond598c2282015-07-20 08:54:22 -0400302
303 def _set_extensions(self):
304 """
305 Sets common named extensions to private attributes and creates a list
306 of critical extensions
307 """
308
wbond2fde6452015-07-23 10:54:13 -0400309 self._critical_extensions = set()
wbond598c2282015-07-20 08:54:22 -0400310
311 for extension in self['tbs_cert_list']['crl_extensions']:
312 name = extension['extn_id'].native
313 attribute_name = '_%s_value' % name
314 if hasattr(self, attribute_name):
315 setattr(self, attribute_name, extension['extn_value'].parsed)
316 if extension['critical'].native:
wbond2fde6452015-07-23 10:54:13 -0400317 self._critical_extensions.add(name)
wbond598c2282015-07-20 08:54:22 -0400318
319 self._processed_extensions = True
320
321 @property
322 def critical_extensions(self):
323 """
wbond2fde6452015-07-23 10:54:13 -0400324 Returns a set of the names (or OID if not a known extension) of the
wbond598c2282015-07-20 08:54:22 -0400325 extensions marked as critical
326
327 :return:
wbond2fde6452015-07-23 10:54:13 -0400328 A set of unicode strings
wbond598c2282015-07-20 08:54:22 -0400329 """
330
331 if not self._processed_extensions:
332 self._set_extensions()
333 return self._critical_extensions
334
335 @property
336 def issuer_alt_name_value(self):
337 """
338 This extension allows associating one or more alternative names with
339 the issuer of the CRL.
340
341 :return:
342 None or an x509.GeneralNames object
343 """
344
345 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400346 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400347 return self._issuer_alt_name_value
348
349 @property
350 def crl_number_value(self):
351 """
352 This extension adds a monotonically increasing number to the CRL and is
353 used to distinguish different versions of the CRL.
354
355 :return:
356 None or an Integer object
357 """
358
359 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400360 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400361 return self._crl_number_value
362
363 @property
364 def delta_crl_indicator_value(self):
365 """
366 This extension indicates a CRL is a delta CRL, and contains the CRL
367 number of the base CRL that it is a delta from.
368
369 :return:
370 None or an Integer object
371 """
372
373 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400374 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400375 return self._delta_crl_indicator_value
376
377 @property
378 def issuing_distribution_point_value(self):
379 """
380 This extension includes information about what types of revocations
381 and certificates are part of the CRL.
382
383 :return:
384 None or an IssuingDistributionPoint object
385 """
386
387 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400388 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400389 return self._issuing_distribution_point_value
390
391 @property
392 def authority_key_identifier_value(self):
393 """
394 This extension helps in identifying the public key with which to
395 validate the authenticity of the CRL.
396
397 :return:
398 None or an AuthorityKeyIdentifier object
399 """
400
401 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400402 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400403 return self._authority_key_identifier_value
404
405 @property
406 def freshest_crl_value(self):
407 """
408 This extension is used in complete CRLs to indicate where a delta CRL
409 may be located.
410
411 :return:
412 None or a CRLDistributionPoints object
413 """
414
415 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400416 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400417 return self._freshest_crl_value
418
419 @property
420 def authority_information_access_value(self):
421 """
422 This extension is used to provide a URL with which to download the
423 certificate used to sign this CRL.
424
425 :return:
426 None or an AuthorityInfoAccessSyntax object
427 """
428
429 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400430 self._set_extensions()
wbond598c2282015-07-20 08:54:22 -0400431 return self._authority_information_access_value
wbond5fb733f2015-07-21 14:57:59 -0400432
433 @property
wbond7a5c0152015-07-24 08:37:53 -0400434 def issuer(self):
435 """
436 :return:
437 An asn1crypto.x509.Name object for the issuer of the CRL
438 """
439
440 return self['tbs_cert_list']['issuer']
441
442 @property
wbond5fb733f2015-07-21 14:57:59 -0400443 def authority_key_identifier(self):
444 """
445 :return:
446 None or a byte string of the key_identifier from the authority key
447 identifier extension
448 """
449
450 if not self.authority_key_identifier_value:
451 return None
452
453 return self.authority_key_identifier_value['key_identifier'].native
454
455 @property
456 def issuer_cert_urls(self):
457 """
458 :return:
459 A list of unicode strings that are URLs that should contain either
460 an individual DER-encoded X509 certificate, or a DER-encoded CMS
461 message containing multiple certificates
462 """
463
464 if self._issuer_cert_urls is None:
465 self._issuer_cert_urls = []
466 if self.authority_information_access_value:
467 for entry in self.authority_information_access_value:
468 if entry['access_method'].native == 'ca_issuers':
469 location = entry['access_location']
470 if location.name != 'uniform_resource_identifier':
471 continue
472 url = location.native
473 if url.lower()[0:7] == 'http://':
474 self._issuer_cert_urls.append(url)
475 return self._issuer_cert_urls
476
477 @property
478 def delta_crl_distribution_points(self):
479 """
480 Returns delta CRL URLs - only applies to complete CRLs
481
482 :return:
483 A list of zero or more DistributionPoint objects
484 """
485
486 if self._delta_crl_distribution_points is None:
487 self._delta_crl_distribution_points = []
488
489 if self.freshest_crl_value is not None:
490 for distribution_point in self.freshest_crl_value:
491 distribution_point_name = distribution_point['distribution_point']
492 # RFC5280 indicates conforming CA should not use the relative form
493 if distribution_point_name.name == 'name_relative_to_crl_issuer':
494 continue
495 # This library is currently only concerned with HTTP-based CRLs
496 for general_name in distribution_point_name.chosen:
497 if general_name.name == 'uniform_resource_identifier':
498 self._delta_crl_distribution_points.append(distribution_point)
499
500 return self._delta_crl_distribution_points