|
|
- # coding: utf-8
-
- """
- ASN.1 type classes for certificate revocation lists (CRL). Exports the
- following items:
-
- - CertificateList()
-
- Other type classes are defined that help compose the types listed above.
- """
-
- from __future__ import unicode_literals, division, absolute_import, print_function
-
- import hashlib
-
- from .algos import SignedDigestAlgorithm
- from .core import (
- Boolean,
- Enumerated,
- GeneralizedTime,
- Integer,
- ObjectIdentifier,
- OctetBitString,
- ParsableOctetString,
- Sequence,
- SequenceOf,
- )
- from .x509 import (
- AuthorityInfoAccessSyntax,
- AuthorityKeyIdentifier,
- CRLDistributionPoints,
- DistributionPointName,
- GeneralNames,
- Name,
- ReasonFlags,
- Time,
- )
-
-
- # The structures in this file are taken from https://tools.ietf.org/html/rfc5280
-
-
- class Version(Integer):
- _map = {
- 0: 'v1',
- 1: 'v2',
- 2: 'v3',
- }
-
-
- class IssuingDistributionPoint(Sequence):
- _fields = [
- ('distribution_point', DistributionPointName, {'explicit': 0, 'optional': True}),
- ('only_contains_user_certs', Boolean, {'implicit': 1, 'default': False}),
- ('only_contains_ca_certs', Boolean, {'implicit': 2, 'default': False}),
- ('only_some_reasons', ReasonFlags, {'implicit': 3, 'optional': True}),
- ('indirect_crl', Boolean, {'implicit': 4, 'default': False}),
- ('only_contains_attribute_certs', Boolean, {'implicit': 5, 'default': False}),
- ]
-
-
- class TBSCertListExtensionId(ObjectIdentifier):
- _map = {
- '2.5.29.18': 'issuer_alt_name',
- '2.5.29.20': 'crl_number',
- '2.5.29.27': 'delta_crl_indicator',
- '2.5.29.28': 'issuing_distribution_point',
- '2.5.29.35': 'authority_key_identifier',
- '2.5.29.46': 'freshest_crl',
- '1.3.6.1.5.5.7.1.1': 'authority_information_access',
- }
-
-
- class TBSCertListExtension(Sequence):
- _fields = [
- ('extn_id', TBSCertListExtensionId),
- ('critical', Boolean, {'default': False}),
- ('extn_value', ParsableOctetString),
- ]
-
- _oid_pair = ('extn_id', 'extn_value')
- _oid_specs = {
- 'issuer_alt_name': GeneralNames,
- 'crl_number': Integer,
- 'delta_crl_indicator': Integer,
- 'issuing_distribution_point': IssuingDistributionPoint,
- 'authority_key_identifier': AuthorityKeyIdentifier,
- 'freshest_crl': CRLDistributionPoints,
- 'authority_information_access': AuthorityInfoAccessSyntax,
- }
-
-
- class TBSCertListExtensions(SequenceOf):
- _child_spec = TBSCertListExtension
-
-
- class CRLReason(Enumerated):
- _map = {
- 0: 'unspecified',
- 1: 'key_compromise',
- 2: 'ca_compromise',
- 3: 'affiliation_changed',
- 4: 'superseded',
- 5: 'cessation_of_operation',
- 6: 'certificate_hold',
- 8: 'remove_from_crl',
- 9: 'privilege_withdrawn',
- 10: 'aa_compromise',
- }
-
- @property
- def human_friendly(self):
- """
- :return:
- A unicode string with revocation description that is suitable to
- show to end-users. Starts with a lower case letter and phrased in
- such a way that it makes sense after the phrase "because of" or
- "due to".
- """
-
- return {
- 'unspecified': 'an unspecified reason',
- 'key_compromise': 'a compromised key',
- 'ca_compromise': 'the CA being compromised',
- 'affiliation_changed': 'an affiliation change',
- 'superseded': 'certificate supersession',
- 'cessation_of_operation': 'a cessation of operation',
- 'certificate_hold': 'a certificate hold',
- 'remove_from_crl': 'removal from the CRL',
- 'privilege_withdrawn': 'privilege withdrawl',
- 'aa_compromise': 'the AA being compromised',
- }[self.native]
-
-
- class CRLEntryExtensionId(ObjectIdentifier):
- _map = {
- '2.5.29.21': 'crl_reason',
- '2.5.29.23': 'hold_instruction_code',
- '2.5.29.24': 'invalidity_date',
- '2.5.29.29': 'certificate_issuer',
- }
-
-
- class CRLEntryExtension(Sequence):
- _fields = [
- ('extn_id', CRLEntryExtensionId),
- ('critical', Boolean, {'default': False}),
- ('extn_value', ParsableOctetString),
- ]
-
- _oid_pair = ('extn_id', 'extn_value')
- _oid_specs = {
- 'crl_reason': CRLReason,
- 'hold_instruction_code': ObjectIdentifier,
- 'invalidity_date': GeneralizedTime,
- 'certificate_issuer': GeneralNames,
- }
-
-
- class CRLEntryExtensions(SequenceOf):
- _child_spec = CRLEntryExtension
-
-
- class RevokedCertificate(Sequence):
- _fields = [
- ('user_certificate', Integer),
- ('revocation_date', Time),
- ('crl_entry_extensions', CRLEntryExtensions, {'optional': True}),
- ]
-
- _processed_extensions = False
- _critical_extensions = None
- _crl_reason_value = None
- _invalidity_date_value = None
- _certificate_issuer_value = None
- _issuer_name = False
-
- def _set_extensions(self):
- """
- Sets common named extensions to private attributes and creates a list
- of critical extensions
- """
-
- self._critical_extensions = set()
-
- for extension in self['crl_entry_extensions']:
- name = extension['extn_id'].native
- attribute_name = '_%s_value' % name
- if hasattr(self, attribute_name):
- setattr(self, attribute_name, extension['extn_value'].parsed)
- if extension['critical'].native:
- self._critical_extensions.add(name)
-
- self._processed_extensions = True
-
- @property
- def critical_extensions(self):
- """
- Returns a set of the names (or OID if not a known extension) of the
- extensions marked as critical
-
- :return:
- A set of unicode strings
- """
-
- if not self._processed_extensions:
- self._set_extensions()
- return self._critical_extensions
-
- @property
- def crl_reason_value(self):
- """
- This extension indicates the reason that a certificate was revoked.
-
- :return:
- None or a CRLReason object
- """
-
- if self._processed_extensions is False:
- self._set_extensions()
- return self._crl_reason_value
-
- @property
- def invalidity_date_value(self):
- """
- This extension indicates the suspected date/time the private key was
- compromised or the certificate became invalid. This would usually be
- before the revocation date, which is when the CA processed the
- revocation.
-
- :return:
- None or a GeneralizedTime object
- """
-
- if self._processed_extensions is False:
- self._set_extensions()
- return self._invalidity_date_value
-
- @property
- def certificate_issuer_value(self):
- """
- This extension indicates the issuer of the certificate in question,
- and is used in indirect CRLs. CRL entries without this extension are
- for certificates issued from the last seen issuer.
-
- :return:
- None or an x509.GeneralNames object
- """
-
- if self._processed_extensions is False:
- self._set_extensions()
- return self._certificate_issuer_value
-
- @property
- def issuer_name(self):
- """
- :return:
- None, or an asn1crypto.x509.Name object for the issuer of the cert
- """
-
- if self._issuer_name is False:
- self._issuer_name = None
- if self.certificate_issuer_value:
- for general_name in self.certificate_issuer_value:
- if general_name.name == 'directory_name':
- self._issuer_name = general_name.chosen
- break
- return self._issuer_name
-
-
- class RevokedCertificates(SequenceOf):
- _child_spec = RevokedCertificate
-
-
- class TbsCertList(Sequence):
- _fields = [
- ('version', Version, {'optional': True}),
- ('signature', SignedDigestAlgorithm),
- ('issuer', Name),
- ('this_update', Time),
- ('next_update', Time, {'optional': True}),
- ('revoked_certificates', RevokedCertificates, {'optional': True}),
- ('crl_extensions', TBSCertListExtensions, {'explicit': 0, 'optional': True}),
- ]
-
-
- class CertificateList(Sequence):
- _fields = [
- ('tbs_cert_list', TbsCertList),
- ('signature_algorithm', SignedDigestAlgorithm),
- ('signature', OctetBitString),
- ]
-
- _processed_extensions = False
- _critical_extensions = None
- _issuer_alt_name_value = None
- _crl_number_value = None
- _delta_crl_indicator_value = None
- _issuing_distribution_point_value = None
- _authority_key_identifier_value = None
- _freshest_crl_value = None
- _authority_information_access_value = None
- _issuer_cert_urls = None
- _delta_crl_distribution_points = None
- _sha1 = None
- _sha256 = None
-
- def _set_extensions(self):
- """
- Sets common named extensions to private attributes and creates a list
- of critical extensions
- """
-
- self._critical_extensions = set()
-
- for extension in self['tbs_cert_list']['crl_extensions']:
- name = extension['extn_id'].native
- attribute_name = '_%s_value' % name
- if hasattr(self, attribute_name):
- setattr(self, attribute_name, extension['extn_value'].parsed)
- if extension['critical'].native:
- self._critical_extensions.add(name)
-
- self._processed_extensions = True
-
- @property
- def critical_extensions(self):
- """
- Returns a set of the names (or OID if not a known extension) of the
- extensions marked as critical
-
- :return:
- A set of unicode strings
- """
-
- if not self._processed_extensions:
- self._set_extensions()
- return self._critical_extensions
-
- @property
- def issuer_alt_name_value(self):
- """
- This extension allows associating one or more alternative names with
- the issuer of the CRL.
-
- :return:
- None or an x509.GeneralNames object
- """
-
- if self._processed_extensions is False:
- self._set_extensions()
- return self._issuer_alt_name_value
-
- @property
- def crl_number_value(self):
- """
- This extension adds a monotonically increasing number to the CRL and is
- used to distinguish different versions of the CRL.
-
- :return:
- None or an Integer object
- """
-
- if self._processed_extensions is False:
- self._set_extensions()
- return self._crl_number_value
-
- @property
- def delta_crl_indicator_value(self):
- """
- This extension indicates a CRL is a delta CRL, and contains the CRL
- number of the base CRL that it is a delta from.
-
- :return:
- None or an Integer object
- """
-
- if self._processed_extensions is False:
- self._set_extensions()
- return self._delta_crl_indicator_value
-
- @property
- def issuing_distribution_point_value(self):
- """
- This extension includes information about what types of revocations
- and certificates are part of the CRL.
-
- :return:
- None or an IssuingDistributionPoint object
- """
-
- if self._processed_extensions is False:
- self._set_extensions()
- return self._issuing_distribution_point_value
-
- @property
- def authority_key_identifier_value(self):
- """
- This extension helps in identifying the public key with which to
- validate the authenticity of the CRL.
-
- :return:
- None or an AuthorityKeyIdentifier object
- """
-
- if self._processed_extensions is False:
- self._set_extensions()
- return self._authority_key_identifier_value
-
- @property
- def freshest_crl_value(self):
- """
- This extension is used in complete CRLs to indicate where a delta CRL
- may be located.
-
- :return:
- None or a CRLDistributionPoints object
- """
-
- if self._processed_extensions is False:
- self._set_extensions()
- return self._freshest_crl_value
-
- @property
- def authority_information_access_value(self):
- """
- This extension is used to provide a URL with which to download the
- certificate used to sign this CRL.
-
- :return:
- None or an AuthorityInfoAccessSyntax object
- """
-
- if self._processed_extensions is False:
- self._set_extensions()
- return self._authority_information_access_value
-
- @property
- def issuer(self):
- """
- :return:
- An asn1crypto.x509.Name object for the issuer of the CRL
- """
-
- return self['tbs_cert_list']['issuer']
-
- @property
- def authority_key_identifier(self):
- """
- :return:
- None or a byte string of the key_identifier from the authority key
- identifier extension
- """
-
- if not self.authority_key_identifier_value:
- return None
-
- return self.authority_key_identifier_value['key_identifier'].native
-
- @property
- def issuer_cert_urls(self):
- """
- :return:
- A list of unicode strings that are URLs that should contain either
- an individual DER-encoded X.509 certificate, or a DER-encoded CMS
- message containing multiple certificates
- """
-
- if self._issuer_cert_urls is None:
- self._issuer_cert_urls = []
- if self.authority_information_access_value:
- for entry in self.authority_information_access_value:
- if entry['access_method'].native == 'ca_issuers':
- location = entry['access_location']
- if location.name != 'uniform_resource_identifier':
- continue
- url = location.native
- if url.lower()[0:7] == 'http://':
- self._issuer_cert_urls.append(url)
- return self._issuer_cert_urls
-
- @property
- def delta_crl_distribution_points(self):
- """
- Returns delta CRL URLs - only applies to complete CRLs
-
- :return:
- A list of zero or more DistributionPoint objects
- """
-
- if self._delta_crl_distribution_points is None:
- self._delta_crl_distribution_points = []
-
- if self.freshest_crl_value is not None:
- for distribution_point in self.freshest_crl_value:
- distribution_point_name = distribution_point['distribution_point']
- # RFC 5280 indicates conforming CA should not use the relative form
- if distribution_point_name.name == 'name_relative_to_crl_issuer':
- continue
- # This library is currently only concerned with HTTP-based CRLs
- for general_name in distribution_point_name.chosen:
- if general_name.name == 'uniform_resource_identifier':
- self._delta_crl_distribution_points.append(distribution_point)
-
- return self._delta_crl_distribution_points
-
- @property
- def signature(self):
- """
- :return:
- A byte string of the signature
- """
-
- return self['signature'].native
-
- @property
- def sha1(self):
- """
- :return:
- The SHA1 hash of the DER-encoded bytes of this certificate list
- """
-
- if self._sha1 is None:
- self._sha1 = hashlib.sha1(self.dump()).digest()
- return self._sha1
-
- @property
- def sha256(self):
- """
- :return:
- The SHA-256 hash of the DER-encoded bytes of this certificate list
- """
-
- if self._sha256 is None:
- self._sha256 = hashlib.sha256(self.dump()).digest()
- return self._sha256
|