text: # 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
  