class _ForceNullParameters(object):
    """
    Various structures based on AlgorithmIdentifier require that the parameters
    field be core.Null() for certain OIDs. This mixin ensures that happens.
    """

    # The following attribute, plus the parameters spec callback and custom
    # __setitem__ are all to handle a situation where parameters should not be
    # optional and must be Null for certain OIDs. More info at
    # https://tools.ietf.org/html/rfc4055#page-15 and
    # https://tools.ietf.org/html/rfc4055#section-2.1
    _null_algos = set([
        '1.2.840.113549.1.1.1',    # rsassa_pkcs1v15 / rsaes_pkcs1v15 / rsa
        '1.2.840.113549.1.1.11',   # sha256_rsa
        '1.2.840.113549.1.1.12',   # sha384_rsa
        '1.2.840.113549.1.1.13',   # sha512_rsa
        '1.2.840.113549.1.1.14',   # sha224_rsa
        '1.3.14.3.2.26',           # sha1
        '2.16.840.1.101.3.4.2.4',  # sha224
        '2.16.840.1.101.3.4.2.1',  # sha256
        '2.16.840.1.101.3.4.2.2',  # sha384
        '2.16.840.1.101.3.4.2.3',  # sha512
    ])

    def _parameters_spec(self):
        if self._oid_pair == ('algorithm', 'parameters'):
            algo = self['algorithm'].native
            if algo in self._oid_specs:
                return self._oid_specs[algo]

        if self['algorithm'].dotted in self._null_algos:
            return Null

        return None

    _spec_callbacks = {
        'parameters': _parameters_spec
    }

    # We have to override this since the spec callback uses the value of
    # algorithm to determine the parameter spec, however default values are
    # assigned before setting a field, so a default value can't be based on
    # another field value (unless it is a default also). Thus we have to
    # manually check to see if the algorithm was set and parameters is unset,
    # and then fix the value as appropriate.
    def __setitem__(self, key, value):
        res = super(_ForceNullParameters, self).__setitem__(key, value)
        if key != 'algorithm':
            return res
        if self['algorithm'].dotted not in self._null_algos:
            return res
        if self['parameters'].__class__ != Void:
            return res
        self['parameters'] = Null()
        return res class DigestAlgorithm(_ForceNullParameters, Sequence):
    _fields = [
        ('algorithm', DigestAlgorithmId),
        ('parameters', Any, {'optional': True}),
    ] class EncryptionAlgorithm(_ForceNullParameters, Sequence):
    _fields = [
        ('algorithm', EncryptionAlgorithmId),
        ('parameters', Any, {'optional': True}),
    ]

    _oid_pair = ('algorithm', 'parameters')
    _oid_specs = {
        'des': OctetString,
        'tripledes_3key': OctetString,
        'rc2': Rc2Params,
        'rc5': Rc5Params,
        'aes128_cbc': OctetString,
        'aes192_cbc': OctetString,
        'aes256_cbc': OctetString,
        'aes128_ofb': OctetString,
        'aes192_ofb': OctetString,
        'aes256_ofb': OctetString,
        # From RFC5084
        'aes128_ccm': CcmParams,
        'aes192_ccm': CcmParams,
        'aes256_ccm': CcmParams,
        # From PKCS#5
        'pbes1_md2_des': Pbes1Params,
        'pbes1_md5_des': Pbes1Params,
        'pbes1_md2_rc2': Pbes1Params,
        'pbes1_md5_rc2': Pbes1Params,
        'pbes1_sha1_des': Pbes1Params,
        'pbes1_sha1_rc2': Pbes1Params,
        # From PKCS#12
        'pkcs12_sha1_rc4_128': Pbes1Params,
        'pkcs12_sha1_rc4_40': Pbes1Params,
        'pkcs12_sha1_tripledes_3key': Pbes1Params,
        'pkcs12_sha1_tripledes_2key': Pbes1Params,
        'pkcs12_sha1_rc2_128': Pbes1Params,
        'pkcs12_sha1_rc2_40': Pbes1Params,
        # PKCS#1 v2.2
        'rsaes_oaep': RSAESOAEPParams,
    }

    @property
    def kdf(self):
        """
        Returns the name of the key derivation function to use.

        :return:
            A unicode from of one of the following: "pbkdf1", "pbkdf2",
            "pkcs12_kdf"
        """

        encryption_algo = self['algorithm'].native

        if encryption_algo == 'pbes2':
            return self['parameters']['key_derivation_func']['algorithm'].native

        if encryption_algo.find('.') == -1:
            if encryption_algo.find('_') != -1:
                encryption_algo, _ = encryption_algo.split('_', 1)

                if encryption_algo == 'pbes1':
                    return 'pbkdf1'

                if encryption_algo == 'pkcs12':
                    return 'pkcs12_kdf'

            raise ValueError(unwrap(
                '''
                Encryption algorithm "%s" does not have a registered key
                derivation function
                ''',
                encryption_algo
            ))

        raise ValueError(unwrap(
            '''
            Unrecognized encryption algorithm "%s", can not determine key
            derivation function
            ''',
            encryption_algo
        ))

    @property
    def kdf_hmac(self):
        """
        Returns the HMAC algorithm to use with the KDF.

        :return:
            A unicode string of one of the following: "md2", "md5", "sha1",
            "sha224", "sha256", "sha384", "sha512"
        """

        encryption_algo = self['algorithm'].native

        if encryption_algo == 'pbes2':
            return self['parameters']['key_derivation_func']['parameters']['prf']['algorithm'].native

        if encryption_algo.find('.') == -1:
            if encryption_algo.find('_') != -1:
                _, hmac_algo, _ = encryption_algo.split('_', 2)
                return hmac_algo

            raise ValueError(unwrap(
                '''
                Encryption algorithm "%s" does not have a registered key
                derivation function
                ''',
                encryption_algo
            ))

        raise ValueError(unwrap(
            '''
            Unrecognized encryption algorithm "%s", can not determine key
            derivation hmac algorithm
            ''',
            encryption_algo
        ))

    @property
    def kdf_salt(self):
        """
        Returns the byte string to use as the salt for the KDF.

        :return:
            A byte string
        """

        encryption_algo = self['algorithm'].native

        if encryption_algo == 'pbes2':
            salt = self['parameters']['key_derivation_func']['parameters']['salt']

            if salt.name == 'other_source':
                raise ValueError(unwrap(
                    '''
                    Can not determine key derivation salt - the
                    reserved-for-future-use other source salt choice was
                    specified in the PBKDF2 params structure
                    '''
                ))

            return salt.native

        if encryption_algo.find('.') == -1:
            if encryption_algo.find('_') != -1:
                return self['parameters']['salt'].native

            raise ValueError(unwrap(
                '''
                Encryption algorithm "%s" does not have a registered key
                derivation function
                ''',
                encryption_algo
            ))

        raise ValueError(unwrap(
            '''
            Unrecognized encryption algorithm "%s", can not determine key
            derivation salt
            ''',
            encryption_algo
        ))

    @property
    def kdf_iterations(self):
        """
        Returns the number of iterations that should be run via the KDF.

        :return:
            An integer
        """

        encryption_algo = self['algorithm'].native

        if encryption_algo == 'pbes2':
            return self['parameters']['key_derivation_func']['parameters']['iteration_count'].native

        if encryption_algo.find('.') == -1:
            if encryption_algo.find('_') != -1:
                return self['parameters']['iterations'].native

            raise ValueError(unwrap(
                '''
                Encryption algorithm "%s" does not have a registered key
                derivation function
                ''',
                encryption_algo
            ))

        raise ValueError(unwrap(
            '''
            Unrecognized encryption algorithm "%s", can not determine key
            derivation iterations
            ''',
            encryption_algo
        ))

    @property
    def key_length(self):
        """
        Returns the key length to pass to the cipher/kdf. The PKCS#5 spec does
        not specify a way to store the RC5 key length, however this tends not
        to be a problem since OpenSSL does not support RC5 in PKCS#8 and OS X
        does not provide an RC5 cipher for use in the Security Transforms
        library.

        :raises:
            ValueError - when the key length can not be determined

        :return:
            An integer representing the length in bytes
        """

        encryption_algo = self['algorithm'].native

        if encryption_algo[0:3] == 'aes':
            return {
                'aes128_': 16,
                'aes192_': 24,
                'aes256_': 32,
            }[encryption_algo[0:7]]

        cipher_lengths = {
            'des': 8,
            'tripledes_3key': 24,
        }

        if encryption_algo in cipher_lengths:
            return cipher_lengths[encryption_algo]

        if encryption_algo == 'rc2':
            rc2_parameter_version = self['parameters']['rc2_parameter_version'].native

            # See page 24 of
            # http://www.emc.com/collateral/white-papers/h11302-pkcs5v2-1-password-based-cryptography-standard-wp.pdf
            encoded_key_bits_map = {
                160: 5,   # 40-bit
                120: 8,   # 64-bit
                58: 16,   # 128-bit
            }

            if rc2_parameter_version in encoded_key_bits_map:
                return encoded_key_bits_map[rc2_parameter_version]

            if rc2_parameter_version >= 256:
                return rc2_parameter_version

            if rc2_parameter_version is None:
                return 4  # 32-bit default

            raise ValueError(unwrap(
                '''
                Invalid RC2 parameter version found in EncryptionAlgorithm
                parameters
                '''
            ))

        if encryption_algo == 'pbes2':
            key_length = self['parameters']['key_derivation_func']['parameters']['key_length'].native
            if key_length is not None:
                return key_length

            # If the KDF params don't specify the key size, we can infer it from
            # the encryption scheme for all schemes except for RC5. However, in
            # practical terms, neither OpenSSL or OS X support RC5 for PKCS#8
            # so it is unlikely to be an issue that is run into.

            return self['parameters']['encryption_scheme'].key_length

        if encryption_algo.find('.') == -1:
            return {
                'pbes1_md2_des': 8,
                'pbes1_md5_des': 8,
                'pbes1_md2_rc2': 8,
                'pbes1_md5_rc2': 8,
                'pbes1_sha1_des': 8,
                'pbes1_sha1_rc2': 8,
                'pkcs12_sha1_rc4_128': 16,
                'pkcs12_sha1_rc4_40': 5,
                'pkcs12_sha1_tripledes_3key': 24,
                'pkcs12_sha1_tripledes_2key': 16,
                'pkcs12_sha1_rc2_128': 16,
                'pkcs12_sha1_rc2_40': 5,
            }[encryption_algo]

        raise ValueError(unwrap(
            '''
            Unrecognized encryption algorithm "%s"
            ''',
            encryption_algo
        ))

    @property
    def encryption_mode(self):
        """
        Returns the name of the encryption mode to use.

        :return:
            A unicode string from one of the following: "cbc", "ecb", "ofb",
            "cfb", "wrap", "gcm", "ccm", "wrap_pad"
        """

        encryption_algo = self['algorithm'].native

        if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
            return encryption_algo[7:]

        if encryption_algo[0:6] == 'pbes1_':
            return 'cbc'

        if encryption_algo[0:7] == 'pkcs12_':
            return 'cbc'

        if encryption_algo in set(['des', 'tripledes_3key', 'rc2', 'rc5']):
            return 'cbc'

        if encryption_algo == 'pbes2':
            return self['parameters']['encryption_scheme'].encryption_mode

        raise ValueError(unwrap(
            '''
            Unrecognized encryption algorithm "%s"
            ''',
            encryption_algo
        ))

    @property
    def encryption_cipher(self):
        """
        Returns the name of the symmetric encryption cipher to use. The key
        length can be retrieved via the .key_length property to disabiguate
        between different variations of TripleDES, AES, and the RC* ciphers.

        :return:
            A unicode string from one of the following: "rc2", "rc5", "des",
            "tripledes", "aes"
        """

        encryption_algo = self['algorithm'].native

        if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
            return 'aes'

        if encryption_algo in set(['des', 'rc2', 'rc5']):
            return encryption_algo

        if encryption_algo == 'tripledes_3key':
            return 'tripledes'

        if encryption_algo == 'pbes2':
            return self['parameters']['encryption_scheme'].encryption_cipher

        if encryption_algo.find('.') == -1:
            return {
                'pbes1_md2_des': 'des',
                'pbes1_md5_des': 'des',
                'pbes1_md2_rc2': 'rc2',
                'pbes1_md5_rc2': 'rc2',
                'pbes1_sha1_des': 'des',
                'pbes1_sha1_rc2': 'rc2',
                'pkcs12_sha1_rc4_128': 'rc4',
                'pkcs12_sha1_rc4_40': 'rc4',
                'pkcs12_sha1_tripledes_3key': 'tripledes',
                'pkcs12_sha1_tripledes_2key': 'tripledes',
                'pkcs12_sha1_rc2_128': 'rc2',
                'pkcs12_sha1_rc2_40': 'rc2',
            }[encryption_algo]

        raise ValueError(unwrap(
            '''
            Unrecognized encryption algorithm "%s"
            ''',
            encryption_algo
        ))

    @property
    def encryption_block_size(self):
        """
        Returns the block size of the encryption cipher, in bytes.

        :return:
            An integer that is the block size in bytes
        """

        encryption_algo = self['algorithm'].native

        if encryption_algo[0:7] in set(['aes128_', 'aes192_', 'aes256_']):
            return 16

        cipher_map = {
            'des': 8,
            'tripledes_3key': 8,
            'rc2': 8,
        }
        if encryption_algo in cipher_map:
            return cipher_map[encryption_algo]

        if encryption_algo == 'rc5':
            return self['parameters']['block_size_in_bits'].native // 8

        if encryption_algo == 'pbes2':
            return self['parameters']['encryption_scheme'].encryption_block_size

        if encryption_algo.find('.') == -1:
            return {
                'pbes1_md2_des': 8,
                'pbes1_md5_des': 8,
                'pbes1_md2_rc2': 8,
                'pbes1_md5_rc2': 8,
                'pbes1_sha1_des': 8,
                'pbes1_sha1_rc2': 8,
                'pkcs12_sha1_rc4_128': 0,
                'pkcs12_sha1_rc4_40': 0,
                'pkcs12_sha1_tripledes_3key': 8,
                'pkcs12_sha1_tripledes_2key': 8,
                'pkcs12_sha1_rc2_128': 8,
                'pkcs12_sha1_rc2_40': 8,
            }[encryption_algo]

        raise ValueError(unwrap(
            '''
            Unrecognized encryption algorithm "%s"
            ''',
            encryption_algo
        ))

    @property
    def encryption_iv(self):
        """
        Returns the byte string of the initialization vector for the encryption
        scheme. Only the PBES2 stores the IV in the params. For PBES1, the IV
        is derived from the KDF and this property will return None.

        :return:
            A byte string or None
        """

        encryption_algo = self['algorithm'].native

        if encryption_algo in set(['rc2', 'rc5']):
            return self['parameters']['iv'].native

        # For DES/Triple DES and AES the IV is the entirety of the parameters
        octet_string_iv_oids = set([
            'des',
            'tripledes_3key',
            'aes128_cbc',
            'aes192_cbc',
            'aes256_cbc',
            'aes128_ofb',
            'aes192_ofb',
            'aes256_ofb',
        ])
        if encryption_algo in octet_string_iv_oids:
            return self['parameters'].native

        if encryption_algo == 'pbes2':
            return self['parameters']['encryption_scheme'].encryption_iv

        # All of the PBES1 algos use their KDF to create the IV. For the pbkdf1,
        # the KDF is told to generate a key that is an extra 8 bytes long, and
        # that is used for the IV. For the PKCS#12 KDF, it is called with an id
        # of 2 to generate the IV. In either case, we can't return the IV
        # without knowing the user's password.
        if encryption_algo.find('.') == -1:
            return None

        raise ValueError(unwrap(
            '''
            Unrecognized encryption algorithm "%s"
            ''',
            encryption_algo
        )) class EncryptionAlgorithmId(ObjectIdentifier):
    _map = {
        '1.3.14.3.2.7': 'des',
        '1.2.840.113549.3.7': 'tripledes_3key',
        '1.2.840.113549.3.2': 'rc2',
        '1.2.840.113549.3.4': 'rc4',
        '1.2.840.113549.3.9': 'rc5',
        # From http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html#AES
        '2.16.840.1.101.3.4.1.1': 'aes128_ecb',
        '2.16.840.1.101.3.4.1.2': 'aes128_cbc',
        '2.16.840.1.101.3.4.1.3': 'aes128_ofb',
        '2.16.840.1.101.3.4.1.4': 'aes128_cfb',
        '2.16.840.1.101.3.4.1.5': 'aes128_wrap',
        '2.16.840.1.101.3.4.1.6': 'aes128_gcm',
        '2.16.840.1.101.3.4.1.7': 'aes128_ccm',
        '2.16.840.1.101.3.4.1.8': 'aes128_wrap_pad',
        '2.16.840.1.101.3.4.1.21': 'aes192_ecb',
        '2.16.840.1.101.3.4.1.22': 'aes192_cbc',
        '2.16.840.1.101.3.4.1.23': 'aes192_ofb',
        '2.16.840.1.101.3.4.1.24': 'aes192_cfb',
        '2.16.840.1.101.3.4.1.25': 'aes192_wrap',
        '2.16.840.1.101.3.4.1.26': 'aes192_gcm',
        '2.16.840.1.101.3.4.1.27': 'aes192_ccm',
        '2.16.840.1.101.3.4.1.28': 'aes192_wrap_pad',
        '2.16.840.1.101.3.4.1.41': 'aes256_ecb',
        '2.16.840.1.101.3.4.1.42': 'aes256_cbc',
        '2.16.840.1.101.3.4.1.43': 'aes256_ofb',
        '2.16.840.1.101.3.4.1.44': 'aes256_cfb',
        '2.16.840.1.101.3.4.1.45': 'aes256_wrap',
        '2.16.840.1.101.3.4.1.46': 'aes256_gcm',
        '2.16.840.1.101.3.4.1.47': 'aes256_ccm',
        '2.16.840.1.101.3.4.1.48': 'aes256_wrap_pad',
        # From PKCS#5
        '1.2.840.113549.1.5.13': 'pbes2',
        '1.2.840.113549.1.5.1': 'pbes1_md2_des',
        '1.2.840.113549.1.5.3': 'pbes1_md5_des',
        '1.2.840.113549.1.5.4': 'pbes1_md2_rc2',
        '1.2.840.113549.1.5.6': 'pbes1_md5_rc2',
        '1.2.840.113549.1.5.10': 'pbes1_sha1_des',
        '1.2.840.113549.1.5.11': 'pbes1_sha1_rc2',
        # From PKCS#12
        '1.2.840.113549.1.12.1.1': 'pkcs12_sha1_rc4_128',
        '1.2.840.113549.1.12.1.2': 'pkcs12_sha1_rc4_40',
        '1.2.840.113549.1.12.1.3': 'pkcs12_sha1_tripledes_3key',
        '1.2.840.113549.1.12.1.4': 'pkcs12_sha1_tripledes_2key',
        '1.2.840.113549.1.12.1.5': 'pkcs12_sha1_rc2_128',
        '1.2.840.113549.1.12.1.6': 'pkcs12_sha1_rc2_40',
        # PKCS#1 v2.2
        '1.2.840.113549.1.1.1': 'rsaes_pkcs1v15',
        '1.2.840.113549.1.1.7': 'rsaes_oaep',
    } class HmacAlgorithm(Sequence):
    _fields = [
        ('algorithm', HmacAlgorithmId),
        ('parameters', Any, {'optional': True}),
    ] class KdfAlgorithm(Sequence):
    _fields = [
        ('algorithm', KdfAlgorithmId),
        ('parameters', Any, {'optional': True}),
    ]
    _oid_pair = ('algorithm', 'parameters')
    _oid_specs = {
        'pbkdf2': Pbkdf2Params
    } class RSAESOAEPParams(Sequence):
    _fields = [
        (
            'hash_algorithm',
            DigestAlgorithm,
            {
                'explicit': 0,
                'default': {'algorithm': 'sha1'}
            }
        ),
        (
            'mask_gen_algorithm',
            MaskGenAlgorithm,
            {
                'explicit': 1,
                'default': {
                    'algorithm': 'mgf1',
                    'parameters': {'algorithm': 'sha1'}
                }
            }
        ),
        (
            'p_source_algorithm',
            PSourceAlgorithm,
            {
                'explicit': 2,
                'default': {
                    'algorithm': 'p_specified',
                    'parameters': b''
                }
            }
        ),
    ] class SignedDigestAlgorithm(_ForceNullParameters, Sequence):
    _fields = [
        ('algorithm', SignedDigestAlgorithmId),
        ('parameters', Any, {'optional': True}),
    ]

    _oid_pair = ('algorithm', 'parameters')
    _oid_specs = {
        'rsassa_pss': RSASSAPSSParams,
    }

    @property
    def signature_algo(self):
        """
        :return:
            A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa",
            "ecdsa", "ed25519" or "ed448"
        """

        algorithm = self['algorithm'].native

        algo_map = {
            'md2_rsa': 'rsassa_pkcs1v15',
            'md5_rsa': 'rsassa_pkcs1v15',
            'sha1_rsa': 'rsassa_pkcs1v15',
            'sha224_rsa': 'rsassa_pkcs1v15',
            'sha256_rsa': 'rsassa_pkcs1v15',
            'sha384_rsa': 'rsassa_pkcs1v15',
            'sha512_rsa': 'rsassa_pkcs1v15',
            'rsassa_pkcs1v15': 'rsassa_pkcs1v15',
            'rsassa_pss': 'rsassa_pss',
            'sha1_dsa': 'dsa',
            'sha224_dsa': 'dsa',
            'sha256_dsa': 'dsa',
            'dsa': 'dsa',
            'sha1_ecdsa': 'ecdsa',
            'sha224_ecdsa': 'ecdsa',
            'sha256_ecdsa': 'ecdsa',
            'sha384_ecdsa': 'ecdsa',
            'sha512_ecdsa': 'ecdsa',
            'sha3_224_ecdsa': 'ecdsa',
            'sha3_256_ecdsa': 'ecdsa',
            'sha3_384_ecdsa': 'ecdsa',
            'sha3_512_ecdsa': 'ecdsa',
            'ecdsa': 'ecdsa',
            'ed25519': 'ed25519',
            'ed448': 'ed448',
        }
        if algorithm in algo_map:
            return algo_map[algorithm]

        raise ValueError(unwrap(
            '''
            Signature algorithm not known for %s
            ''',
            algorithm
        ))

    @property
    def hash_algo(self):
        """
        :return:
            A unicode string of "md2", "md5", "sha1", "sha224", "sha256",
            "sha384", "sha512", "sha512_224", "sha512_256" or "shake256"
        """

        algorithm = self['algorithm'].native

        algo_map = {
            'md2_rsa': 'md2',
            'md5_rsa': 'md5',
            'sha1_rsa': 'sha1',
            'sha224_rsa': 'sha224',
            'sha256_rsa': 'sha256',
            'sha384_rsa': 'sha384',
            'sha512_rsa': 'sha512',
            'sha1_dsa': 'sha1',
            'sha224_dsa': 'sha224',
            'sha256_dsa': 'sha256',
            'sha1_ecdsa': 'sha1',
            'sha224_ecdsa': 'sha224',
            'sha256_ecdsa': 'sha256',
            'sha384_ecdsa': 'sha384',
            'sha512_ecdsa': 'sha512',
            'ed25519': 'sha512',
            'ed448': 'shake256',
        }
        if algorithm in algo_map:
            return algo_map[algorithm]

        if algorithm == 'rsassa_pss':
            return self['parameters']['hash_algorithm']['algorithm'].native

        raise ValueError(unwrap(
            '''
            Hash algorithm not known for %s
            ''',
            algorithm
        )) class Any(Asn1Value):
    """
    A value class that can contain any value, and allows for easy parsing of
    the underlying encoded value using a spec. This is normally contained in
    a Structure that has an ObjectIdentifier field and _oid_pair and _oid_specs
    defined.
    """

    # The parsed value object
    _parsed = None

    def __init__(self, value=None, **kwargs):
        """
        Sets the value of the object before passing to Asn1Value.__init__()

        :param value:
            An Asn1Value object that will be set as the parsed value
        """

        Asn1Value.__init__(self, **kwargs)

        try:
            if value is not None:
                if not isinstance(value, Asn1Value):
                    raise TypeError(unwrap(
                        '''
                        value must be an instance of Asn1Value, not %s
                        ''',
                        type_name(value)
                    ))

                self._parsed = (value, value.__class__, None)
                self.contents = value.dump()

        except (ValueError, TypeError) as e:
            args = e.args[1:]
            e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
            raise e

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            The .native value from the parsed value object
        """

        if self._parsed is None:
            self.parse()

        return self._parsed[0].native

    @property
    def parsed(self):
        """
        Returns the parsed object from .parse()

        :return:
            The object returned by .parse()
        """

        if self._parsed is None:
            self.parse()

        return self._parsed[0]

    def parse(self, spec=None, spec_params=None):
        """
        Parses the contents generically, or using a spec with optional params

        :param spec:
            A class derived from Asn1Value that defines what class_ and tag the
            value should have, and the semantics of the encoded value. The
            return value will be of this type. If omitted, the encoded value
            will be decoded using the standard universal tag based on the
            encoded tag number.

        :param spec_params:
            A dict of params to pass to the spec object

        :return:
            An object of the type spec, or if not present, a child of Asn1Value
        """

        if self._parsed is None or self._parsed[1:3] != (spec, spec_params):
            try:
                passed_params = spec_params or {}
                _tag_type_to_explicit_implicit(passed_params)
                if self.explicit is not None:
                    if 'explicit' in passed_params:
                        passed_params['explicit'] = self.explicit + passed_params['explicit']
                    else:
                        passed_params['explicit'] = self.explicit
                contents = self._header + self.contents + self._trailer
                parsed_value, _ = _parse_build(
                    contents,
                    spec=spec,
                    spec_params=passed_params
                )
                self._parsed = (parsed_value, spec, spec_params)

                # Once we've parsed the Any value, clear any attributes from this object
                # since they are now duplicate
                self.tag = None
                self.explicit = None
                self.implicit = False
                self._header = b''
                self.contents = contents
                self._trailer = b''

            except (ValueError, TypeError) as e:
                args = e.args[1:]
                e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
                raise e
        return self._parsed[0]

    def _copy(self, other, copy_func):
        """
        Copies the contents of another Any object to itself

        :param object:
            Another instance of the same class

        :param copy_func:
            An reference of copy.copy() or copy.deepcopy() to use when copying
            lists, dicts and objects
        """

        super(Any, self)._copy(other, copy_func)
        self._parsed = copy_func(other._parsed)

    def dump(self, force=False):
        """
        Encodes the value using DER

        :param force:
            If the encoded contents already exist, clear them and regenerate
            to ensure they are in DER format instead of BER format

        :return:
            A byte string of the DER-encoded value
        """

        if self._parsed is None:
            self.parse()

        return self._parsed[0].dump(force=force) class BitString(_IntegerBitString, Constructable, Castable, Primitive, ValueMap):
    """
    Represents a bit string from ASN.1 as a Python tuple of 1s and 0s
    """

    tag = 3

    _size = None

    def _setup(self):
        """
        Generates _reverse_map from _map
        """

        ValueMap._setup(self)

        cls = self.__class__
        if cls._map is not None:
            cls._size = max(self._map.keys()) + 1

    def set(self, value):
        """
        Sets the value of the object

        :param value:
            An integer or a tuple of integers 0 and 1

        :raises:
            ValueError - when an invalid value is passed
        """

        if isinstance(value, set):
            if self._map is None:
                raise ValueError(unwrap(
                    '''
                    %s._map has not been defined
                    ''',
                    type_name(self)
                ))

            bits = [0] * self._size
            self._native = value
            for index in range(0, self._size):
                key = self._map.get(index)
                if key is None:
                    continue
                if key in value:
                    bits[index] = 1

            value = ''.join(map(str_cls, bits))

        elif value.__class__ == tuple:
            if self._map is None:
                self._native = value
            else:
                self._native = set()
                for index, bit in enumerate(value):
                    if bit:
                        name = self._map.get(index, index)
                        self._native.add(name)
            value = ''.join(map(str_cls, value))

        else:
            raise TypeError(unwrap(
                '''
                %s value must be a tuple of ones and zeros or a set of unicode
                strings, not %s
                ''',
                type_name(self),
                type_name(value)
            ))

        if self._map is not None:
            if len(value) > self._size:
                raise ValueError(unwrap(
                    '''
                    %s value must be at most %s bits long, specified was %s long
                    ''',
                    type_name(self),
                    self._size,
                    len(value)
                ))
            # A NamedBitList must have trailing zero bit truncated. See
            # https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
            # section 11.2,
            # https://tools.ietf.org/html/rfc5280#page-134 and
            # https://www.ietf.org/mail-archive/web/pkix/current/msg10443.html
            value = value.rstrip('0')
        size = len(value)

        size_mod = size % 8
        extra_bits = 0
        if size_mod != 0:
            extra_bits = 8 - size_mod
            value += '0' * extra_bits

        size_in_bytes = int(math.ceil(size / 8))

        if extra_bits:
            extra_bits_byte = int_to_bytes(extra_bits)
        else:
            extra_bits_byte = b'\x00'

        if value == '':
            value_bytes = b''
        else:
            value_bytes = int_to_bytes(int(value, 2))
        if len(value_bytes) != size_in_bytes:
            value_bytes = (b'\x00' * (size_in_bytes - len(value_bytes))) + value_bytes

        self.contents = extra_bits_byte + value_bytes
        self._unused_bits = (0,) * extra_bits
        self._header = None
        if self._indefinite:
            self._indefinite = False
            self.method = 0
        if self._trailer != b'':
            self._trailer = b''

    def __getitem__(self, key):
        """
        Retrieves a boolean version of one of the bits based on a name from the
        _map

        :param key:
            The unicode string of one of the bit names

        :raises:
            ValueError - when _map is not set or the key name is invalid

        :return:
            A boolean if the bit is set
        """

        is_int = isinstance(key, int_types)
        if not is_int:
            if not isinstance(self._map, dict):
                raise ValueError(unwrap(
                    '''
                    %s._map has not been defined
                    ''',
                    type_name(self)
                ))

            if key not in self._reverse_map:
                raise ValueError(unwrap(
                    '''
                    %s._map does not contain an entry for "%s"
                    ''',
                    type_name(self),
                    key
                ))

        if self._native is None:
            self.native

        if self._map is None:
            if len(self._native) >= key + 1:
                return bool(self._native[key])
            return False

        if is_int:
            key = self._map.get(key, key)

        return key in self._native

    def __setitem__(self, key, value):
        """
        Sets one of the bits based on a name from the _map

        :param key:
            The unicode string of one of the bit names

        :param value:
            A boolean value

        :raises:
            ValueError - when _map is not set or the key name is invalid
        """

        is_int = isinstance(key, int_types)
        if not is_int:
            if self._map is None:
                raise ValueError(unwrap(
                    '''
                    %s._map has not been defined
                    ''',
                    type_name(self)
                ))

            if key not in self._reverse_map:
                raise ValueError(unwrap(
                    '''
                    %s._map does not contain an entry for "%s"
                    ''',
                    type_name(self),
                    key
                ))

        if self._native is None:
            self.native

        if self._map is None:
            new_native = list(self._native)
            max_key = len(new_native) - 1
            if key > max_key:
                new_native.extend([0] * (key - max_key))
            new_native[key] = 1 if value else 0
            self._native = tuple(new_native)

        else:
            if is_int:
                key = self._map.get(key, key)

            if value:
                if key not in self._native:
                    self._native.add(key)
            else:
                if key in self._native:
                    self._native.remove(key)

        self.set(self._native)

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            If a _map is set, a set of names, or if no _map is set, a tuple of
            integers 1 and 0. None if no value.
        """

        # For BitString we default the value to be all zeros
        if self.contents is None:
            if self._map is None:
                self.set(())
            else:
                self.set(set())

        if self._native is None:
            int_value, bit_count, self._unused_bits = self._chunks_to_int()
            bits = _int_to_bit_tuple(int_value, bit_count)

            if self._map:
                self._native = set()
                for index, bit in enumerate(bits):
                    if bit:
                        name = self._map.get(index, index)
                        self._native.add(name)
            else:
                self._native = bits
        return self._native class Choice(Asn1Value):
    """
    A class to handle when a value may be one of several options
    """

    # The index in _alternatives of the validated alternative
    _choice = None

    # The name of the chosen alternative
    _name = None

    # The Asn1Value object for the chosen alternative
    _parsed = None

    # Choice overrides .contents to be a property so that the code expecting
    # the .contents attribute will get the .contents of the chosen alternative
    _contents = None

    # A list of tuples in one of the following forms.
    #
    # Option 1, a unicode string field name and a value class
    #
    # ("name", Asn1ValueClass)
    #
    # Option 2, same as Option 1, but with a dict of class params
    #
    # ("name", Asn1ValueClass, {'explicit': 5})
    _alternatives = None

    # A dict that maps tuples of (class_, tag) to an index in _alternatives
    _id_map = None

    # A dict that maps alternative names to an index in _alternatives
    _name_map = None

    @classmethod
    def load(cls, encoded_data, strict=False, **kwargs):
        """
        Loads a BER/DER-encoded byte string using the current class as the spec

        :param encoded_data:
            A byte string of BER or DER encoded data

        :param strict:
            A boolean indicating if trailing data should be forbidden - if so, a
            ValueError will be raised when trailing data exists

        :return:
            A instance of the current class
        """

        if not isinstance(encoded_data, byte_cls):
            raise TypeError('encoded_data must be a byte string, not %s' % type_name(encoded_data))

        value, _ = _parse_build(encoded_data, spec=cls, spec_params=kwargs, strict=strict)
        return value

    def _setup(self):
        """
        Generates _id_map from _alternatives to allow validating contents
        """

        cls = self.__class__
        cls._id_map = {}
        cls._name_map = {}
        for index, info in enumerate(cls._alternatives):
            if len(info) < 3:
                info = info + ({},)
                cls._alternatives[index] = info
            id_ = _build_id_tuple(info[2], info[1])
            cls._id_map[id_] = index
            cls._name_map[info[0]] = index

    def __init__(self, name=None, value=None, **kwargs):
        """
        Checks to ensure implicit tagging is not being used since it is
        incompatible with Choice, then forwards on to Asn1Value.__init__()

        :param name:
            The name of the alternative to be set - used with value.
            Alternatively this may be a dict with a single key being the name
            and the value being the value, or a two-element tuple of the name
            and the value.

        :param value:
            The alternative value to set - used with name

        :raises:
            ValueError - when implicit param is passed (or legacy tag_type param is "implicit")
        """

        _tag_type_to_explicit_implicit(kwargs)

        Asn1Value.__init__(self, **kwargs)

        try:
            if kwargs.get('implicit') is not None:
                raise ValueError(unwrap(
                    '''
                    The Choice type can not be implicitly tagged even if in an
                    implicit module - due to its nature any tagging must be
                    explicit
                    '''
                ))

            if name is not None:
                if isinstance(name, dict):
                    if len(name) != 1:
                        raise ValueError(unwrap(
                            '''
                            When passing a dict as the "name" argument to %s,
                            it must have a single key/value - however %d were
                            present
                            ''',
                            type_name(self),
                            len(name)
                        ))
                    name, value = list(name.items())[0]

                if isinstance(name, tuple):
                    if len(name) != 2:
                        raise ValueError(unwrap(
                            '''
                            When passing a tuple as the "name" argument to %s,
                            it must have two elements, the name and value -
                            however %d were present
                            ''',
                            type_name(self),
                            len(name)
                        ))
                    value = name[1]
                    name = name[0]

                if name not in self._name_map:
                    raise ValueError(unwrap(
                        '''
                        The name specified, "%s", is not a valid alternative
                        for %s
                        ''',
                        name,
                        type_name(self)
                    ))

                self._choice = self._name_map[name]
                _, spec, params = self._alternatives[self._choice]

                if not isinstance(value, spec):
                    value = spec(value, **params)
                else:
                    value = _fix_tagging(value, params)
                self._parsed = value

        except (ValueError, TypeError) as e:
            args = e.args[1:]
            e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
            raise e

    @property
    def contents(self):
        """
        :return:
            A byte string of the DER-encoded contents of the chosen alternative
        """

        if self._parsed is not None:
            return self._parsed.contents

        return self._contents

    @contents.setter
    def contents(self, value):
        """
        :param value:
            A byte string of the DER-encoded contents of the chosen alternative
        """

        self._contents = value

    @property
    def name(self):
        """
        :return:
            A unicode string of the field name of the chosen alternative
        """
        if not self._name:
            self._name = self._alternatives[self._choice][0]
        return self._name

    def parse(self):
        """
        Parses the detected alternative

        :return:
            An Asn1Value object of the chosen alternative
        """

        if self._parsed is None:
            try:
                _, spec, params = self._alternatives[self._choice]
                self._parsed, _ = _parse_build(self._contents, spec=spec, spec_params=params)
            except (ValueError, TypeError) as e:
                args = e.args[1:]
                e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
                raise e
        return self._parsed

    @property
    def chosen(self):
        """
        :return:
            An Asn1Value object of the chosen alternative
        """

        return self.parse()

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            The .native value from the contained value object
        """

        return self.chosen.native

    def validate(self, class_, tag, contents):
        """
        Ensures that the class and tag specified exist as an alternative

        :param class_:
            The integer class_ from the encoded value header

        :param tag:
            The integer tag from the encoded value header

        :param contents:
            A byte string of the contents of the value - used when the object
            is explicitly tagged

        :raises:
            ValueError - when value is not a valid alternative
        """

        id_ = (class_, tag)

        if self.explicit is not None:
            if self.explicit[-1] != id_:
                raise ValueError(unwrap(
                    '''
                    %s was explicitly tagged, but the value provided does not
                    match the class and tag
                    ''',
                    type_name(self)
                ))

            ((class_, _, tag, _, _, _), _) = _parse(contents, len(contents))
            id_ = (class_, tag)

        if id_ in self._id_map:
            self._choice = self._id_map[id_]
            return

        # This means the Choice was implicitly tagged
        if self.class_ is not None and self.tag is not None:
            if len(self._alternatives) > 1:
                raise ValueError(unwrap(
                    '''
                    %s was implicitly tagged, but more than one alternative
                    exists
                    ''',
                    type_name(self)
                ))
            if id_ == (self.class_, self.tag):
                self._choice = 0
                return

        asn1 = self._format_class_tag(class_, tag)
        asn1s = [self._format_class_tag(pair[0], pair[1]) for pair in self._id_map]

        raise ValueError(unwrap(
            '''
            Value %s did not match the class and tag of any of the alternatives
            in %s: %s
            ''',
            asn1,
            type_name(self),
            ', '.join(asn1s)
        ))

    def _format_class_tag(self, class_, tag):
        """
        :return:
            A unicode string of a human-friendly representation of the class and tag
        """

        return '[%s %s]' % (CLASS_NUM_TO_NAME_MAP[class_].upper(), tag)

    def _copy(self, other, copy_func):
        """
        Copies the contents of another Choice object to itself

        :param object:
            Another instance of the same class

        :param copy_func:
            An reference of copy.copy() or copy.deepcopy() to use when copying
            lists, dicts and objects
        """

        super(Choice, self)._copy(other, copy_func)
        self._choice = other._choice
        self._name = other._name
        self._parsed = copy_func(other._parsed)

    def dump(self, force=False):
        """
        Encodes the value using DER

        :param force:
            If the encoded contents already exist, clear them and regenerate
            to ensure they are in DER format instead of BER format

        :return:
            A byte string of the DER-encoded value
        """

        # If the length is indefinite, force the re-encoding
        if self._header is not None and self._header[-1:] == b'\x80':
            force = True

        self._contents = self.chosen.dump(force=force)
        if self._header is None or force:
            self._header = b''
            if self.explicit is not None:
                for class_, tag in self.explicit:
                    self._header = _dump_header(class_, 1, tag, self._header + self._contents) + self._header
        return self._header + self._contents class Enumerated(Integer):
    """
    Represents a enumerated list of integers from ASN.1 as a Python
    unicode string
    """

    tag = 10

    def set(self, value):
        """
        Sets the value of the object

        :param value:
            An integer or a unicode string from _map

        :raises:
            ValueError - when an invalid value is passed
        """

        if not isinstance(value, int_types) and not isinstance(value, str_cls):
            raise TypeError(unwrap(
                '''
                %s value must be an integer or a unicode string, not %s
                ''',
                type_name(self),
                type_name(value)
            ))

        if isinstance(value, str_cls):
            if value not in self._reverse_map:
                raise ValueError(unwrap(
                    '''
                    %s value "%s" is not a valid value
                    ''',
                    type_name(self),
                    value
                ))

            value = self._reverse_map[value]

        elif value not in self._map:
            raise ValueError(unwrap(
                '''
                %s value %s is not a valid value
                ''',
                type_name(self),
                value
            ))

        Integer.set(self, value)

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            A unicode string or None
        """

        if self.contents is None:
            return None

        if self._native is None:
            self._native = self._map[self.__int__()]
        return self._native class GeneralizedTime(AbstractTime):
    """
    Represents a generalized time from ASN.1 as a Python datetime.datetime
    object or asn1crypto.util.extended_datetime object in UTC
    """

    tag = 24

    # Regular expression for GeneralizedTime as described in X.680 sect. 42 and ISO 8601
    _TIMESTRING_RE = re.compile(r'''
        ^
        # YYYYMMDD
        (?P<year>\d{4})
        (?P<month>\d{2})
        (?P<day>\d{2})

        # hh or hhmm or hhmmss
        (?P<hour>\d{2})
        (?:
            (?P<minute>\d{2})
            (?P<second>\d{2})?
        )?

        # Optional fraction; [.,]dddd (one or more decimals)
        # If Seconds are given, it's fractions of Seconds.
        # Else if Minutes are given, it's fractions of Minutes.
        # Else it's fractions of Hours.
        (?:
            [,.]
            (?P<fraction>\d+)
        )?

        # Optional timezone. If left out, the time is in local time.
        # Z or [-+]hh or [-+]hhmm
        (?:
            (?P<zulu>Z)
            |
            (?:
                (?P<dsign>[-+])
                (?P<dhour>\d{2})
                (?P<dminute>\d{2})?
            )
        )?
        $
    ''', re.X)

    def set(self, value):
        """
        Sets the value of the object

        :param value:
            A unicode string, a datetime.datetime object or an
            asn1crypto.util.extended_datetime object

        :raises:
            ValueError - when an invalid value is passed
        """

        if isinstance(value, (datetime, extended_datetime)):
            if not value.tzinfo:
                raise ValueError('Must be timezone aware')

            # Convert value to UTC.
            value = value.astimezone(utc_with_dst)

            if value.microsecond:
                fraction = '.' + str(value.microsecond).zfill(6).rstrip('0')
            else:
                fraction = ''

            value = value.strftime('%Y%m%d%H%M%S') + fraction + 'Z'
            if _PY2:
                value = value.decode('ascii')

        AbstractString.set(self, value)
        # Set it to None and let the class take care of converting the next
        # time that .native is called
        self._native = None

    def _get_datetime(self, parsed):
        """
        Create a datetime object from the parsed time.

        :return:
            A datetime.datetime object or asn1crypto.util.extended_datetime object.
            It may or may not be aware.
        """

        if parsed['year'] == 0:
            # datetime does not support year 0. Use extended_datetime instead.
            return extended_datetime(**parsed)
        else:
            return datetime(**parsed) class Integer(Primitive, ValueMap):
    """
    Represents an integer in both ASN.1 and Python
    """

    tag = 2

    def set(self, value):
        """
        Sets the value of the object

        :param value:
            An integer, or a unicode string if _map is set

        :raises:
            ValueError - when an invalid value is passed
        """

        if isinstance(value, str_cls):
            if self._map is None:
                raise ValueError(unwrap(
                    '''
                    %s value is a unicode string, but no _map provided
                    ''',
                    type_name(self)
                ))

            if value not in self._reverse_map:
                raise ValueError(unwrap(
                    '''
                    %s value, %s, is not present in the _map
                    ''',
                    type_name(self),
                    value
                ))

            value = self._reverse_map[value]

        elif not isinstance(value, int_types):
            raise TypeError(unwrap(
                '''
                %s value must be an integer or unicode string when a name_map
                is provided, not %s
                ''',
                type_name(self),
                type_name(value)
            ))

        self._native = self._map[value] if self._map and value in self._map else value

        self.contents = int_to_bytes(value, signed=True)
        self._header = None
        if self._trailer != b'':
            self._trailer = b''

    def __int__(self):
        """
        :return:
            An integer
        """
        return int_from_bytes(self.contents, signed=True)

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            An integer or None
        """

        if self.contents is None:
            return None

        if self._native is None:
            self._native = self.__int__()
            if self._map is not None and self._native in self._map:
                self._native = self._map[self._native]
        return self._native class ObjectIdentifier(Primitive, ValueMap):
    """
    Represents an object identifier in ASN.1 as a Python unicode dotted
    integer string
    """

    tag = 6

    # A unicode string of the dotted form of the object identifier
    _dotted = None

    @classmethod
    def map(cls, value):
        """
        Converts a dotted unicode string OID into a mapped unicode string

        :param value:
            A dotted unicode string OID

        :raises:
            ValueError - when no _map dict has been defined on the class
            TypeError - when value is not a unicode string

        :return:
            A mapped unicode string
        """

        if cls._map is None:
            raise ValueError(unwrap(
                '''
                %s._map has not been defined
                ''',
                type_name(cls)
            ))

        if not isinstance(value, str_cls):
            raise TypeError(unwrap(
                '''
                value must be a unicode string, not %s
                ''',
                type_name(value)
            ))

        return cls._map.get(value, value)

    @classmethod
    def unmap(cls, value):
        """
        Converts a mapped unicode string value into a dotted unicode string OID

        :param value:
            A mapped unicode string OR dotted unicode string OID

        :raises:
            ValueError - when no _map dict has been defined on the class or the value can't be unmapped
            TypeError - when value is not a unicode string

        :return:
            A dotted unicode string OID
        """

        if cls not in _SETUP_CLASSES:
            cls()._setup()
            _SETUP_CLASSES[cls] = True

        if cls._map is None:
            raise ValueError(unwrap(
                '''
                %s._map has not been defined
                ''',
                type_name(cls)
            ))

        if not isinstance(value, str_cls):
            raise TypeError(unwrap(
                '''
                value must be a unicode string, not %s
                ''',
                type_name(value)
            ))

        if value in cls._reverse_map:
            return cls._reverse_map[value]

        if not _OID_RE.match(value):
            raise ValueError(unwrap(
                '''
                %s._map does not contain an entry for "%s"
                ''',
                type_name(cls),
                value
            ))

        return value

    def set(self, value):
        """
        Sets the value of the object

        :param value:
            A unicode string. May be a dotted integer string, or if _map is
            provided, one of the mapped values.

        :raises:
            ValueError - when an invalid value is passed
        """

        if not isinstance(value, str_cls):
            raise TypeError(unwrap(
                '''
                %s value must be a unicode string, not %s
                ''',
                type_name(self),
                type_name(value)
            ))

        self._native = value

        if self._map is not None:
            if value in self._reverse_map:
                value = self._reverse_map[value]

        self.contents = b''
        first = None
        for index, part in enumerate(value.split('.')):
            part = int(part)

            # The first two parts are merged into a single byte
            if index == 0:
                first = part
                continue
            elif index == 1:
                if first > 2:
                    raise ValueError(unwrap(
                        '''
                        First arc must be one of 0, 1 or 2, not %s
                        ''',
                        repr(first)
                    ))
                elif first < 2 and part >= 40:
                    raise ValueError(unwrap(
                        '''
                        Second arc must be less than 40 if first arc is 0 or
                        1, not %s
                        ''',
                        repr(part)
                    ))
                part = (first * 40) + part

            encoded_part = chr_cls(0x7F & part)
            part = part >> 7
            while part > 0:
                encoded_part = chr_cls(0x80 | (0x7F & part)) + encoded_part
                part = part >> 7
            self.contents += encoded_part

        self._header = None
        if self._trailer != b'':
            self._trailer = b''

    def __unicode__(self):
        """
        :return:
            A unicode string
        """

        return self.dotted

    @property
    def dotted(self):
        """
        :return:
            A unicode string of the object identifier in dotted notation, thus
            ignoring any mapped value
        """

        if self._dotted is None:
            output = []

            part = 0
            for byte in self.contents:
                if _PY2:
                    byte = ord(byte)
                part = part * 128
                part += byte & 127
                # Last byte in subidentifier has the eighth bit set to 0
                if byte & 0x80 == 0:
                    if len(output) == 0:
                        if part >= 80:
                            output.append(str_cls(2))
                            output.append(str_cls(part - 80))
                        elif part >= 40:
                            output.append(str_cls(1))
                            output.append(str_cls(part - 40))
                        else:
                            output.append(str_cls(0))
                            output.append(str_cls(part))
                    else:
                        output.append(str_cls(part))
                    part = 0

            self._dotted = '.'.join(output)
        return self._dotted

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            A unicode string or None. If _map is not defined, the unicode string
            is a string of dotted integers. If _map is defined and the dotted
            string is present in the _map, the mapped value is returned.
        """

        if self.contents is None:
            return None

        if self._native is None:
            self._native = self.dotted
        if self._map is not None and self._native in self._map:
            self._native = self._map[self._native]
        return self._native class OctetBitString(Constructable, Castable, Primitive):
    """
    Represents a bit string in ASN.1 as a Python byte string
    """

    tag = 3

    # Instance attribute of (possibly-merged) byte string
    _bytes = None

    # Tuple of 1s and 0s; set through native
    _unused_bits = ()

    def set(self, value):
        """
        Sets the value of the object

        :param value:
            A byte string

        :raises:
            ValueError - when an invalid value is passed
        """

        if not isinstance(value, byte_cls):
            raise TypeError(unwrap(
                '''
                %s value must be a byte string, not %s
                ''',
                type_name(self),
                type_name(value)
            ))

        self._bytes = value
        # Set the unused bits to 0
        self.contents = b'\x00' + value
        self._unused_bits = ()
        self._header = None
        if self._indefinite:
            self._indefinite = False
            self.method = 0
        if self._trailer != b'':
            self._trailer = b''

    def __bytes__(self):
        """
        :return:
            A byte string
        """

        if self.contents is None:
            return b''
        if self._bytes is None:
            if not self._indefinite:
                self._bytes, self._unused_bits = self._as_chunk()[0]
            else:
                chunks = self._merge_chunks()
                self._unused_bits = ()
                for chunk in chunks:
                    if self._unused_bits:
                        # Disallowed by X.690 §8.6.4
                        raise ValueError('Only last chunk in a bit string may have unused bits')
                    self._unused_bits = chunk[1]
                self._bytes = b''.join(chunk[0] for chunk in chunks)

        return self._bytes

    def _copy(self, other, copy_func):
        """
        Copies the contents of another OctetBitString object to itself

        :param object:
            Another instance of the same class

        :param copy_func:
            An reference of copy.copy() or copy.deepcopy() to use when copying
            lists, dicts and objects
        """

        super(OctetBitString, self)._copy(other, copy_func)
        self._bytes = other._bytes
        self._unused_bits = other._unused_bits

    def _as_chunk(self):
        """
        Allows reconstructing indefinite length values

        :raises:
            ValueError - when an invalid value is passed

        :return:
            List with one tuple, consisting of a byte string and an integer (unused bits)
        """

        unused_bits_len = ord(self.contents[0]) if _PY2 else self.contents[0]
        if not unused_bits_len:
            return [(self.contents[1:], ())]

        if len(self.contents) == 1:
            # Disallowed by X.690 §8.6.2.3
            raise ValueError('Empty bit string has {0} unused bits'.format(unused_bits_len))

        if unused_bits_len > 7:
            # Disallowed by X.690 §8.6.2.2
            raise ValueError('Bit string has {0} unused bits'.format(unused_bits_len))

        mask = (1 << unused_bits_len) - 1
        last_byte = ord(self.contents[-1]) if _PY2 else self.contents[-1]

        # zero out the unused bits in the last byte.
        zeroed_byte = last_byte & ~mask
        value = self.contents[1:-1] + (chr(zeroed_byte) if _PY2 else bytes((zeroed_byte,)))

        unused_bits = _int_to_bit_tuple(last_byte & mask, unused_bits_len)

        return [(value, unused_bits)]

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            A byte string or None
        """

        if self.contents is None:
            return None

        return self.__bytes__()

    @property
    def unused_bits(self):
        """
        The unused bits of the bit string encoding.

        :return:
            A tuple of 1s and 0s
        """

        # call native to set _unused_bits
        self.native

        return self._unused_bits class OctetString(Constructable, Castable, Primitive):
    """
    Represents a byte string in both ASN.1 and Python
    """

    tag = 4

    # Instance attribute of (possibly-merged) byte string
    _bytes = None

    def set(self, value):
        """
        Sets the value of the object

        :param value:
            A byte string
        """

        if not isinstance(value, byte_cls):
            raise TypeError(unwrap(
                '''
                %s value must be a byte string, not %s
                ''',
                type_name(self),
                type_name(value)
            ))

        self._bytes = value
        self.contents = value
        self._header = None
        if self._indefinite:
            self._indefinite = False
            self.method = 0
        if self._trailer != b'':
            self._trailer = b''

    def __bytes__(self):
        """
        :return:
            A byte string
        """

        if self.contents is None:
            return b''
        if self._bytes is None:
            self._bytes = self._merge_chunks()
        return self._bytes

    def _copy(self, other, copy_func):
        """
        Copies the contents of another OctetString object to itself

        :param object:
            Another instance of the same class

        :param copy_func:
            An reference of copy.copy() or copy.deepcopy() to use when copying
            lists, dicts and objects
        """

        super(OctetString, self)._copy(other, copy_func)
        self._bytes = other._bytes

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            A byte string or None
        """

        if self.contents is None:
            return None

        return self.__bytes__() class ParsableOctetString(Constructable, Castable, Primitive):

    tag = 4

    _parsed = None

    # Instance attribute of (possibly-merged) byte string
    _bytes = None

    def __init__(self, value=None, parsed=None, **kwargs):
        """
        Allows providing a parsed object that will be serialized to get the
        byte string value

        :param value:
            A native Python datatype to initialize the object value with

        :param parsed:
            If value is None and this is an Asn1Value object, this will be
            set as the parsed value, and the value will be obtained by calling
            .dump() on this object.
        """

        set_parsed = False
        if value is None and parsed is not None and isinstance(parsed, Asn1Value):
            value = parsed.dump()
            set_parsed = True

        Primitive.__init__(self, value=value, **kwargs)

        if set_parsed:
            self._parsed = (parsed, parsed.__class__, None)

    def set(self, value):
        """
        Sets the value of the object

        :param value:
            A byte string
        """

        if not isinstance(value, byte_cls):
            raise TypeError(unwrap(
                '''
                %s value must be a byte string, not %s
                ''',
                type_name(self),
                type_name(value)
            ))

        self._bytes = value
        self.contents = value
        self._header = None
        if self._indefinite:
            self._indefinite = False
            self.method = 0
        if self._trailer != b'':
            self._trailer = b''

    def parse(self, spec=None, spec_params=None):
        """
        Parses the contents generically, or using a spec with optional params

        :param spec:
            A class derived from Asn1Value that defines what class_ and tag the
            value should have, and the semantics of the encoded value. The
            return value will be of this type. If omitted, the encoded value
            will be decoded using the standard universal tag based on the
            encoded tag number.

        :param spec_params:
            A dict of params to pass to the spec object

        :return:
            An object of the type spec, or if not present, a child of Asn1Value
        """

        if self._parsed is None or self._parsed[1:3] != (spec, spec_params):
            parsed_value, _ = _parse_build(self.__bytes__(), spec=spec, spec_params=spec_params)
            self._parsed = (parsed_value, spec, spec_params)
        return self._parsed[0]

    def __bytes__(self):
        """
        :return:
            A byte string
        """

        if self.contents is None:
            return b''
        if self._bytes is None:
            self._bytes = self._merge_chunks()
        return self._bytes

    def _setable_native(self):
        """
        Returns a byte string that can be passed into .set()

        :return:
            A python value that is valid to pass to .set()
        """

        return self.__bytes__()

    def _copy(self, other, copy_func):
        """
        Copies the contents of another ParsableOctetString object to itself

        :param object:
            Another instance of the same class

        :param copy_func:
            An reference of copy.copy() or copy.deepcopy() to use when copying
            lists, dicts and objects
        """

        super(ParsableOctetString, self)._copy(other, copy_func)
        self._bytes = other._bytes
        self._parsed = copy_func(other._parsed)

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            A byte string or None
        """

        if self.contents is None:
            return None

        if self._parsed is not None:
            return self._parsed[0].native
        else:
            return self.__bytes__()

    @property
    def parsed(self):
        """
        Returns the parsed object from .parse()

        :return:
            The object returned by .parse()
        """

        if self._parsed is None:
            self.parse()

        return self._parsed[0]

    def dump(self, force=False):
        """
        Encodes the value using DER

        :param force:
            If the encoded contents already exist, clear them and regenerate
            to ensure they are in DER format instead of BER format

        :return:
            A byte string of the DER-encoded value
        """

        # If the length is indefinite, force the re-encoding
        if self._indefinite:
            force = True

        if force:
            if self._parsed is not None:
                native = self.parsed.dump(force=force)
            else:
                native = self.native
            self.contents = None
            self.set(native)

        return Asn1Value.dump(self) class Sequence(Asn1Value):
    """
    Represents a sequence of fields from ASN.1 as a Python object with a
    dict-like interface
    """

    tag = 16

    class_ = 0
    method = 1

    # A list of child objects, in order of _fields
    children = None

    # Sequence overrides .contents to be a property so that the mutated state
    # of child objects can be checked to ensure everything is up-to-date
    _contents = None

    # Variable to track if the object has been mutated
    _mutated = False

    # A list of tuples in one of the following forms.
    #
    # Option 1, a unicode string field name and a value class
    #
    # ("name", Asn1ValueClass)
    #
    # Option 2, same as Option 1, but with a dict of class params
    #
    # ("name", Asn1ValueClass, {'explicit': 5})
    _fields = []

    # A dict with keys being the name of a field and the value being a unicode
    # string of the method name on self to call to get the spec for that field
    _spec_callbacks = None

    # A dict that maps unicode string field names to an index in _fields
    _field_map = None

    # A list in the same order as _fields that has tuples in the form (class_, tag)
    _field_ids = None

    # An optional 2-element tuple that defines the field names of an OID field
    # and the field that the OID should be used to help decode. Works with the
    # _oid_specs attribute.
    _oid_pair = None

    # A dict with keys that are unicode string OID values and values that are
    # Asn1Value classes to use for decoding a variable-type field.
    _oid_specs = None

    # A 2-element tuple of the indexes in _fields of the OID and value fields
    _oid_nums = None

    # Predetermined field specs to optimize away calls to _determine_spec()
    _precomputed_specs = None

    def __init__(self, value=None, default=None, **kwargs):
        """
        Allows setting field values before passing everything else along to
        Asn1Value.__init__()

        :param value:
            A native Python datatype to initialize the object value with

        :param default:
            The default value if no value is specified
        """

        Asn1Value.__init__(self, **kwargs)

        check_existing = False
        if value is None and default is not None:
            check_existing = True
            if self.children is None:
                if self.contents is None:
                    check_existing = False
                else:
                    self._parse_children()
            value = default

        if value is not None:
            try:
                # Fields are iterated in definition order to allow things like
                # OID-based specs. Otherwise sometimes the value would be processed
                # before the OID field, resulting in invalid value object creation.
                if self._fields:
                    keys = [info[0] for info in self._fields]
                    unused_keys = set(value.keys())
                else:
                    keys = value.keys()
                    unused_keys = set(keys)

                for key in keys:
                    # If we are setting defaults, but a real value has already
                    # been set for the field, then skip it
                    if check_existing:
                        index = self._field_map[key]
                        if index < len(self.children) and self.children[index] is not VOID:
                            if key in unused_keys:
                                unused_keys.remove(key)
                            continue

                    if key in value:
                        self.__setitem__(key, value[key])
                        unused_keys.remove(key)

                if len(unused_keys):
                    raise ValueError(unwrap(
                        '''
                        One or more unknown fields was passed to the constructor
                        of %s: %s
                        ''',
                        type_name(self),
                        ', '.join(sorted(list(unused_keys)))
                    ))

            except (ValueError, TypeError) as e:
                args = e.args[1:]
                e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
                raise e

    @property
    def contents(self):
        """
        :return:
            A byte string of the DER-encoded contents of the sequence
        """

        if self.children is None:
            return self._contents

        if self._is_mutated():
            self._set_contents()

        return self._contents

    @contents.setter
    def contents(self, value):
        """
        :param value:
            A byte string of the DER-encoded contents of the sequence
        """

        self._contents = value

    def _is_mutated(self):
        """
        :return:
            A boolean - if the sequence or any children (recursively) have been
            mutated
        """

        mutated = self._mutated
        if self.children is not None:
            for child in self.children:
                if isinstance(child, Sequence) or isinstance(child, SequenceOf):
                    mutated = mutated or child._is_mutated()

        return mutated

    def _lazy_child(self, index):
        """
        Builds a child object if the child has only been parsed into a tuple so far
        """

        child = self.children[index]
        if child.__class__ == tuple:
            child = self.children[index] = _build(*child)
        return child

    def __len__(self):
        """
        :return:
            Integer
        """
        # We inline this check to prevent method invocation each time
        if self.children is None:
            self._parse_children()

        return len(self.children)

    def __getitem__(self, key):
        """
        Allows accessing fields by name or index

        :param key:
            A unicode string of the field name, or an integer of the field index

        :raises:
            KeyError - when a field name or index is invalid

        :return:
            The Asn1Value object of the field specified
        """

        # We inline this check to prevent method invocation each time
        if self.children is None:
            self._parse_children()

        if not isinstance(key, int_types):
            if key not in self._field_map:
                raise KeyError(unwrap(
                    '''
                    No field named "%s" defined for %s
                    ''',
                    key,
                    type_name(self)
                ))
            key = self._field_map[key]

        if key >= len(self.children):
            raise KeyError(unwrap(
                '''
                No field numbered %s is present in this %s
                ''',
                key,
                type_name(self)
            ))

        try:
            return self._lazy_child(key)

        except (ValueError, TypeError) as e:
            args = e.args[1:]
            e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
            raise e

    def __setitem__(self, key, value):
        """
        Allows settings fields by name or index

        :param key:
            A unicode string of the field name, or an integer of the field index

        :param value:
            A native Python datatype to set the field value to. This method will
            construct the appropriate Asn1Value object from _fields.

        :raises:
            ValueError - when a field name or index is invalid
        """

        # We inline this check to prevent method invocation each time
        if self.children is None:
            self._parse_children()

        if not isinstance(key, int_types):
            if key not in self._field_map:
                raise KeyError(unwrap(
                    '''
                    No field named "%s" defined for %s
                    ''',
                    key,
                    type_name(self)
                ))
            key = self._field_map[key]

        field_name, field_spec, value_spec, field_params, _ = self._determine_spec(key)

        new_value = self._make_value(field_name, field_spec, value_spec, field_params, value)

        invalid_value = False
        if isinstance(new_value, Any):
            invalid_value = new_value.parsed is None
        else:
            invalid_value = new_value.contents is None

        if invalid_value:
            raise ValueError(unwrap(
                '''
                Value for field "%s" of %s is not set
                ''',
                field_name,
                type_name(self)
            ))

        self.children[key] = new_value

        if self._native is not None:
            self._native[self._fields[key][0]] = self.children[key].native
        self._mutated = True

    def __delitem__(self, key):
        """
        Allows deleting optional or default fields by name or index

        :param key:
            A unicode string of the field name, or an integer of the field index

        :raises:
            ValueError - when a field name or index is invalid, or the field is not optional or defaulted
        """

        # We inline this check to prevent method invocation each time
        if self.children is None:
            self._parse_children()

        if not isinstance(key, int_types):
            if key not in self._field_map:
                raise KeyError(unwrap(
                    '''
                    No field named "%s" defined for %s
                    ''',
                    key,
                    type_name(self)
                ))
            key = self._field_map[key]

        name, _, params = self._fields[key]
        if not params or ('default' not in params and 'optional' not in params):
            raise ValueError(unwrap(
                '''
                Can not delete the value for the field "%s" of %s since it is
                not optional or defaulted
                ''',
                name,
                type_name(self)
            ))

        if 'optional' in params:
            self.children[key] = VOID
            if self._native is not None:
                self._native[name] = None
        else:
            self.__setitem__(key, None)
        self._mutated = True

    def __iter__(self):
        """
        :return:
            An iterator of field key names
        """

        for info in self._fields:
            yield info[0]

    def _set_contents(self, force=False):
        """
        Updates the .contents attribute of the value with the encoded value of
        all of the child objects

        :param force:
            Ensure all contents are in DER format instead of possibly using
            cached BER-encoded data
        """

        if self.children is None:
            self._parse_children()

        contents = BytesIO()
        for index, info in enumerate(self._fields):
            child = self.children[index]
            if child is None:
                child_dump = b''
            elif child.__class__ == tuple:
                if force:
                    child_dump = self._lazy_child(index).dump(force=force)
                else:
                    child_dump = child[3] + child[4] + child[5]
            else:
                child_dump = child.dump(force=force)
            # Skip values that are the same as the default
            if info[2] and 'default' in info[2]:
                default_value = info[1](**info[2])
                if default_value.dump() == child_dump:
                    continue
            contents.write(child_dump)
        self._contents = contents.getvalue()

        self._header = None
        if self._trailer != b'':
            self._trailer = b''

    def _setup(self):
        """
        Generates _field_map, _field_ids and _oid_nums for use in parsing
        """

        cls = self.__class__
        cls._field_map = {}
        cls._field_ids = []
        cls._precomputed_specs = []
        for index, field in enumerate(cls._fields):
            if len(field) < 3:
                field = field + ({},)
                cls._fields[index] = field
            cls._field_map[field[0]] = index
            cls._field_ids.append(_build_id_tuple(field[2], field[1]))

        if cls._oid_pair is not None:
            cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]])

        for index, field in enumerate(cls._fields):
            has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks
            is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index
            if has_callback or is_mapped_oid:
                cls._precomputed_specs.append(None)
            else:
                cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None))

    def _determine_spec(self, index):
        """
        Determine how a value for a field should be constructed

        :param index:
            The field number

        :return:
            A tuple containing the following elements:
             - unicode string of the field name
             - Asn1Value class of the field spec
             - Asn1Value class of the value spec
             - None or dict of params to pass to the field spec
             - None or Asn1Value class indicating the value spec was derived from an OID or a spec callback
        """

        name, field_spec, field_params = self._fields[index]
        value_spec = field_spec
        spec_override = None

        if self._spec_callbacks is not None and name in self._spec_callbacks:
            callback = self._spec_callbacks[name]
            spec_override = callback(self)
            if spec_override:
                # Allow a spec callback to specify both the base spec and
                # the override, for situations such as OctetString and parse_as
                if spec_override.__class__ == tuple and len(spec_override) == 2:
                    field_spec, value_spec = spec_override
                    if value_spec is None:
                        value_spec = field_spec
                        spec_override = None
                # When no field spec is specified, use a single return value as that
                elif field_spec is None:
                    field_spec = spec_override
                    value_spec = field_spec
                    spec_override = None
                else:
                    value_spec = spec_override

        elif self._oid_nums is not None and self._oid_nums[1] == index:
            oid = self._lazy_child(self._oid_nums[0]).native
            if oid in self._oid_specs:
                spec_override = self._oid_specs[oid]
                value_spec = spec_override

        return (name, field_spec, value_spec, field_params, spec_override)

    def _make_value(self, field_name, field_spec, value_spec, field_params, value):
        """
        Contructs an appropriate Asn1Value object for a field

        :param field_name:
            A unicode string of the field name

        :param field_spec:
            An Asn1Value class that is the field spec

        :param value_spec:
            An Asn1Value class that is the vaue spec

        :param field_params:
            None or a dict of params for the field spec

        :param value:
            The value to construct an Asn1Value object from

        :return:
            An instance of a child class of Asn1Value
        """

        if value is None and 'optional' in field_params:
            return VOID

        specs_different = field_spec != value_spec
        is_any = issubclass(field_spec, Any)

        if issubclass(value_spec, Choice):
            is_asn1value = isinstance(value, Asn1Value)
            is_tuple = isinstance(value, tuple) and len(value) == 2
            is_dict = isinstance(value, dict) and len(value) == 1
            if not is_asn1value and not is_tuple and not is_dict:
                raise ValueError(unwrap(
                    '''
                    Can not set a native python value to %s, which has the
                    choice type of %s - value must be an instance of Asn1Value
                    ''',
                    field_name,
                    type_name(value_spec)
                ))
            if is_tuple or is_dict:
                value = value_spec(value)
            if not isinstance(value, value_spec):
                wrapper = value_spec()
                wrapper.validate(value.class_, value.tag, value.contents)
                wrapper._parsed = value
                new_value = wrapper
            else:
                new_value = value

        elif isinstance(value, field_spec):
            new_value = value
            if specs_different:
                new_value.parse(value_spec)

        elif (not specs_different or is_any) and not isinstance(value, value_spec):
            if (not is_any or specs_different) and isinstance(value, Asn1Value):
                raise TypeError(unwrap(
                    '''
                    %s value must be %s, not %s
                    ''',
                    field_name,
                    type_name(value_spec),
                    type_name(value)
                ))
            new_value = value_spec(value, **field_params)

        else:
            if isinstance(value, value_spec):
                new_value = value
            else:
                if isinstance(value, Asn1Value):
                    raise TypeError(unwrap(
                        '''
                        %s value must be %s, not %s
                        ''',
                        field_name,
                        type_name(value_spec),
                        type_name(value)
                    ))
                new_value = value_spec(value)

            # For when the field is OctetString or OctetBitString with embedded
            # values we need to wrap the value in the field spec to get the
            # appropriate encoded value.
            if specs_different and not is_any:
                wrapper = field_spec(value=new_value.dump(), **field_params)
                wrapper._parsed = (new_value, new_value.__class__, None)
                new_value = wrapper

        new_value = _fix_tagging(new_value, field_params)

        return new_value

    def _parse_children(self, recurse=False):
        """
        Parses the contents and generates Asn1Value objects based on the
        definitions from _fields.

        :param recurse:
            If child objects that are Sequence or SequenceOf objects should
            be recursively parsed

        :raises:
            ValueError - when an error occurs parsing child objects
        """

        cls = self.__class__
        if self._contents is None:
            if self._fields:
                self.children = [VOID] * len(self._fields)
                for index, (_, _, params) in enumerate(self._fields):
                    if 'default' in params:
                        if cls._precomputed_specs[index]:
                            field_name, field_spec, value_spec, field_params, _ = cls._precomputed_specs[index]
                        else:
                            field_name, field_spec, value_spec, field_params, _ = self._determine_spec(index)
                        self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None)
            return

        try:
            self.children = []
            contents_length = len(self._contents)
            child_pointer = 0
            field = 0
            field_len = len(self._fields)
            parts = None
            again = child_pointer < contents_length
            while again:
                if parts is None:
                    parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer)
                again = child_pointer < contents_length

                if field < field_len:
                    _, field_spec, value_spec, field_params, spec_override = (
                        cls._precomputed_specs[field] or self._determine_spec(field))

                    # If the next value is optional or default, allow it to be absent
                    if field_params and ('optional' in field_params or 'default' in field_params):
                        if self._field_ids[field] != (parts[0], parts[2]) and field_spec != Any:

                            # See if the value is a valid choice before assuming
                            # that we have a missing optional or default value
                            choice_match = False
                            if issubclass(field_spec, Choice):
                                try:
                                    tester = field_spec(**field_params)
                                    tester.validate(parts[0], parts[2], parts[4])
                                    choice_match = True
                                except (ValueError):
                                    pass

                            if not choice_match:
                                if 'optional' in field_params:
                                    self.children.append(VOID)
                                else:
                                    self.children.append(field_spec(**field_params))
                                field += 1
                                again = True
                                continue

                    if field_spec is None or (spec_override and issubclass(field_spec, Any)):
                        field_spec = value_spec
                        spec_override = None

                    if spec_override:
                        child = parts + (field_spec, field_params, value_spec)
                    else:
                        child = parts + (field_spec, field_params)

                # Handle situations where an optional or defaulted field definition is incorrect
                elif field_len > 0 and field + 1 <= field_len:
                    missed_fields = []
                    prev_field = field - 1
                    while prev_field >= 0:
                        prev_field_info = self._fields[prev_field]
                        if len(prev_field_info) < 3:
                            break
                        if 'optional' in prev_field_info[2] or 'default' in prev_field_info[2]:
                            missed_fields.append(prev_field_info[0])
                        prev_field -= 1
                    plural = 's' if len(missed_fields) > 1 else ''
                    missed_field_names = ', '.join(missed_fields)
                    raise ValueError(unwrap(
                        '''
                        Data for field %s (%s class, %s method, tag %s) does
                        not match the field definition%s of %s
                        ''',
                        field + 1,
                        CLASS_NUM_TO_NAME_MAP.get(parts[0]),
                        METHOD_NUM_TO_NAME_MAP.get(parts[1]),
                        parts[2],
                        plural,
                        missed_field_names
                    ))

                else:
                    child = parts

                if recurse:
                    child = _build(*child)
                    if isinstance(child, (Sequence, SequenceOf)):
                        child._parse_children(recurse=True)

                self.children.append(child)
                field += 1
                parts = None

            index = len(self.children)
            while index < field_len:
                name, field_spec, field_params = self._fields[index]
                if 'default' in field_params:
                    self.children.append(field_spec(**field_params))
                elif 'optional' in field_params:
                    self.children.append(VOID)
                else:
                    raise ValueError(unwrap(
                        '''
                        Field "%s" is missing from structure
                        ''',
                        name
                    ))
                index += 1

        except (ValueError, TypeError) as e:
            self.children = None
            args = e.args[1:]
            e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
            raise e

    def spec(self, field_name):
        """
        Determines the spec to use for the field specified. Depending on how
        the spec is determined (_oid_pair or _spec_callbacks), it may be
        necessary to set preceding field values before calling this. Usually
        specs, if dynamic, are controlled by a preceding ObjectIdentifier
        field.

        :param field_name:
            A unicode string of the field name to get the spec for

        :return:
            A child class of asn1crypto.core.Asn1Value that the field must be
            encoded using
        """

        if not isinstance(field_name, str_cls):
            raise TypeError(unwrap(
                '''
                field_name must be a unicode string, not %s
                ''',
                type_name(field_name)
            ))

        if self._fields is None:
            raise ValueError(unwrap(
                '''
                Unable to retrieve spec for field %s in the class %s because
                _fields has not been set
                ''',
                repr(field_name),
                type_name(self)
            ))

        index = self._field_map[field_name]
        info = self._determine_spec(index)

        return info[2]

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            An OrderedDict or None. If an OrderedDict, all child values are
            recursively converted to native representation also.
        """

        if self.contents is None:
            return None

        if self._native is None:
            if self.children is None:
                self._parse_children(recurse=True)
            try:
                self._native = OrderedDict()
                for index, child in enumerate(self.children):
                    if child.__class__ == tuple:
                        child = _build(*child)
                        self.children[index] = child
                    try:
                        name = self._fields[index][0]
                    except (IndexError):
                        name = str_cls(index)
                    self._native[name] = child.native
            except (ValueError, TypeError) as e:
                self._native = None
                args = e.args[1:]
                e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
                raise e
        return self._native

    def _copy(self, other, copy_func):
        """
        Copies the contents of another Sequence object to itself

        :param object:
            Another instance of the same class

        :param copy_func:
            An reference of copy.copy() or copy.deepcopy() to use when copying
            lists, dicts and objects
        """

        super(Sequence, self)._copy(other, copy_func)
        if self.children is not None:
            self.children = []
            for child in other.children:
                if child.__class__ == tuple:
                    self.children.append(child)
                else:
                    self.children.append(child.copy())

    def debug(self, nest_level=1):
        """
        Show the binary data and parsed data in a tree structure
        """

        if self.children is None:
            self._parse_children()

        prefix = '  ' * nest_level
        _basic_debug(prefix, self)
        for field_name in self:
            child = self._lazy_child(self._field_map[field_name])
            if child is not VOID:
                print('%s    Field "%s"' % (prefix, field_name))
                child.debug(nest_level + 3)

    def dump(self, force=False):
        """
        Encodes the value using DER

        :param force:
            If the encoded contents already exist, clear them and regenerate
            to ensure they are in DER format instead of BER format

        :return:
            A byte string of the DER-encoded value
        """

        # If the length is indefinite, force the re-encoding
        if self._header is not None and self._header[-1:] == b'\x80':
            force = True

        # We can't force encoding if we don't have a spec
        if force and self._fields == [] and self.__class__ is Sequence:
            force = False

        if force:
            self._set_contents(force=force)

        if self._fields and self.children is not None:
            for index, (field_name, _, params) in enumerate(self._fields):
                if self.children[index] is not VOID:
                    continue
                if 'default' in params or 'optional' in params:
                    continue
                raise ValueError(unwrap(
                    '''
                    Field "%s" is missing from structure
                    ''',
                    field_name
                ))

        return Asn1Value.dump(self) class SequenceOf(Asn1Value):
    """
    Represents a sequence (ordered) of a single type of values from ASN.1 as a
    Python object with a list-like interface
    """

    tag = 16

    class_ = 0
    method = 1

    # A list of child objects
    children = None

    # SequenceOf overrides .contents to be a property so that the mutated state
    # of child objects can be checked to ensure everything is up-to-date
    _contents = None

    # Variable to track if the object has been mutated
    _mutated = False

    # An Asn1Value class to use when parsing children
    _child_spec = None

    def __init__(self, value=None, default=None, contents=None, spec=None, **kwargs):
        """
        Allows setting child objects and the _child_spec via the spec parameter
        before passing everything else along to Asn1Value.__init__()

        :param value:
            A native Python datatype to initialize the object value with

        :param default:
            The default value if no value is specified

        :param contents:
            A byte string of the encoded contents of the value

        :param spec:
            A class derived from Asn1Value to use to parse children
        """

        if spec:
            self._child_spec = spec

        Asn1Value.__init__(self, **kwargs)

        try:
            if contents is not None:
                self.contents = contents
            else:
                if value is None and default is not None:
                    value = default

                if value is not None:
                    for index, child in enumerate(value):
                        self.__setitem__(index, child)

                    # Make sure a blank list is serialized
                    if self.contents is None:
                        self._set_contents()

        except (ValueError, TypeError) as e:
            args = e.args[1:]
            e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
            raise e

    @property
    def contents(self):
        """
        :return:
            A byte string of the DER-encoded contents of the sequence
        """

        if self.children is None:
            return self._contents

        if self._is_mutated():
            self._set_contents()

        return self._contents

    @contents.setter
    def contents(self, value):
        """
        :param value:
            A byte string of the DER-encoded contents of the sequence
        """

        self._contents = value

    def _is_mutated(self):
        """
        :return:
            A boolean - if the sequence or any children (recursively) have been
            mutated
        """

        mutated = self._mutated
        if self.children is not None:
            for child in self.children:
                if isinstance(child, Sequence) or isinstance(child, SequenceOf):
                    mutated = mutated or child._is_mutated()

        return mutated

    def _lazy_child(self, index):
        """
        Builds a child object if the child has only been parsed into a tuple so far
        """

        child = self.children[index]
        if child.__class__ == tuple:
            child = _build(*child)
            self.children[index] = child
        return child

    def _make_value(self, value):
        """
        Constructs a _child_spec value from a native Python data type, or
        an appropriate Asn1Value object

        :param value:
            A native Python value, or some child of Asn1Value

        :return:
            An object of type _child_spec
        """

        if isinstance(value, self._child_spec):
            new_value = value

        elif issubclass(self._child_spec, Any):
            if isinstance(value, Asn1Value):
                new_value = value
            else:
                raise ValueError(unwrap(
                    '''
                    Can not set a native python value to %s where the
                    _child_spec is Any - value must be an instance of Asn1Value
                    ''',
                    type_name(self)
                ))

        elif issubclass(self._child_spec, Choice):
            if not isinstance(value, Asn1Value):
                raise ValueError(unwrap(
                    '''
                    Can not set a native python value to %s where the
                    _child_spec is the choice type %s - value must be an
                    instance of Asn1Value
                    ''',
                    type_name(self),
                    self._child_spec.__name__
                ))
            if not isinstance(value, self._child_spec):
                wrapper = self._child_spec()
                wrapper.validate(value.class_, value.tag, value.contents)
                wrapper._parsed = value
                value = wrapper
            new_value = value

        else:
            return self._child_spec(value=value)

        params = {}
        if self._child_spec.explicit:
            params['explicit'] = self._child_spec.explicit
        if self._child_spec.implicit:
            params['implicit'] = (self._child_spec.class_, self._child_spec.tag)
        return _fix_tagging(new_value, params)

    def __len__(self):
        """
        :return:
            An integer
        """
        # We inline this checks to prevent method invocation each time
        if self.children is None:
            self._parse_children()

        return len(self.children)

    def __getitem__(self, key):
        """
        Allows accessing children via index

        :param key:
            Integer index of child
        """

        # We inline this checks to prevent method invocation each time
        if self.children is None:
            self._parse_children()

        return self._lazy_child(key)

    def __setitem__(self, key, value):
        """
        Allows overriding a child via index

        :param key:
            Integer index of child

        :param value:
            Native python datatype that will be passed to _child_spec to create
            new child object
        """

        # We inline this checks to prevent method invocation each time
        if self.children is None:
            self._parse_children()

        new_value = self._make_value(value)

        # If adding at the end, create a space for the new value
        if key == len(self.children):
            self.children.append(None)
            if self._native is not None:
                self._native.append(None)

        self.children[key] = new_value

        if self._native is not None:
            self._native[key] = self.children[key].native

        self._mutated = True

    def __delitem__(self, key):
        """
        Allows removing a child via index

        :param key:
            Integer index of child
        """

        # We inline this checks to prevent method invocation each time
        if self.children is None:
            self._parse_children()

        self.children.pop(key)
        if self._native is not None:
            self._native.pop(key)

        self._mutated = True

    def __iter__(self):
        """
        :return:
            An iter() of child objects
        """

        # We inline this checks to prevent method invocation each time
        if self.children is None:
            self._parse_children()

        for index in range(0, len(self.children)):
            yield self._lazy_child(index)

    def __contains__(self, item):
        """
        :param item:
            An object of the type cls._child_spec

        :return:
            A boolean if the item is contained in this SequenceOf
        """

        if item is None or item is VOID:
            return False

        if not isinstance(item, self._child_spec):
            raise TypeError(unwrap(
                '''
                Checking membership in %s is only available for instances of
                %s, not %s
                ''',
                type_name(self),
                type_name(self._child_spec),
                type_name(item)
            ))

        for child in self:
            if child == item:
                return True

        return False

    def append(self, value):
        """
        Allows adding a child to the end of the sequence

        :param value:
            Native python datatype that will be passed to _child_spec to create
            new child object
        """

        # We inline this checks to prevent method invocation each time
        if self.children is None:
            self._parse_children()

        self.children.append(self._make_value(value))

        if self._native is not None:
            self._native.append(self.children[-1].native)

        self._mutated = True

    def _set_contents(self, force=False):
        """
        Encodes all child objects into the contents for this object

        :param force:
            Ensure all contents are in DER format instead of possibly using
            cached BER-encoded data
        """

        if self.children is None:
            self._parse_children()

        contents = BytesIO()
        for child in self:
            contents.write(child.dump(force=force))
        self._contents = contents.getvalue()
        self._header = None
        if self._trailer != b'':
            self._trailer = b''

    def _parse_children(self, recurse=False):
        """
        Parses the contents and generates Asn1Value objects based on the
        definitions from _child_spec.

        :param recurse:
            If child objects that are Sequence or SequenceOf objects should
            be recursively parsed

        :raises:
            ValueError - when an error occurs parsing child objects
        """

        try:
            self.children = []
            if self._contents is None:
                return
            contents_length = len(self._contents)
            child_pointer = 0
            while child_pointer < contents_length:
                parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer)
                if self._child_spec:
                    child = parts + (self._child_spec,)
                else:
                    child = parts
                if recurse:
                    child = _build(*child)
                    if isinstance(child, (Sequence, SequenceOf)):
                        child._parse_children(recurse=True)
                self.children.append(child)
        except (ValueError, TypeError) as e:
            self.children = None
            args = e.args[1:]
            e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
            raise e

    def spec(self):
        """
        Determines the spec to use for child values.

        :return:
            A child class of asn1crypto.core.Asn1Value that child values must be
            encoded using
        """

        return self._child_spec

    @property
    def native(self):
        """
        The native Python datatype representation of this value

        :return:
            A list or None. If a list, all child values are recursively
            converted to native representation also.
        """

        if self.contents is None:
            return None

        if self._native is None:
            if self.children is None:
                self._parse_children(recurse=True)
            try:
                self._native = [child.native for child in self]
            except (ValueError, TypeError) as e:
                args = e.args[1:]
                e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
                raise e
        return self._native

    def _copy(self, other, copy_func):
        """
        Copies the contents of another SequenceOf object to itself

        :param object:
            Another instance of the same class

        :param copy_func:
            An reference of copy.copy() or copy.deepcopy() to use when copying
            lists, dicts and objects
        """

        super(SequenceOf, self)._copy(other, copy_func)
        if self.children is not None:
            self.children = []
            for child in other.children:
                if child.__class__ == tuple:
                    self.children.append(child)
                else:
                    self.children.append(child.copy())

    def debug(self, nest_level=1):
        """
        Show the binary data and parsed data in a tree structure
        """

        if self.children is None:
            self._parse_children()

        prefix = '  ' * nest_level
        _basic_debug(prefix, self)
        for child in self:
            child.debug(nest_level + 1)

    def dump(self, force=False):
        """
        Encodes the value using DER

        :param force:
            If the encoded contents already exist, clear them and regenerate
            to ensure they are in DER format instead of BER format

        :return:
            A byte string of the DER-encoded value
        """

        # If the length is indefinite, force the re-encoding
        if self._header is not None and self._header[-1:] == b'\x80':
            force = True

        if force:
            self._set_contents(force=force)

        return Asn1Value.dump(self) class SetOf(SequenceOf):
    """
    Represents a set (unordered) of a single type of values from ASN.1 as a
    Python object with a list-like interface
    """

    tag = 17

    def _set_contents(self, force=False):
        """
        Encodes all child objects into the contents for this object.

        This method is overridden because a SetOf needs to be encoded by
        sorting the child encodings.

        :param force:
            Ensure all contents are in DER format instead of possibly using
            cached BER-encoded data
        """

        if self.children is None:
            self._parse_children()

        child_encodings = []
        for child in self:
            child_encodings.append(child.dump(force=force))

        self._contents = b''.join(sorted(child_encodings))
        self._header = None
        if self._trailer != b'':
            self._trailer = b'' class UTCTime(AbstractTime):
    """
    Represents a UTC time from ASN.1 as a timezone aware Python datetime.datetime object
    """

    tag = 23

    # Regular expression for UTCTime as described in X.680 sect. 43 and ISO 8601
    _TIMESTRING_RE = re.compile(r'''
        ^
        # YYMMDD
        (?P<year>\d{2})
        (?P<month>\d{2})
        (?P<day>\d{2})

        # hhmm or hhmmss
        (?P<hour>\d{2})
        (?P<minute>\d{2})
        (?P<second>\d{2})?

        # Matches nothing, needed because GeneralizedTime uses this.
        (?P<fraction>)

        # Z or [-+]hhmm
        (?:
            (?P<zulu>Z)
            |
            (?:
                (?P<dsign>[-+])
                (?P<dhour>\d{2})
                (?P<dminute>\d{2})
            )
        )
        $
    ''', re.X)

    def set(self, value):
        """
        Sets the value of the object

        :param value:
            A unicode string or a datetime.datetime object

        :raises:
            ValueError - when an invalid value is passed
        """

        if isinstance(value, datetime):
            if not value.tzinfo:
                raise ValueError('Must be timezone aware')

            # Convert value to UTC.
            value = value.astimezone(utc_with_dst)

            if not 1950 <= value.year <= 2049:
                raise ValueError('Year of the UTCTime is not in range [1950, 2049], use GeneralizedTime instead')

            value = value.strftime('%y%m%d%H%M%SZ')
            if _PY2:
                value = value.decode('ascii')

        AbstractString.set(self, value)
        # Set it to None and let the class take care of converting the next
        # time that .native is called
        self._native = None

    def _get_datetime(self, parsed):
        """
        Create a datetime object from the parsed time.

        :return:
            An aware datetime.datetime object
        """

        # X.680 only specifies that UTCTime is not using a century.
        # So "18" could as well mean 2118 or 1318.
        # X.509 and CMS specify to use UTCTime for years earlier than 2050.
        # Assume that UTCTime is only used for years [1950, 2049].
        if parsed['year'] < 50:
            parsed['year'] += 2000
        else:
            parsed['year'] += 1900

        return datetime(**parsed) class UTF8String(AbstractString):
    """
    Represents a UTF-8 string from ASN.1 as a Python unicode string
    """

    tag = 12
    _encoding = 'utf-8' class PublicKeyInfo(Sequence):
    """
    Original Name: SubjectPublicKeyInfo
    Source: https://tools.ietf.org/html/rfc5280#page-17
    """

    _fields = [
        ('algorithm', PublicKeyAlgorithm),
        ('public_key', ParsableOctetBitString),
    ]

    def _public_key_spec(self):
        algorithm = self['algorithm']['algorithm'].native
        return {
            'rsa': RSAPublicKey,
            'rsaes_oaep': RSAPublicKey,
            'rsassa_pss': RSAPublicKey,
            'dsa': Integer,
            # We override the field spec with ECPoint so that users can easily
            # decompose the byte string into the constituent X and Y coords
            'ec': (ECPointBitString, None),
            'dh': Integer,
            # These should be treated as opaque bit strings according
            # to RFC 8410, and need not even be valid ASN.1
            'x25519': (OctetBitString, None),
            'x448': (OctetBitString, None),
            'ed25519': (OctetBitString, None),
            'ed448': (OctetBitString, None),
        }[algorithm]

    _spec_callbacks = {
        'public_key': _public_key_spec
    }

    _algorithm = None
    _bit_size = None
    _fingerprint = None
    _sha1 = None
    _sha256 = None

    @classmethod
    def wrap(cls, public_key, algorithm):
        """
        Wraps a public key in a PublicKeyInfo structure

        :param public_key:
            A byte string or Asn1Value object of the public key

        :param algorithm:
            A unicode string of "rsa"

        :return:
            A PublicKeyInfo object
        """

        if not isinstance(public_key, byte_cls) and not isinstance(public_key, Asn1Value):
            raise TypeError(unwrap(
                '''
                public_key must be a byte string or Asn1Value, not %s
                ''',
                type_name(public_key)
            ))

        if algorithm != 'rsa':
            raise ValueError(unwrap(
                '''
                algorithm must "rsa", not %s
                ''',
                repr(algorithm)
            ))

        algo = PublicKeyAlgorithm()
        algo['algorithm'] = PublicKeyAlgorithmId(algorithm)
        algo['parameters'] = Null()

        container = cls()
        container['algorithm'] = algo
        if isinstance(public_key, Asn1Value):
            public_key = public_key.untag().dump()
        container['public_key'] = ParsableOctetBitString(public_key)

        return container

    def unwrap(self):
        """
        Unwraps an RSA public key into an RSAPublicKey object. Does not support
        DSA or EC public keys since they do not have an unwrapped form.

        :return:
            An RSAPublicKey object
        """

        raise APIException(
            'asn1crypto.keys.PublicKeyInfo().unwrap() has been removed, '
            'please use oscrypto.asymmetric.PublicKey().unwrap() instead')

    @property
    def curve(self):
        """
        Returns information about the curve used for an EC key

        :raises:
            ValueError - when the key is not an EC key

        :return:
            A two-element tuple, with the first element being a unicode string
            of "implicit_ca", "specified" or "named". If the first element is
            "implicit_ca", the second is None. If "specified", the second is
            an OrderedDict that is the native version of SpecifiedECDomain. If
            "named", the second is a unicode string of the curve name.
        """

        if self.algorithm != 'ec':
            raise ValueError(unwrap(
                '''
                Only EC keys have a curve, this key is %s
                ''',
                self.algorithm.upper()
            ))

        params = self['algorithm']['parameters']
        chosen = params.chosen

        if params.name == 'implicit_ca':
            value = None
        else:
            value = chosen.native

        return (params.name, value)

    @property
    def hash_algo(self):
        """
        Returns the name of the family of hash algorithms used to generate a
        DSA key

        :raises:
            ValueError - when the key is not a DSA key

        :return:
            A unicode string of "sha1" or "sha2" or None if no parameters are
            present
        """

        if self.algorithm != 'dsa':
            raise ValueError(unwrap(
                '''
                Only DSA keys are generated using a hash algorithm, this key is
                %s
                ''',
                self.algorithm.upper()
            ))

        parameters = self['algorithm']['parameters']
        if parameters.native is None:
            return None

        byte_len = math.log(parameters['q'].native, 2) / 8

        return 'sha1' if byte_len <= 20 else 'sha2'

    @property
    def algorithm(self):
        """
        :return:
            A unicode string of "rsa", "dsa" or "ec"
        """

        if self._algorithm is None:
            self._algorithm = self['algorithm']['algorithm'].native
        return self._algorithm

    @property
    def bit_size(self):
        """
        :return:
            The bit size of the public key, as an integer
        """

        if self._bit_size is None:
            if self.algorithm == 'ec':
                self._bit_size = int(((len(self['public_key'].native) - 1) / 2) * 8)
            else:
                if self.algorithm == 'rsa':
                    prime = self['public_key'].parsed['modulus'].native
                elif self.algorithm == 'dsa':
                    prime = self['algorithm']['parameters']['p'].native
                self._bit_size = int(math.ceil(math.log(prime, 2)))
                modulus = self._bit_size % 8
                if modulus != 0:
                    self._bit_size += 8 - modulus

        return self._bit_size

    @property
    def byte_size(self):
        """
        :return:
            The byte size of the public key, as an integer
        """

        return int(math.ceil(self.bit_size / 8))

    @property
    def sha1(self):
        """
        :return:
            The SHA1 hash of the DER-encoded bytes of this public key info
        """

        if self._sha1 is None:
            self._sha1 = hashlib.sha1(byte_cls(self['public_key'])).digest()
        return self._sha1

    @property
    def sha256(self):
        """
        :return:
            The SHA-256 hash of the DER-encoded bytes of this public key info
        """

        if self._sha256 is None:
            self._sha256 = hashlib.sha256(byte_cls(self['public_key'])).digest()
        return self._sha256

    @property
    def fingerprint(self):
        """
        Creates a fingerprint that can be compared with a private key to see if
        the two form a pair.

        This fingerprint is not compatible with fingerprints generated by any
        other software.

        :return:
            A byte string that is a sha256 hash of selected components (based
            on the key type)
        """

        raise APIException(
            'asn1crypto.keys.PublicKeyInfo().fingerprint has been removed, '
            'please use oscrypto.asymmetric.PublicKey().fingerprint instead') class OCSPResponse(Sequence):
    _fields = [
        ('response_status', OCSPResponseStatus),
        ('response_bytes', ResponseBytes, {'explicit': 0, 'optional': True}),
    ]

    _processed_extensions = False
    _critical_extensions = None
    _nonce_value = None
    _extended_revoke_value = 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['response_bytes']['response'].parsed['tbs_response_data']['response_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 nonce_value(self):
        """
        This extension is used to prevent replay attacks on the request/response
        exchange

        :return:
            None or an OctetString object
        """

        if self._processed_extensions is False:
            self._set_extensions()
        return self._nonce_value

    @property
    def extended_revoke_value(self):
        """
        This extension is used to signal that the responder will return a
        "revoked" status for non-issued certificates.

        :return:
            None or a Null object (if present)
        """

        if self._processed_extensions is False:
            self._set_extensions()
        return self._extended_revoke_value

    @property
    def basic_ocsp_response(self):
        """
        A shortcut into the BasicOCSPResponse sequence

        :return:
            None or an asn1crypto.ocsp.BasicOCSPResponse object
        """

        return self['response_bytes']['response'].parsed

    @property
    def response_data(self):
        """
        A shortcut into the parsed, ResponseData sequence

        :return:
            None or an asn1crypto.ocsp.ResponseData object
        """

        return self['response_bytes']['response'].parsed['tbs_response_data'] class Attributes(SequenceOf):
    _child_spec = Attribute class Certificate(Sequence):
    _fields = [
        ('tbs_certificate', TbsCertificate),
        ('signature_algorithm', SignedDigestAlgorithm),
        ('signature_value', OctetBitString),
    ]

    _processed_extensions = False
    _critical_extensions = None
    _subject_directory_attributes_value = None
    _key_identifier_value = None
    _key_usage_value = None
    _subject_alt_name_value = None
    _issuer_alt_name_value = None
    _basic_constraints_value = None
    _name_constraints_value = None
    _crl_distribution_points_value = None
    _certificate_policies_value = None
    _policy_mappings_value = None
    _authority_key_identifier_value = None
    _policy_constraints_value = None
    _freshest_crl_value = None
    _inhibit_any_policy_value = None
    _extended_key_usage_value = None
    _authority_information_access_value = None
    _subject_information_access_value = None
    _private_key_usage_period_value = None
    _tls_feature_value = None
    _ocsp_no_check_value = None
    _issuer_serial = None
    _authority_issuer_serial = False
    _crl_distribution_points = None
    _delta_crl_distribution_points = None
    _valid_domains = None
    _valid_ips = None
    _self_issued = None
    _self_signed = 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_certificate']['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 private_key_usage_period_value(self):
        """
        This extension is used to constrain the period over which the subject
        private key may be used

        :return:
            None or a PrivateKeyUsagePeriod object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._private_key_usage_period_value

    @property
    def subject_directory_attributes_value(self):
        """
        This extension is used to contain additional identification attributes
        about the subject.

        :return:
            None or a SubjectDirectoryAttributes object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._subject_directory_attributes_value

    @property
    def key_identifier_value(self):
        """
        This extension is used to help in creating certificate validation paths.
        It contains an identifier that should generally, but is not guaranteed
        to, be unique.

        :return:
            None or an OctetString object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._key_identifier_value

    @property
    def key_usage_value(self):
        """
        This extension is used to define the purpose of the public key
        contained within the certificate.

        :return:
            None or a KeyUsage
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._key_usage_value

    @property
    def subject_alt_name_value(self):
        """
        This extension allows for additional names to be associate with the
        subject of the certificate. While it may contain a whole host of
        possible names, it is usually used to allow certificates to be used
        with multiple different domain names.

        :return:
            None or a GeneralNames object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._subject_alt_name_value

    @property
    def issuer_alt_name_value(self):
        """
        This extension allows associating one or more alternative names with
        the issuer of the certificate.

        :return:
            None or an x509.GeneralNames object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._issuer_alt_name_value

    @property
    def basic_constraints_value(self):
        """
        This extension is used to determine if the subject of the certificate
        is a CA, and if so, what the maximum number of intermediate CA certs
        after this are, before an end-entity certificate is found.

        :return:
            None or a BasicConstraints object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._basic_constraints_value

    @property
    def name_constraints_value(self):
        """
        This extension is used in CA certificates, and is used to limit the
        possible names of certificates issued.

        :return:
            None or a NameConstraints object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._name_constraints_value

    @property
    def crl_distribution_points_value(self):
        """
        This extension is used to help in locating the CRL for this certificate.

        :return:
            None or a CRLDistributionPoints object
            extension
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._crl_distribution_points_value

    @property
    def certificate_policies_value(self):
        """
        This extension defines policies in CA certificates under which
        certificates may be issued. In end-entity certificates, the inclusion
        of a policy indicates the issuance of the certificate follows the
        policy.

        :return:
            None or a CertificatePolicies object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._certificate_policies_value

    @property
    def policy_mappings_value(self):
        """
        This extension allows mapping policy OIDs to other OIDs. This is used
        to allow different policies to be treated as equivalent in the process
        of validation.

        :return:
            None or a PolicyMappings object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._policy_mappings_value

    @property
    def authority_key_identifier_value(self):
        """
        This extension helps in identifying the public key with which to
        validate the authenticity of the certificate.

        :return:
            None or an AuthorityKeyIdentifier object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._authority_key_identifier_value

    @property
    def policy_constraints_value(self):
        """
        This extension is used to control if policy mapping is allowed and
        when policies are required.

        :return:
            None or a PolicyConstraints object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._policy_constraints_value

    @property
    def freshest_crl_value(self):
        """
        This extension is used to help locate any available delta CRLs

        :return:
            None or an CRLDistributionPoints object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._freshest_crl_value

    @property
    def inhibit_any_policy_value(self):
        """
        This extension is used to prevent mapping of the any policy to
        specific requirements

        :return:
            None or a Integer object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._inhibit_any_policy_value

    @property
    def extended_key_usage_value(self):
        """
        This extension is used to define additional purposes for the public key
        beyond what is contained in the basic constraints.

        :return:
            None or an ExtKeyUsageSyntax object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._extended_key_usage_value

    @property
    def authority_information_access_value(self):
        """
        This extension is used to locate the CA certificate used to sign this
        certificate, or the OCSP responder for this certificate.

        :return:
            None or an AuthorityInfoAccessSyntax object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._authority_information_access_value

    @property
    def subject_information_access_value(self):
        """
        This extension is used to access information about the subject of this
        certificate.

        :return:
            None or a SubjectInfoAccessSyntax object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._subject_information_access_value

    @property
    def tls_feature_value(self):
        """
        This extension is used to list the TLS features a server must respond
        with if a client initiates a request supporting them.

        :return:
            None or a Features object
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._tls_feature_value

    @property
    def ocsp_no_check_value(self):
        """
        This extension is used on certificates of OCSP responders, indicating
        that revocation information for the certificate should never need to
        be verified, thus preventing possible loops in path validation.

        :return:
            None or a Null object (if present)
        """

        if not self._processed_extensions:
            self._set_extensions()
        return self._ocsp_no_check_value

    @property
    def signature(self):
        """
        :return:
            A byte string of the signature
        """

        return self['signature_value'].native

    @property
    def signature_algo(self):
        """
        :return:
            A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa", "ecdsa"
        """

        return self['signature_algorithm'].signature_algo

    @property
    def hash_algo(self):
        """
        :return:
            A unicode string of "md2", "md5", "sha1", "sha224", "sha256",
            "sha384", "sha512", "sha512_224", "sha512_256"
        """

        return self['signature_algorithm'].hash_algo

    @property
    def public_key(self):
        """
        :return:
            The PublicKeyInfo object for this certificate
        """

        return self['tbs_certificate']['subject_public_key_info']

    @property
    def subject(self):
        """
        :return:
            The Name object for the subject of this certificate
        """

        return self['tbs_certificate']['subject']

    @property
    def issuer(self):
        """
        :return:
            The Name object for the issuer of this certificate
        """

        return self['tbs_certificate']['issuer']

    @property
    def serial_number(self):
        """
        :return:
            An integer of the certificate's serial number
        """

        return self['tbs_certificate']['serial_number'].native

    @property
    def key_identifier(self):
        """
        :return:
            None or a byte string of the certificate's key identifier from the
            key identifier extension
        """

        if not self.key_identifier_value:
            return None

        return self.key_identifier_value.native

    @property
    def issuer_serial(self):
        """
        :return:
            A byte string of the SHA-256 hash of the issuer concatenated with
            the ascii character ":", concatenated with the serial number as
            an ascii string
        """

        if self._issuer_serial is None:
            self._issuer_serial = self.issuer.sha256 + b':' + str_cls(self.serial_number).encode('ascii')
        return self._issuer_serial

    @property
    def not_valid_after(self):
        """
        :return:
            A datetime of latest time when the certificate is still valid
        """
        return self['tbs_certificate']['validity']['not_after'].native

    @property
    def not_valid_before(self):
        """
        :return:
            A datetime of the earliest time when the certificate is valid
        """
        return self['tbs_certificate']['validity']['not_before'].native

    @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 authority_issuer_serial(self):
        """
        :return:
            None or a byte string of the SHA-256 hash of the isser from the
            authority key identifier extension concatenated with the ascii
            character ":", concatenated with the serial number from the
            authority key identifier extension as an ascii string
        """

        if self._authority_issuer_serial is False:
            akiv = self.authority_key_identifier_value
            if akiv and akiv['authority_cert_issuer'].native:
                issuer = self.authority_key_identifier_value['authority_cert_issuer'][0].chosen
                # We untag the element since it is tagged via being a choice from GeneralName
                issuer = issuer.untag()
                authority_serial = self.authority_key_identifier_value['authority_cert_serial_number'].native
                self._authority_issuer_serial = issuer.sha256 + b':' + str_cls(authority_serial).encode('ascii')
            else:
                self._authority_issuer_serial = None
        return self._authority_issuer_serial

    @property
    def crl_distribution_points(self):
        """
        Returns complete CRL URLs - does not include delta CRLs

        :return:
            A list of zero or more DistributionPoint objects
        """

        if self._crl_distribution_points is None:
            self._crl_distribution_points = self._get_http_crl_distribution_points(self.crl_distribution_points_value)
        return self._crl_distribution_points

    @property
    def delta_crl_distribution_points(self):
        """
        Returns delta CRL URLs - does not include complete CRLs

        :return:
            A list of zero or more DistributionPoint objects
        """

        if self._delta_crl_distribution_points is None:
            self._delta_crl_distribution_points = self._get_http_crl_distribution_points(self.freshest_crl_value)
        return self._delta_crl_distribution_points

    def _get_http_crl_distribution_points(self, crl_distribution_points):
        """
        Fetches the DistributionPoint object for non-relative, HTTP CRLs
        referenced by the certificate

        :param crl_distribution_points:
            A CRLDistributionPoints object to grab the DistributionPoints from

        :return:
            A list of zero or more DistributionPoint objects
        """

        output = []

        if crl_distribution_points is None:
            return []

        for distribution_point in crl_distribution_points:
            distribution_point_name = distribution_point['distribution_point']
            if distribution_point_name is VOID:
                continue
            # 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':
                    output.append(distribution_point)

        return output

    @property
    def ocsp_urls(self):
        """
        :return:
            A list of zero or more unicode strings of the OCSP URLs for this
            cert
        """

        if not self.authority_information_access_value:
            return []

        output = []
        for entry in self.authority_information_access_value:
            if entry['access_method'].native == 'ocsp':
                location = entry['access_location']
                if location.name != 'uniform_resource_identifier':
                    continue
                url = location.native
                if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')):
                    output.append(url)
        return output

    @property
    def valid_domains(self):
        """
        :return:
            A list of unicode strings of valid domain names for the certificate.
            Wildcard certificates will have a domain in the form: *.example.com
        """

        if self._valid_domains is None:
            self._valid_domains = []

            # For the subject alt name extension, we can look at the name of
            # the choice selected since it distinguishes between domain names,
            # email addresses, IPs, etc
            if self.subject_alt_name_value:
                for general_name in self.subject_alt_name_value:
                    if general_name.name == 'dns_name' and general_name.native not in self._valid_domains:
                        self._valid_domains.append(general_name.native)

            # If there was no subject alt name extension, and the common name
            # in the subject looks like a domain, that is considered the valid
            # list. This is done because according to
            # https://tools.ietf.org/html/rfc6125#section-6.4.4, the common
            # name should not be used if the subject alt name is present.
            else:
                pattern = re.compile('^(\\*\\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$')
                for rdn in self.subject.chosen:
                    for name_type_value in rdn:
                        if name_type_value['type'].native == 'common_name':
                            value = name_type_value['value'].native
                            if pattern.match(value):
                                self._valid_domains.append(value)

        return self._valid_domains

    @property
    def valid_ips(self):
        """
        :return:
            A list of unicode strings of valid IP addresses for the certificate
        """

        if self._valid_ips is None:
            self._valid_ips = []

            if self.subject_alt_name_value:
                for general_name in self.subject_alt_name_value:
                    if general_name.name == 'ip_address':
                        self._valid_ips.append(general_name.native)

        return self._valid_ips

    @property
    def ca(self):
        """
        :return;
            A boolean - if the certificate is marked as a CA
        """

        return self.basic_constraints_value and self.basic_constraints_value['ca'].native

    @property
    def max_path_length(self):
        """
        :return;
            None or an integer of the maximum path length
        """

        if not self.ca:
            return None
        return self.basic_constraints_value['path_len_constraint'].native

    @property
    def self_issued(self):
        """
        :return:
            A boolean - if the certificate is self-issued, as defined by RFC
            5280
        """

        if self._self_issued is None:
            self._self_issued = self.subject == self.issuer
        return self._self_issued

    @property
    def self_signed(self):
        """
        :return:
            A unicode string of "no" or "maybe". The "maybe" result will
            be returned if the certificate issuer and subject are the same.
            If a key identifier and authority key identifier are present,
            they will need to match otherwise "no" will be returned.

            To verify is a certificate is truly self-signed, the signature
            will need to be verified. See the certvalidator package for
            one possible solution.
        """

        if self._self_signed is None:
            self._self_signed = 'no'
            if self.self_issued:
                if self.key_identifier:
                    if not self.authority_key_identifier:
                        self._self_signed = 'maybe'
                    elif self.authority_key_identifier == self.key_identifier:
                        self._self_signed = 'maybe'
                else:
                    self._self_signed = 'maybe'
        return self._self_signed

    @property
    def sha1(self):
        """
        :return:
            The SHA-1 hash of the DER-encoded bytes of this complete certificate
        """

        if self._sha1 is None:
            self._sha1 = hashlib.sha1(self.dump()).digest()
        return self._sha1

    @property
    def sha1_fingerprint(self):
        """
        :return:
            A unicode string of the SHA-1 hash, formatted using hex encoding
            with a space between each pair of characters, all uppercase
        """

        return ' '.join('%02X' % c for c in bytes_to_list(self.sha1))

    @property
    def sha256(self):
        """
        :return:
            The SHA-256 hash of the DER-encoded bytes of this complete
            certificate
        """

        if self._sha256 is None:
            self._sha256 = hashlib.sha256(self.dump()).digest()
        return self._sha256

    @property
    def sha256_fingerprint(self):
        """
        :return:
            A unicode string of the SHA-256 hash, formatted using hex encoding
            with a space between each pair of characters, all uppercase
        """

        return ' '.join('%02X' % c for c in bytes_to_list(self.sha256))

    def is_valid_domain_ip(self, domain_ip):
        """
        Check if a domain name or IP address is valid according to the
        certificate

        :param domain_ip:
            A unicode string of a domain name or IP address

        :return:
            A boolean - if the domain or IP is valid for the certificate
        """

        if not isinstance(domain_ip, str_cls):
            raise TypeError(unwrap(
                '''
                domain_ip must be a unicode string, not %s
                ''',
                type_name(domain_ip)
            ))

        encoded_domain_ip = domain_ip.encode('idna').decode('ascii').lower()

        is_ipv6 = encoded_domain_ip.find(':') != -1
        is_ipv4 = not is_ipv6 and re.match('^\\d+\\.\\d+\\.\\d+\\.\\d+$', encoded_domain_ip)
        is_domain = not is_ipv6 and not is_ipv4

        # Handle domain name checks
        if is_domain:
            if not self.valid_domains:
                return False

            domain_labels = encoded_domain_ip.split('.')

            for valid_domain in self.valid_domains:
                encoded_valid_domain = valid_domain.encode('idna').decode('ascii').lower()
                valid_domain_labels = encoded_valid_domain.split('.')

                # The domain must be equal in label length to match
                if len(valid_domain_labels) != len(domain_labels):
                    continue

                if valid_domain_labels == domain_labels:
                    return True

                is_wildcard = self._is_wildcard_domain(encoded_valid_domain)
                if is_wildcard and self._is_wildcard_match(domain_labels, valid_domain_labels):
                    return True

            return False

        # Handle IP address checks
        if not self.valid_ips:
            return False

        family = socket.AF_INET if is_ipv4 else socket.AF_INET6
        normalized_ip = inet_pton(family, encoded_domain_ip)

        for valid_ip in self.valid_ips:
            valid_family = socket.AF_INET if valid_ip.find('.') != -1 else socket.AF_INET6
            normalized_valid_ip = inet_pton(valid_family, valid_ip)

            if normalized_valid_ip == normalized_ip:
                return True

        return False

    def _is_wildcard_domain(self, domain):
        """
        Checks if a domain is a valid wildcard according to
        https://tools.ietf.org/html/rfc6125#section-6.4.3

        :param domain:
            A unicode string of the domain name, where any U-labels from an IDN
            have been converted to A-labels

        :return:
            A boolean - if the domain is a valid wildcard domain
        """

        # The * character must be present for a wildcard match, and if there is
        # most than one, it is an invalid wildcard specification
        if domain.count('*') != 1:
            return False

        labels = domain.lower().split('.')

        if not labels:
            return False

        # Wildcards may only appear in the left-most label
        if labels[0].find('*') == -1:
            return False

        # Wildcards may not be embedded in an A-label from an IDN
        if labels[0][0:4] == 'xn--':
            return False

        return True

    def _is_wildcard_match(self, domain_labels, valid_domain_labels):
        """
        Determines if the labels in a domain are a match for labels from a
        wildcard valid domain name

        :param domain_labels:
            A list of unicode strings, with A-label form for IDNs, of the labels
            in the domain name to check

        :param valid_domain_labels:
            A list of unicode strings, with A-label form for IDNs, of the labels
            in a wildcard domain pattern

        :return:
            A boolean - if the domain matches the valid domain
        """

        first_domain_label = domain_labels[0]
        other_domain_labels = domain_labels[1:]

        wildcard_label = valid_domain_labels[0]
        other_valid_domain_labels = valid_domain_labels[1:]

        # The wildcard is only allowed in the first label, so if
        # The subsequent labels are not equal, there is no match
        if other_domain_labels != other_valid_domain_labels:
            return False

        if wildcard_label == '*':
            return True

        wildcard_regex = re.compile('^' + wildcard_label.replace('*', '.*') + '$')
        if wildcard_regex.match(first_domain_label):
            return True

        return False class Extensions(SequenceOf):
    _child_spec = Extension class GeneralName(Choice):
    _alternatives = [
        ('other_name', AnotherName, {'implicit': 0}),
        ('rfc822_name', EmailAddress, {'implicit': 1}),
        ('dns_name', DNSName, {'implicit': 2}),
        ('x400_address', ORAddress, {'implicit': 3}),
        ('directory_name', Name, {'explicit': 4}),
        ('edi_party_name', EDIPartyName, {'implicit': 5}),
        ('uniform_resource_identifier', URI, {'implicit': 6}),
        ('ip_address', IPAddress, {'implicit': 7}),
        ('registered_id', ObjectIdentifier, {'implicit': 8}),
    ]

    def __ne__(self, other):
        return not self == other

    def __eq__(self, other):
        """
        Does not support other_name, x400_address or edi_party_name

        :param other:
            The other GeneralName to compare to

        :return:
            A boolean
        """

        if self.name in ('other_name', 'x400_address', 'edi_party_name'):
            raise ValueError(unwrap(
                '''
                Comparison is not supported for GeneralName objects of
                choice %s
                ''',
                self.name
            ))

        if other.name in ('other_name', 'x400_address', 'edi_party_name'):
            raise ValueError(unwrap(
                '''
                Comparison is not supported for GeneralName objects of choice
                %s''',
                other.name
            ))

        if self.name != other.name:
            return False

        return self.chosen == other.chosen class GeneralNames(SequenceOf):
    _child_spec = GeneralName class Name(Choice):
    _alternatives = [
        ('', RDNSequence),
    ]

    _human_friendly = None
    _sha1 = None
    _sha256 = None

    @classmethod
    def build(cls, name_dict, use_printable=False):
        """
        Creates a Name object from a dict of unicode string keys and values.
        The keys should be from NameType._map, or a dotted-integer OID unicode
        string.

        :param name_dict:
            A dict of name information, e.g. {"common_name": "Will Bond",
            "country_name": "US", "organization_name": "Codex Non Sufficit LC"}

        :param use_printable:
            A bool - if PrintableString should be used for encoding instead of
            UTF8String. This is for backwards compatibility with old software.

        :return:
            An x509.Name object
        """

        rdns = []
        if not use_printable:
            encoding_name = 'utf8_string'
            encoding_class = UTF8String
        else:
            encoding_name = 'printable_string'
            encoding_class = PrintableString

        # Sort the attributes according to NameType.preferred_order
        name_dict = OrderedDict(
            sorted(
                name_dict.items(),
                key=lambda item: NameType.preferred_ordinal(item[0])
            )
        )

        for attribute_name, attribute_value in name_dict.items():
            attribute_name = NameType.map(attribute_name)
            if attribute_name == 'email_address':
                value = EmailAddress(attribute_value)
            elif attribute_name == 'domain_component':
                value = DNSName(attribute_value)
            elif attribute_name in set(['dn_qualifier', 'country_name', 'serial_number']):
                value = DirectoryString(
                    name='printable_string',
                    value=PrintableString(attribute_value)
                )
            else:
                value = DirectoryString(
                    name=encoding_name,
                    value=encoding_class(attribute_value)
                )

            rdns.append(RelativeDistinguishedName([
                NameTypeAndValue({
                    'type': attribute_name,
                    'value': value
                })
            ]))

        return cls(name='', value=RDNSequence(rdns))

    @property
    def hashable(self):
        """
        :return:
            A unicode string that can be used as a dict key or in a set
        """

        return self.chosen.hashable

    def __len__(self):
        return len(self.chosen)

    def __ne__(self, other):
        return not self == other

    def __eq__(self, other):
        """
        Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1

        :param other:
            Another Name object

        :return:
            A boolean
        """

        if not isinstance(other, Name):
            return False
        return self.chosen == other.chosen

    @property
    def native(self):
        if self._native is None:
            self._native = OrderedDict()
            for rdn in self.chosen.native:
                for type_val in rdn:
                    field_name = type_val['type']
                    if field_name in self._native:
                        existing = self._native[field_name]
                        if not isinstance(existing, list):
                            existing = self._native[field_name] = [existing]
                        existing.append(type_val['value'])
                    else:
                        self._native[field_name] = type_val['value']
        return self._native

    @property
    def human_friendly(self):
        """
        :return:
            A human-friendly unicode string containing the parts of the name
        """

        if self._human_friendly is None:
            data = OrderedDict()
            last_field = None
            for rdn in self.chosen:
                for type_val in rdn:
                    field_name = type_val['type'].human_friendly
                    last_field = field_name
                    if field_name in data:
                        data[field_name] = [data[field_name]]
                        data[field_name].append(type_val['value'])
                    else:
                        data[field_name] = type_val['value']
            to_join = []
            keys = data.keys()
            if last_field == 'Country':
                keys = reversed(list(keys))
            for key in keys:
                value = data[key]
                native_value = self._recursive_humanize(value)
                to_join.append('%s: %s' % (key, native_value))

            has_comma = False
            for element in to_join:
                if element.find(',') != -1:
                    has_comma = True
                    break

            separator = ', ' if not has_comma else '; '
            self._human_friendly = separator.join(to_join[::-1])

        return self._human_friendly

    def _recursive_humanize(self, value):
        """
        Recursively serializes data compiled from the RDNSequence

        :param value:
            An Asn1Value object, or a list of Asn1Value objects

        :return:
            A unicode string
        """

        if isinstance(value, list):
            return ', '.join(
                reversed([self._recursive_humanize(sub_value) for sub_value in value])
            )
        return value.native

    @property
    def sha1(self):
        """
        :return:
            The SHA1 hash of the DER-encoded bytes of this name
        """

        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 name
        """

        if self._sha256 is None:
            self._sha256 = hashlib.sha256(self.dump()).digest()
        return self._sha256
    import zlib
from .algos import (
    _ForceNullParameters,
    DigestAlgorithm,
    EncryptionAlgorithm,
    EncryptionAlgorithmId,
    HmacAlgorithm,
    KdfAlgorithm,
    RSAESOAEPParams,
    SignedDigestAlgorithm,
)
from .core import (
    Any,
    BitString,
    Choice,
    Enumerated,
    GeneralizedTime,
    Integer,
    ObjectIdentifier,
    OctetBitString,
    OctetString,
    ParsableOctetString,
    Sequence,
    SequenceOf,
    SetOf,
    UTCTime,
    UTF8String,
)
from .crl import CertificateList
from .keys import PublicKeyInfo
from .ocsp import OCSPResponse
from .x509 import Attributes, Certificate, Extensions, GeneralName, GeneralNames, Name
    ]


class KeyEncryptionAlgorithmId(ObjectIdentifier):
    _map = {
        '1.2.840.113549.1.1.1': 'rsaes_pkcs1v15',
        '1.2.840.113549.1.1.7': 'rsaes_oaep',
        '2.16.840.1.101.3.4.1.5': 'aes128_wrap',
        '2.16.840.1.101.3.4.1.8': 'aes128_wrap_pad',
        '2.16.840.1.101.3.4.1.25': 'aes192_wrap',
        '2.16.840.1.101.3.4.1.28': 'aes192_wrap_pad',
        '2.16.840.1.101.3.4.1.45': 'aes256_wrap',
        '2.16.840.1.101.3.4.1.48': 'aes256_wrap_pad',
    }

    _reverse_map = {
        'rsa': '1.2.840.113549.1.1.1',
        'rsaes_pkcs1v15': '1.2.840.113549.1.1.1',
        'rsaes_oaep': '1.2.840.113549.1.1.7',
        'aes128_wrap': '2.16.840.1.101.3.4.1.5',
        'aes128_wrap_pad': '2.16.840.1.101.3.4.1.8',
        'aes192_wrap': '2.16.840.1.101.3.4.1.25',
        'aes192_wrap_pad': '2.16.840.1.101.3.4.1.28',
        'aes256_wrap': '2.16.840.1.101.3.4.1.45',
        'aes256_wrap_pad': '2.16.840.1.101.3.4.1.48',
    }


class KeyEncryptionAlgorithm(_ForceNullParameters, Sequence):
    _fields = [
        ('algorithm', KeyEncryptionAlgorithmId),
        ('parameters', Any, {'optional': True}),
    ]

    _oid_pair = ('algorithm', 'parameters')
    _oid_specs = {
        'rsaes_oaep': RSAESOAEPParams,
    }


class KeyTransRecipientInfo(Sequence):
    _fields = [
        ('version', CMSVersion),
        ('rid', RecipientIdentifier),
        ('key_encryption_algorithm', KeyEncryptionAlgorithm),
        ('encrypted_key', OctetString),
    ]


class OriginatorIdentifierOrKey(Choice):
    _alternatives = [
        ('issuer_and_serial_number', IssuerAndSerialNumber),
        ('subject_key_identifier', OctetString, {'implicit': 0}),
        ('originator_key', PublicKeyInfo, {'implicit': 1}),
    ]


class OtherKeyAttribute(Sequence):
    _fields = [
        ('key_attr_id', ObjectIdentifier),
        ('key_attr', Any),
    ]


class RecipientKeyIdentifier(Sequence):
    _fields = [
        ('subject_key_identifier', OctetString),
        ('date', GeneralizedTime, {'optional': True}),
        ('other', OtherKeyAttribute, {'optional': True}),
    ]


class KeyAgreementRecipientIdentifier(Choice):
    _alternatives = [
        ('issuer_and_serial_number', IssuerAndSerialNumber),
        ('r_key_id', RecipientKeyIdentifier, {'implicit': 0}),
    ]


class RecipientEncryptedKey(Sequence):
    _fields = [
        ('rid', KeyAgreementRecipientIdentifier),
        ('encrypted_key', OctetString),
    ]


class RecipientEncryptedKeys(SequenceOf):
    _child_spec = RecipientEncryptedKey


class KeyAgreeRecipientInfo(Sequence):
    _fields = [
        ('version', CMSVersion),
        ('originator', OriginatorIdentifierOrKey, {'explicit': 0}),
        ('ukm', OctetString, {'explicit': 1, 'optional': True}),
        ('key_encryption_algorithm', KeyEncryptionAlgorithm),
        ('recipient_encrypted_keys', RecipientEncryptedKeys),
    ]


class KEKIdentifier(Sequence):
    _fields = [
        ('key_identifier', OctetString),
        ('date', GeneralizedTime, {'optional': True}),
        ('other', OtherKeyAttribute, {'optional': True}),
    ]


class KEKRecipientInfo(Sequence):
    _fields = [
        ('version', CMSVersion),
        ('kekid', KEKIdentifier),
        ('key_encryption_algorithm', KeyEncryptionAlgorithm),
        ('encrypted_key', OctetString),
    ]


class PasswordRecipientInfo(Sequence):
    _fields = [
        ('version', CMSVersion),

        ('key_derivation_algorithm', KdfAlgorithm, {'implicit': 0, 'optional': True}),