You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

652 lines
17 KiB

4 years ago
  1. # coding: utf-8
  2. """
  3. ASN.1 type classes for the online certificate status protocol (OCSP). Exports
  4. the following items:
  5. - OCSPRequest()
  6. - OCSPResponse()
  7. Other type classes are defined that help compose the types listed above.
  8. """
  9. from __future__ import unicode_literals, division, absolute_import, print_function
  10. from .algos import DigestAlgorithm, SignedDigestAlgorithm
  11. from .core import (
  12. Boolean,
  13. Choice,
  14. Enumerated,
  15. GeneralizedTime,
  16. IA5String,
  17. Integer,
  18. Null,
  19. ObjectIdentifier,
  20. OctetBitString,
  21. OctetString,
  22. ParsableOctetString,
  23. Sequence,
  24. SequenceOf,
  25. )
  26. from .crl import AuthorityInfoAccessSyntax, CRLReason
  27. from .keys import PublicKeyAlgorithm
  28. from .x509 import Certificate, GeneralName, GeneralNames, Name
  29. # The structures in this file are taken from https://tools.ietf.org/html/rfc6960
  30. class Version(Integer):
  31. _map = {
  32. 0: 'v1'
  33. }
  34. class CertId(Sequence):
  35. _fields = [
  36. ('hash_algorithm', DigestAlgorithm),
  37. ('issuer_name_hash', OctetString),
  38. ('issuer_key_hash', OctetString),
  39. ('serial_number', Integer),
  40. ]
  41. class ServiceLocator(Sequence):
  42. _fields = [
  43. ('issuer', Name),
  44. ('locator', AuthorityInfoAccessSyntax),
  45. ]
  46. class RequestExtensionId(ObjectIdentifier):
  47. _map = {
  48. '1.3.6.1.5.5.7.48.1.7': 'service_locator',
  49. }
  50. class RequestExtension(Sequence):
  51. _fields = [
  52. ('extn_id', RequestExtensionId),
  53. ('critical', Boolean, {'default': False}),
  54. ('extn_value', ParsableOctetString),
  55. ]
  56. _oid_pair = ('extn_id', 'extn_value')
  57. _oid_specs = {
  58. 'service_locator': ServiceLocator,
  59. }
  60. class RequestExtensions(SequenceOf):
  61. _child_spec = RequestExtension
  62. class Request(Sequence):
  63. _fields = [
  64. ('req_cert', CertId),
  65. ('single_request_extensions', RequestExtensions, {'explicit': 0, 'optional': True}),
  66. ]
  67. _processed_extensions = False
  68. _critical_extensions = None
  69. _service_locator_value = None
  70. def _set_extensions(self):
  71. """
  72. Sets common named extensions to private attributes and creates a list
  73. of critical extensions
  74. """
  75. self._critical_extensions = set()
  76. for extension in self['single_request_extensions']:
  77. name = extension['extn_id'].native
  78. attribute_name = '_%s_value' % name
  79. if hasattr(self, attribute_name):
  80. setattr(self, attribute_name, extension['extn_value'].parsed)
  81. if extension['critical'].native:
  82. self._critical_extensions.add(name)
  83. self._processed_extensions = True
  84. @property
  85. def critical_extensions(self):
  86. """
  87. Returns a set of the names (or OID if not a known extension) of the
  88. extensions marked as critical
  89. :return:
  90. A set of unicode strings
  91. """
  92. if not self._processed_extensions:
  93. self._set_extensions()
  94. return self._critical_extensions
  95. @property
  96. def service_locator_value(self):
  97. """
  98. This extension is used when communicating with an OCSP responder that
  99. acts as a proxy for OCSP requests
  100. :return:
  101. None or a ServiceLocator object
  102. """
  103. if self._processed_extensions is False:
  104. self._set_extensions()
  105. return self._service_locator_value
  106. class Requests(SequenceOf):
  107. _child_spec = Request
  108. class ResponseType(ObjectIdentifier):
  109. _map = {
  110. '1.3.6.1.5.5.7.48.1.1': 'basic_ocsp_response',
  111. }
  112. class AcceptableResponses(SequenceOf):
  113. _child_spec = ResponseType
  114. class PreferredSignatureAlgorithm(Sequence):
  115. _fields = [
  116. ('sig_identifier', SignedDigestAlgorithm),
  117. ('cert_identifier', PublicKeyAlgorithm, {'optional': True}),
  118. ]
  119. class PreferredSignatureAlgorithms(SequenceOf):
  120. _child_spec = PreferredSignatureAlgorithm
  121. class TBSRequestExtensionId(ObjectIdentifier):
  122. _map = {
  123. '1.3.6.1.5.5.7.48.1.2': 'nonce',
  124. '1.3.6.1.5.5.7.48.1.4': 'acceptable_responses',
  125. '1.3.6.1.5.5.7.48.1.8': 'preferred_signature_algorithms',
  126. }
  127. class TBSRequestExtension(Sequence):
  128. _fields = [
  129. ('extn_id', TBSRequestExtensionId),
  130. ('critical', Boolean, {'default': False}),
  131. ('extn_value', ParsableOctetString),
  132. ]
  133. _oid_pair = ('extn_id', 'extn_value')
  134. _oid_specs = {
  135. 'nonce': OctetString,
  136. 'acceptable_responses': AcceptableResponses,
  137. 'preferred_signature_algorithms': PreferredSignatureAlgorithms,
  138. }
  139. class TBSRequestExtensions(SequenceOf):
  140. _child_spec = TBSRequestExtension
  141. class TBSRequest(Sequence):
  142. _fields = [
  143. ('version', Version, {'explicit': 0, 'default': 'v1'}),
  144. ('requestor_name', GeneralName, {'explicit': 1, 'optional': True}),
  145. ('request_list', Requests),
  146. ('request_extensions', TBSRequestExtensions, {'explicit': 2, 'optional': True}),
  147. ]
  148. class Certificates(SequenceOf):
  149. _child_spec = Certificate
  150. class Signature(Sequence):
  151. _fields = [
  152. ('signature_algorithm', SignedDigestAlgorithm),
  153. ('signature', OctetBitString),
  154. ('certs', Certificates, {'explicit': 0, 'optional': True}),
  155. ]
  156. class OCSPRequest(Sequence):
  157. _fields = [
  158. ('tbs_request', TBSRequest),
  159. ('optional_signature', Signature, {'explicit': 0, 'optional': True}),
  160. ]
  161. _processed_extensions = False
  162. _critical_extensions = None
  163. _nonce_value = None
  164. _acceptable_responses_value = None
  165. _preferred_signature_algorithms_value = None
  166. def _set_extensions(self):
  167. """
  168. Sets common named extensions to private attributes and creates a list
  169. of critical extensions
  170. """
  171. self._critical_extensions = set()
  172. for extension in self['tbs_request']['request_extensions']:
  173. name = extension['extn_id'].native
  174. attribute_name = '_%s_value' % name
  175. if hasattr(self, attribute_name):
  176. setattr(self, attribute_name, extension['extn_value'].parsed)
  177. if extension['critical'].native:
  178. self._critical_extensions.add(name)
  179. self._processed_extensions = True
  180. @property
  181. def critical_extensions(self):
  182. """
  183. Returns a set of the names (or OID if not a known extension) of the
  184. extensions marked as critical
  185. :return:
  186. A set of unicode strings
  187. """
  188. if not self._processed_extensions:
  189. self._set_extensions()
  190. return self._critical_extensions
  191. @property
  192. def nonce_value(self):
  193. """
  194. This extension is used to prevent replay attacks by including a unique,
  195. random value with each request/response pair
  196. :return:
  197. None or an OctetString object
  198. """
  199. if self._processed_extensions is False:
  200. self._set_extensions()
  201. return self._nonce_value
  202. @property
  203. def acceptable_responses_value(self):
  204. """
  205. This extension is used to allow the client and server to communicate
  206. with alternative response formats other than just basic_ocsp_response,
  207. although no other formats are defined in the standard.
  208. :return:
  209. None or an AcceptableResponses object
  210. """
  211. if self._processed_extensions is False:
  212. self._set_extensions()
  213. return self._acceptable_responses_value
  214. @property
  215. def preferred_signature_algorithms_value(self):
  216. """
  217. This extension is used by the client to define what signature algorithms
  218. are preferred, including both the hash algorithm and the public key
  219. algorithm, with a level of detail down to even the public key algorithm
  220. parameters, such as curve name.
  221. :return:
  222. None or a PreferredSignatureAlgorithms object
  223. """
  224. if self._processed_extensions is False:
  225. self._set_extensions()
  226. return self._preferred_signature_algorithms_value
  227. class OCSPResponseStatus(Enumerated):
  228. _map = {
  229. 0: 'successful',
  230. 1: 'malformed_request',
  231. 2: 'internal_error',
  232. 3: 'try_later',
  233. 5: 'sign_required',
  234. 6: 'unauthorized',
  235. }
  236. class ResponderId(Choice):
  237. _alternatives = [
  238. ('by_name', Name, {'explicit': 1}),
  239. ('by_key', OctetString, {'explicit': 2}),
  240. ]
  241. class RevokedInfo(Sequence):
  242. _fields = [
  243. ('revocation_time', GeneralizedTime),
  244. ('revocation_reason', CRLReason, {'explicit': 0, 'optional': True}),
  245. ]
  246. class CertStatus(Choice):
  247. _alternatives = [
  248. ('good', Null, {'implicit': 0}),
  249. ('revoked', RevokedInfo, {'implicit': 1}),
  250. ('unknown', Null, {'implicit': 2}),
  251. ]
  252. class CrlId(Sequence):
  253. _fields = [
  254. ('crl_url', IA5String, {'explicit': 0, 'optional': True}),
  255. ('crl_num', Integer, {'explicit': 1, 'optional': True}),
  256. ('crl_time', GeneralizedTime, {'explicit': 2, 'optional': True}),
  257. ]
  258. class SingleResponseExtensionId(ObjectIdentifier):
  259. _map = {
  260. '1.3.6.1.5.5.7.48.1.3': 'crl',
  261. '1.3.6.1.5.5.7.48.1.6': 'archive_cutoff',
  262. # These are CRLEntryExtension values from
  263. # https://tools.ietf.org/html/rfc5280
  264. '2.5.29.21': 'crl_reason',
  265. '2.5.29.24': 'invalidity_date',
  266. '2.5.29.29': 'certificate_issuer',
  267. # https://tools.ietf.org/html/rfc6962.html#page-13
  268. '1.3.6.1.4.1.11129.2.4.5': 'signed_certificate_timestamp_list',
  269. }
  270. class SingleResponseExtension(Sequence):
  271. _fields = [
  272. ('extn_id', SingleResponseExtensionId),
  273. ('critical', Boolean, {'default': False}),
  274. ('extn_value', ParsableOctetString),
  275. ]
  276. _oid_pair = ('extn_id', 'extn_value')
  277. _oid_specs = {
  278. 'crl': CrlId,
  279. 'archive_cutoff': GeneralizedTime,
  280. 'crl_reason': CRLReason,
  281. 'invalidity_date': GeneralizedTime,
  282. 'certificate_issuer': GeneralNames,
  283. 'signed_certificate_timestamp_list': OctetString,
  284. }
  285. class SingleResponseExtensions(SequenceOf):
  286. _child_spec = SingleResponseExtension
  287. class SingleResponse(Sequence):
  288. _fields = [
  289. ('cert_id', CertId),
  290. ('cert_status', CertStatus),
  291. ('this_update', GeneralizedTime),
  292. ('next_update', GeneralizedTime, {'explicit': 0, 'optional': True}),
  293. ('single_extensions', SingleResponseExtensions, {'explicit': 1, 'optional': True}),
  294. ]
  295. _processed_extensions = False
  296. _critical_extensions = None
  297. _crl_value = None
  298. _archive_cutoff_value = None
  299. _crl_reason_value = None
  300. _invalidity_date_value = None
  301. _certificate_issuer_value = None
  302. def _set_extensions(self):
  303. """
  304. Sets common named extensions to private attributes and creates a list
  305. of critical extensions
  306. """
  307. self._critical_extensions = set()
  308. for extension in self['single_extensions']:
  309. name = extension['extn_id'].native
  310. attribute_name = '_%s_value' % name
  311. if hasattr(self, attribute_name):
  312. setattr(self, attribute_name, extension['extn_value'].parsed)
  313. if extension['critical'].native:
  314. self._critical_extensions.add(name)
  315. self._processed_extensions = True
  316. @property
  317. def critical_extensions(self):
  318. """
  319. Returns a set of the names (or OID if not a known extension) of the
  320. extensions marked as critical
  321. :return:
  322. A set of unicode strings
  323. """
  324. if not self._processed_extensions:
  325. self._set_extensions()
  326. return self._critical_extensions
  327. @property
  328. def crl_value(self):
  329. """
  330. This extension is used to locate the CRL that a certificate's revocation
  331. is contained within.
  332. :return:
  333. None or a CrlId object
  334. """
  335. if self._processed_extensions is False:
  336. self._set_extensions()
  337. return self._crl_value
  338. @property
  339. def archive_cutoff_value(self):
  340. """
  341. This extension is used to indicate the date at which an archived
  342. (historical) certificate status entry will no longer be available.
  343. :return:
  344. None or a GeneralizedTime object
  345. """
  346. if self._processed_extensions is False:
  347. self._set_extensions()
  348. return self._archive_cutoff_value
  349. @property
  350. def crl_reason_value(self):
  351. """
  352. This extension indicates the reason that a certificate was revoked.
  353. :return:
  354. None or a CRLReason object
  355. """
  356. if self._processed_extensions is False:
  357. self._set_extensions()
  358. return self._crl_reason_value
  359. @property
  360. def invalidity_date_value(self):
  361. """
  362. This extension indicates the suspected date/time the private key was
  363. compromised or the certificate became invalid. This would usually be
  364. before the revocation date, which is when the CA processed the
  365. revocation.
  366. :return:
  367. None or a GeneralizedTime object
  368. """
  369. if self._processed_extensions is False:
  370. self._set_extensions()
  371. return self._invalidity_date_value
  372. @property
  373. def certificate_issuer_value(self):
  374. """
  375. This extension indicates the issuer of the certificate in question.
  376. :return:
  377. None or an x509.GeneralNames object
  378. """
  379. if self._processed_extensions is False:
  380. self._set_extensions()
  381. return self._certificate_issuer_value
  382. class Responses(SequenceOf):
  383. _child_spec = SingleResponse
  384. class ResponseDataExtensionId(ObjectIdentifier):
  385. _map = {
  386. '1.3.6.1.5.5.7.48.1.2': 'nonce',
  387. '1.3.6.1.5.5.7.48.1.9': 'extended_revoke',
  388. }
  389. class ResponseDataExtension(Sequence):
  390. _fields = [
  391. ('extn_id', ResponseDataExtensionId),
  392. ('critical', Boolean, {'default': False}),
  393. ('extn_value', ParsableOctetString),
  394. ]
  395. _oid_pair = ('extn_id', 'extn_value')
  396. _oid_specs = {
  397. 'nonce': OctetString,
  398. 'extended_revoke': Null,
  399. }
  400. class ResponseDataExtensions(SequenceOf):
  401. _child_spec = ResponseDataExtension
  402. class ResponseData(Sequence):
  403. _fields = [
  404. ('version', Version, {'explicit': 0, 'default': 'v1'}),
  405. ('responder_id', ResponderId),
  406. ('produced_at', GeneralizedTime),
  407. ('responses', Responses),
  408. ('response_extensions', ResponseDataExtensions, {'explicit': 1, 'optional': True}),
  409. ]
  410. class BasicOCSPResponse(Sequence):
  411. _fields = [
  412. ('tbs_response_data', ResponseData),
  413. ('signature_algorithm', SignedDigestAlgorithm),
  414. ('signature', OctetBitString),
  415. ('certs', Certificates, {'explicit': 0, 'optional': True}),
  416. ]
  417. class ResponseBytes(Sequence):
  418. _fields = [
  419. ('response_type', ResponseType),
  420. ('response', ParsableOctetString),
  421. ]
  422. _oid_pair = ('response_type', 'response')
  423. _oid_specs = {
  424. 'basic_ocsp_response': BasicOCSPResponse,
  425. }
  426. class OCSPResponse(Sequence):
  427. _fields = [
  428. ('response_status', OCSPResponseStatus),
  429. ('response_bytes', ResponseBytes, {'explicit': 0, 'optional': True}),
  430. ]
  431. _processed_extensions = False
  432. _critical_extensions = None
  433. _nonce_value = None
  434. _extended_revoke_value = None
  435. def _set_extensions(self):
  436. """
  437. Sets common named extensions to private attributes and creates a list
  438. of critical extensions
  439. """
  440. self._critical_extensions = set()
  441. for extension in self['response_bytes']['response'].parsed['tbs_response_data']['response_extensions']:
  442. name = extension['extn_id'].native
  443. attribute_name = '_%s_value' % name
  444. if hasattr(self, attribute_name):
  445. setattr(self, attribute_name, extension['extn_value'].parsed)
  446. if extension['critical'].native:
  447. self._critical_extensions.add(name)
  448. self._processed_extensions = True
  449. @property
  450. def critical_extensions(self):
  451. """
  452. Returns a set of the names (or OID if not a known extension) of the
  453. extensions marked as critical
  454. :return:
  455. A set of unicode strings
  456. """
  457. if not self._processed_extensions:
  458. self._set_extensions()
  459. return self._critical_extensions
  460. @property
  461. def nonce_value(self):
  462. """
  463. This extension is used to prevent replay attacks on the request/response
  464. exchange
  465. :return:
  466. None or an OctetString object
  467. """
  468. if self._processed_extensions is False:
  469. self._set_extensions()
  470. return self._nonce_value
  471. @property
  472. def extended_revoke_value(self):
  473. """
  474. This extension is used to signal that the responder will return a
  475. "revoked" status for non-issued certificates.
  476. :return:
  477. None or a Null object (if present)
  478. """
  479. if self._processed_extensions is False:
  480. self._set_extensions()
  481. return self._extended_revoke_value
  482. @property
  483. def basic_ocsp_response(self):
  484. """
  485. A shortcut into the BasicOCSPResponse sequence
  486. :return:
  487. None or an asn1crypto.ocsp.BasicOCSPResponse object
  488. """
  489. return self['response_bytes']['response'].parsed
  490. @property
  491. def response_data(self):
  492. """
  493. A shortcut into the parsed, ResponseData sequence
  494. :return:
  495. None or an asn1crypto.ocsp.ResponseData object
  496. """
  497. return self['response_bytes']['response'].parsed['tbs_response_data']