blob: 8ce57352a2414686512ea2a89eb714e0dfe5cc53 [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 the online certificate status protocol (OCSP). Exports
5the following items:
6
7 - OCSPRequest()
8 - OCSPResponse()
9
10Other type classes are defined that help compose the types listed above.
11"""
12
wbond6b66ab52015-06-21 10:26:45 -040013from __future__ import unicode_literals, division, absolute_import, print_function
wbonde91513e2015-06-03 14:52:18 -040014
15from .algos import DigestAlgorithm, SignedDigestAlgorithm
16from .core import (
17 Boolean,
18 Choice,
19 Enumerated,
20 GeneralizedTime,
21 IA5String,
22 Integer,
23 Null,
24 ObjectIdentifier,
25 OctetBitString,
26 OctetString,
wbonde5a1c6e2015-08-03 07:42:28 -040027 ParsableOctetString,
wbonde91513e2015-06-03 14:52:18 -040028 Sequence,
29 SequenceOf,
30)
31from .crl import AuthorityInfoAccessSyntax, CRLReason
32from .keys import PublicKeyAlgorithm
33from .x509 import Certificate, GeneralName, GeneralNames, Name
34
35
36
37# The structures in this file are taken from https://tools.ietf.org/html/rfc6960
38
39
wbond90ec1302015-07-20 09:10:50 -040040class Version(Integer):
wbonde91513e2015-06-03 14:52:18 -040041 _map = {
wbond90ec1302015-07-20 09:10:50 -040042 0: 'v1'
wbonde91513e2015-06-03 14:52:18 -040043 }
44
45
wbond90ec1302015-07-20 09:10:50 -040046class CertId(Sequence):
47 _fields = [
48 ('hash_algorithm', DigestAlgorithm),
49 ('issuer_name_hash', OctetString),
50 ('issuer_key_hash', OctetString),
51 ('serial_number', Integer),
52 ]
wbonde91513e2015-06-03 14:52:18 -040053
54
55class ServiceLocator(Sequence):
56 _fields = [
57 ('issuer', Name),
58 ('locator', AuthorityInfoAccessSyntax),
59 ]
60
61
wbonde91513e2015-06-03 14:52:18 -040062class RequestExtensionId(ObjectIdentifier):
63 _map = {
wbond65593fe2015-07-20 10:14:50 -040064 '1.3.6.1.5.5.7.48.1.7': 'service_locator',
wbonde91513e2015-06-03 14:52:18 -040065 }
66
67
68class RequestExtension(Sequence):
69 _fields = [
70 ('extn_id', RequestExtensionId),
71 ('critical', Boolean, {'default': False}),
wbonde5a1c6e2015-08-03 07:42:28 -040072 ('extn_value', ParsableOctetString),
wbonde91513e2015-06-03 14:52:18 -040073 ]
74
75 _oid_pair = ('extn_id', 'extn_value')
76 _oid_specs = {
wbond65593fe2015-07-20 10:14:50 -040077 'service_locator': ServiceLocator,
wbonde91513e2015-06-03 14:52:18 -040078 }
79
80
81class RequestExtensions(SequenceOf):
82 _child_spec = RequestExtension
83
84
wbond90ec1302015-07-20 09:10:50 -040085class Request(Sequence):
86 _fields = [
87 ('req_cert', CertId),
88 ('single_request_extensions', RequestExtensions, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
89 ]
90
wbondbcb62642015-07-20 10:16:27 -040091 _processed_extensions = False
92 _critical_extensions = None
93 _service_locator_value = None
94
95 def _set_extensions(self):
96 """
97 Sets common named extensions to private attributes and creates a list
98 of critical extensions
99 """
100
wbond2fde6452015-07-23 10:54:13 -0400101 self._critical_extensions = set()
wbondbcb62642015-07-20 10:16:27 -0400102
103 for extension in self['single_request_extensions']:
104 name = extension['extn_id'].native
105 attribute_name = '_%s_value' % name
106 if hasattr(self, attribute_name):
107 setattr(self, attribute_name, extension['extn_value'].parsed)
108 if extension['critical'].native:
wbond2fde6452015-07-23 10:54:13 -0400109 self._critical_extensions.add(name)
wbondbcb62642015-07-20 10:16:27 -0400110
111 self._processed_extensions = True
112
113 @property
114 def critical_extensions(self):
115 """
wbond2fde6452015-07-23 10:54:13 -0400116 Returns a set of the names (or OID if not a known extension) of the
wbondbcb62642015-07-20 10:16:27 -0400117 extensions marked as critical
118
119 :return:
wbond2fde6452015-07-23 10:54:13 -0400120 A set of unicode strings
wbondbcb62642015-07-20 10:16:27 -0400121 """
122
123 if not self._processed_extensions:
124 self._set_extensions()
125 return self._critical_extensions
126
127 @property
128 def service_locator_value(self):
129 """
130 This extension is used when communicating with an OCSP responder that
131 acts as a proxy for OCSP requests
132
133 :return:
134 None or a ServiceLocator object
135 """
136
137 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400138 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400139 return self._service_locator_value
140
wbond90ec1302015-07-20 09:10:50 -0400141
142class Requests(SequenceOf):
143 _child_spec = Request
144
145
146class ResponseType(ObjectIdentifier):
147 _map = {
148 '1.3.6.1.5.5.7.48.1.1': 'basic_ocsp_response',
149 }
150
151
152class AcceptableResponses(SequenceOf):
153 _child_spec = ResponseType
154
155
156class PreferredSignatureAlgorithm(Sequence):
157 _fields = [
158 ('sig_identifier', SignedDigestAlgorithm),
159 ('cert_identifier', PublicKeyAlgorithm, {'optional': True}),
160 ]
161
162
163class PreferredSignatureAlgorithms(SequenceOf):
164 _child_spec = PreferredSignatureAlgorithm
165
166
wbonde91513e2015-06-03 14:52:18 -0400167class TBSRequestExtensionId(ObjectIdentifier):
168 _map = {
wbond65593fe2015-07-20 10:14:50 -0400169 '1.3.6.1.5.5.7.48.1.2': 'nonce',
170 '1.3.6.1.5.5.7.48.1.4': 'acceptable_responses',
171 '1.3.6.1.5.5.7.48.1.8': 'preferred_signature_algorithms',
wbonde91513e2015-06-03 14:52:18 -0400172 }
173
174
175class TBSRequestExtension(Sequence):
176 _fields = [
177 ('extn_id', TBSRequestExtensionId),
178 ('critical', Boolean, {'default': False}),
wbonde5a1c6e2015-08-03 07:42:28 -0400179 ('extn_value', ParsableOctetString),
wbonde91513e2015-06-03 14:52:18 -0400180 ]
181
182 _oid_pair = ('extn_id', 'extn_value')
183 _oid_specs = {
wbond65593fe2015-07-20 10:14:50 -0400184 'nonce': OctetString,
185 'acceptable_responses': AcceptableResponses,
186 'preferred_signature_algorithms': PreferredSignatureAlgorithms,
wbonde91513e2015-06-03 14:52:18 -0400187 }
188
189
190class TBSRequestExtensions(SequenceOf):
191 _child_spec = TBSRequestExtension
192
193
wbonde91513e2015-06-03 14:52:18 -0400194class TBSRequest(Sequence):
195 _fields = [
196 ('version', Version, {'tag_type': 'explicit', 'tag': 0, 'default': 'v1'}),
197 ('requestor_name', GeneralName, {'tag_type': 'explicit', 'tag': 1, 'optional': True}),
198 ('request_list', Requests),
199 ('request_extensions', TBSRequestExtensions, {'tag_type': 'explicit', 'tag': 2, 'optional': True}),
200 ]
201
202
203class Certificates(SequenceOf):
204 _child_spec = Certificate
205
206
207class Signature(Sequence):
208 _fields = [
209 ('signature_algorithm', SignedDigestAlgorithm),
210 ('signature', OctetBitString),
211 ('certs', Certificates, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
212 ]
213
214
215class OCSPRequest(Sequence):
216 _fields = [
217 ('tbs_request', TBSRequest),
218 ('optional_signature', Signature, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
219 ]
220
wbondbcb62642015-07-20 10:16:27 -0400221 _processed_extensions = False
222 _critical_extensions = None
223 _nonce_value = None
224 _acceptable_responses_value = None
225 _preferred_signature_algorithms_value = None
226
227 def _set_extensions(self):
228 """
229 Sets common named extensions to private attributes and creates a list
230 of critical extensions
231 """
232
wbond2fde6452015-07-23 10:54:13 -0400233 self._critical_extensions = set()
wbondbcb62642015-07-20 10:16:27 -0400234
235 for extension in self['tbs_request']['request_extensions']:
236 name = extension['extn_id'].native
237 attribute_name = '_%s_value' % name
238 if hasattr(self, attribute_name):
239 setattr(self, attribute_name, extension['extn_value'].parsed)
240 if extension['critical'].native:
wbond2fde6452015-07-23 10:54:13 -0400241 self._critical_extensions.add(name)
wbondbcb62642015-07-20 10:16:27 -0400242
243 self._processed_extensions = True
244
245 @property
246 def critical_extensions(self):
247 """
wbond2fde6452015-07-23 10:54:13 -0400248 Returns a set of the names (or OID if not a known extension) of the
wbondbcb62642015-07-20 10:16:27 -0400249 extensions marked as critical
250
251 :return:
wbond2fde6452015-07-23 10:54:13 -0400252 A set of unicode strings
wbondbcb62642015-07-20 10:16:27 -0400253 """
254
255 if not self._processed_extensions:
256 self._set_extensions()
257 return self._critical_extensions
258
259 @property
260 def nonce_value(self):
261 """
262 This extension is used to prevent replay attacks by including a unique,
263 random value with each request/response pair
264
265 :return:
266 None or an OctetString object
267 """
268
269 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400270 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400271 return self._nonce_value
272
273 @property
274 def acceptable_responses_value(self):
275 """
276 This extension is used to allow the client and server to communicate
277 with alternative response formats other than just basic_ocsp_response,
278 although no other formats are defined in the standard.
279
280 :return:
281 None or an AcceptableResponses object
282 """
283
284 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400285 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400286 return self._acceptable_responses_value
287
288 @property
289 def preferred_signature_algorithms_value(self):
290 """
291 This extension is used by the client to define what signature algorithms
292 are preferred, including both the hash algorithm and the public key
293 algorithm, with a level of detail down to even the public key algorithm
294 parameters, such as curve name.
295
296 :return:
297 None or a PreferredSignatureAlgorithms object
298 """
299
300 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400301 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400302 return self._preferred_signature_algorithms_value
303
wbonde91513e2015-06-03 14:52:18 -0400304
305class OCSPResponseStatus(Enumerated):
306 _map = {
307 0: 'successful',
308 1: 'malformed_request',
309 2: 'internal_error',
310 3: 'try_later',
311 5: 'sign_required',
wbond77b0ccd2015-07-17 11:17:02 -0400312 6: 'unauthorized',
wbonde91513e2015-06-03 14:52:18 -0400313 }
314
315
316class ResponderId(Choice):
317 _alternatives = [
318 ('by_name', Name, {'tag_type': 'explicit', 'tag': 1}),
319 ('by_key', OctetString, {'tag_type': 'explicit', 'tag': 2}),
320 ]
321
322
323class RevokedInfo(Sequence):
324 _fields = [
325 ('revocation_time', GeneralizedTime),
326 ('revocation_reason', CRLReason, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
327 ]
328
329
330class CertStatus(Choice):
331 _alternatives = [
332 ('good', Null, {'tag_type': 'implicit', 'tag': 0}),
333 ('revoked', RevokedInfo, {'tag_type': 'implicit', 'tag': 1}),
334 ('unknown', Null, {'tag_type': 'implicit', 'tag': 2}),
335 ]
336
337
wbond90ec1302015-07-20 09:10:50 -0400338class CrlId(Sequence):
339 _fields = [
340 ('crl_url', IA5String, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
341 ('crl_num', Integer, {'tag_type': 'explicit', 'tag': 1, 'optional': True}),
342 ('crl_time', GeneralizedTime, {'tag_type': 'explicit', 'tag': 2, 'optional': True}),
343 ]
344
345
346class SingleResponseExtensionId(ObjectIdentifier):
347 _map = {
wbond65593fe2015-07-20 10:14:50 -0400348 '1.3.6.1.5.5.7.48.1.3': 'crl',
349 '1.3.6.1.5.5.7.48.1.6': 'archive_cutoff',
350 # These are CRLEntryExtension values from
351 # https://tools.ietf.org/html/rfc5280
wbond90ec1302015-07-20 09:10:50 -0400352 '2.5.29.21': 'crl_reason',
353 '2.5.29.24': 'invalidity_date',
354 '2.5.29.29': 'certificate_issuer',
355 }
356
357
358class SingleResponseExtension(Sequence):
359 _fields = [
360 ('extn_id', SingleResponseExtensionId),
361 ('critical', Boolean, {'default': False}),
wbonde5a1c6e2015-08-03 07:42:28 -0400362 ('extn_value', ParsableOctetString),
wbond90ec1302015-07-20 09:10:50 -0400363 ]
364
365 _oid_pair = ('extn_id', 'extn_value')
366 _oid_specs = {
wbond65593fe2015-07-20 10:14:50 -0400367 'crl': CrlId,
368 'archive_cutoff': GeneralizedTime,
wbond90ec1302015-07-20 09:10:50 -0400369 'crl_reason': CRLReason,
370 'invalidity_date': GeneralizedTime,
371 'certificate_issuer': GeneralNames,
372 }
373
374
375class SingleResponseExtensions(SequenceOf):
376 _child_spec = SingleResponseExtension
377
378
wbonde91513e2015-06-03 14:52:18 -0400379class SingleResponse(Sequence):
380 _fields = [
381 ('cert_id', CertId),
382 ('cert_status', CertStatus),
383 ('this_update', GeneralizedTime),
384 ('next_update', GeneralizedTime, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
385 ('single_extensions', SingleResponseExtensions, {'tag_type': 'explicit', 'tag': 1, 'optional': True}),
386 ]
387
wbondbcb62642015-07-20 10:16:27 -0400388 _processed_extensions = False
389 _critical_extensions = None
390 _crl_value = None
391 _archive_cutoff_value = None
392 _crl_reason_value = None
393 _invalidity_date_value = None
394 _certificate_issuer_value = None
395
396 def _set_extensions(self):
397 """
398 Sets common named extensions to private attributes and creates a list
399 of critical extensions
400 """
401
wbond2fde6452015-07-23 10:54:13 -0400402 self._critical_extensions = set()
wbondbcb62642015-07-20 10:16:27 -0400403
404 for extension in self['single_extensions']:
405 name = extension['extn_id'].native
406 attribute_name = '_%s_value' % name
407 if hasattr(self, attribute_name):
408 setattr(self, attribute_name, extension['extn_value'].parsed)
409 if extension['critical'].native:
wbond2fde6452015-07-23 10:54:13 -0400410 self._critical_extensions.add(name)
wbondbcb62642015-07-20 10:16:27 -0400411
412 self._processed_extensions = True
413
414 @property
415 def critical_extensions(self):
416 """
wbond2fde6452015-07-23 10:54:13 -0400417 Returns a set of the names (or OID if not a known extension) of the
wbondbcb62642015-07-20 10:16:27 -0400418 extensions marked as critical
419
420 :return:
wbond2fde6452015-07-23 10:54:13 -0400421 A set of unicode strings
wbondbcb62642015-07-20 10:16:27 -0400422 """
423
424 if not self._processed_extensions:
425 self._set_extensions()
426 return self._critical_extensions
427
428 @property
429 def crl_value(self):
430 """
431 This extension is used to locate the CRL that a certificate's revocation
432 is contained within.
433
434 :return:
435 None or a CrlId object
436 """
437
438 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400439 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400440 return self._crl_value
441
442 @property
443 def archive_cutoff_value(self):
444 """
445 This extension is used to indicate the date at which an archived
446 (historical) certificate status entry will no longer be available.
447
448 :return:
449 None or a GeneralizedTime object
450 """
451
452 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400453 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400454 return self._archive_cutoff_value
455
456 @property
457 def crl_reason_value(self):
458 """
459 This extension indicates the reason that a certificate was revoked.
460
461 :return:
462 None or a CRLReason object
463 """
464
465 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400466 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400467 return self._crl_reason_value
468
469 @property
470 def invalidity_date_value(self):
471 """
472 This extension indicates the suspected date/time the private key was
473 compromised or the certificate became invalid. This would usually be
474 before the revocation date, which is when the CA processed the
475 revocation.
476
477 :return:
478 None or a GeneralizedTime object
479 """
480
481 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400482 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400483 return self._invalidity_date_value
484
485 @property
486 def certificate_issuer_value(self):
487 """
488 This extension indicates the issuer of the certificate in question.
489
490 :return:
491 None or an x509.GeneralNames object
492 """
493
494 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400495 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400496 return self._certificate_issuer_value
497
wbonde91513e2015-06-03 14:52:18 -0400498
499class Responses(SequenceOf):
500 _child_spec = SingleResponse
501
502
wbond90ec1302015-07-20 09:10:50 -0400503class ResponseDataExtensionId(ObjectIdentifier):
504 _map = {
wbond65593fe2015-07-20 10:14:50 -0400505 '1.3.6.1.5.5.7.48.1.2': 'nonce',
506 '1.3.6.1.5.5.7.48.1.9': 'extended_revoke',
wbond90ec1302015-07-20 09:10:50 -0400507 }
508
509
510class ResponseDataExtension(Sequence):
511 _fields = [
512 ('extn_id', ResponseDataExtensionId),
513 ('critical', Boolean, {'default': False}),
wbonde5a1c6e2015-08-03 07:42:28 -0400514 ('extn_value', ParsableOctetString),
wbond90ec1302015-07-20 09:10:50 -0400515 ]
516
517 _oid_pair = ('extn_id', 'extn_value')
518 _oid_specs = {
wbond65593fe2015-07-20 10:14:50 -0400519 'nonce': OctetString,
520 'extended_revoke': Null,
wbond90ec1302015-07-20 09:10:50 -0400521 }
522
523
524class ResponseDataExtensions(SequenceOf):
525 _child_spec = ResponseDataExtension
526
527
wbonde91513e2015-06-03 14:52:18 -0400528class ResponseData(Sequence):
529 _fields = [
530 ('version', Version, {'tag_type': 'explicit', 'tag': 0, 'default': 'v1'}),
531 ('responder_id', ResponderId),
532 ('produced_at', GeneralizedTime),
533 ('responses', Responses),
534 ('response_extensions', ResponseDataExtensions, {'tag_type': 'explicit', 'tag': 1, 'optional': True}),
535 ]
536
537
538class BasicOCSPResponse(Sequence):
539 _fields = [
540 ('tbs_response_data', ResponseData),
541 ('signature_algorithm', SignedDigestAlgorithm),
542 ('signature', OctetBitString),
543 ('certs', Certificates, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
544 ]
545
546
547class ResponseBytes(Sequence):
548 _fields = [
549 ('response_type', ResponseType),
wbonde5a1c6e2015-08-03 07:42:28 -0400550 ('response', ParsableOctetString),
wbonde91513e2015-06-03 14:52:18 -0400551 ]
552
553 _oid_pair = ('response_type', 'response')
554 _oid_specs = {
555 'basic_ocsp_response': BasicOCSPResponse,
556 }
557
558
559class OCSPResponse(Sequence):
560 _fields = [
561 ('response_status', OCSPResponseStatus),
562 ('response_bytes', ResponseBytes, {'tag_type': 'explicit', 'tag': 0, 'optional': True}),
563 ]
wbondbcb62642015-07-20 10:16:27 -0400564
565 _processed_extensions = False
566 _critical_extensions = None
567 _nonce_value = None
568 _extended_revoke_value = None
569
570 def _set_extensions(self):
571 """
572 Sets common named extensions to private attributes and creates a list
573 of critical extensions
574 """
575
wbond2fde6452015-07-23 10:54:13 -0400576 self._critical_extensions = set()
wbondbcb62642015-07-20 10:16:27 -0400577
578 for extension in self['response_bytes']['response'].parsed['tbs_response_data']['response_extensions']:
579 name = extension['extn_id'].native
580 attribute_name = '_%s_value' % name
581 if hasattr(self, attribute_name):
582 setattr(self, attribute_name, extension['extn_value'].parsed)
583 if extension['critical'].native:
wbond2fde6452015-07-23 10:54:13 -0400584 self._critical_extensions.add(name)
wbondbcb62642015-07-20 10:16:27 -0400585
586 self._processed_extensions = True
587
588 @property
589 def critical_extensions(self):
590 """
wbond2fde6452015-07-23 10:54:13 -0400591 Returns a set of the names (or OID if not a known extension) of the
wbondbcb62642015-07-20 10:16:27 -0400592 extensions marked as critical
593
594 :return:
wbond2fde6452015-07-23 10:54:13 -0400595 A set of unicode strings
wbondbcb62642015-07-20 10:16:27 -0400596 """
597
598 if not self._processed_extensions:
599 self._set_extensions()
600 return self._critical_extensions
601
602 @property
603 def nonce_value(self):
604 """
605 This extension is used to prevent replay attacks on the request/response
606 exchange
607
608 :return:
609 None or an OctetString object
610 """
611
612 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400613 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400614 return self._nonce_value
615
616 @property
617 def extended_revoke_value(self):
618 """
619 This extension is used to signal that the responder will return a
620 "revoked" status for non-issued certificates.
621
622 :return:
623 None or a Null object (if present)
624 """
625
626 if self._processed_extensions is False:
wbondad218f92015-07-20 10:43:16 -0400627 self._set_extensions()
wbondbcb62642015-07-20 10:16:27 -0400628 return self._extended_revoke_value