class _CertificateBase():

    asn1 = None

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

        return self.public_key.algorithm

    @property
    def curve(self):
        """
        :return:
            A unicode string of EC curve name
        """

        return self.public_key.curve

    @property
    def bit_size(self):
        """
        :return:
            The number of bits in the public key, as an integer
        """

        return self.public_key.bit_size

    @property
    def byte_size(self):
        """
        :return:
            The number of bytes in the public key, as an integer
        """

        return self.public_key.byte_size def _fingerprint(key_object, load_private_key):
    """
    Returns a fingerprint used for correlating public keys and private keys

    :param key_object:
        An asn1crypto.keys.PrivateKeyInfo or asn1crypto.keys.PublicKeyInfo

    :raises:
        ValueError - when the key_object is not of the proper type

    ;return:
        A byte string fingerprint
    """

    if isinstance(key_object, PrivateKeyInfo):
        key = key_object['private_key'].parsed

        if key_object.algorithm == 'rsa':
            to_hash = '%d:%d' % (
                key['modulus'].native,
                key['public_exponent'].native,
            )

        elif key_object.algorithm == 'dsa':
            params = key_object['private_key_algorithm']['parameters']
            public_key = Integer(pow(
                params['g'].native,
                key_object['private_key'].parsed.native,
                params['p'].native
            ))

            to_hash = '%d:%d:%d:%d' % (
                params['p'].native,
                params['q'].native,
                params['g'].native,
                public_key.native,
            )

        elif key_object.algorithm == 'ec':
            public_key = key['public_key'].native
            if public_key is None:
                # This is gross, but since the EC public key is optional,
                # and we need to load the private key and use the crypto lib
                # to get the public key, we have to import the platform-specific
                # asymmetric implementation. This is the reason a bunch of the
                # imports are module imports, so we don't get an import cycle.
                public_key_object = load_private_key(key_object).public_key
                public_key = public_key_object.asn1['public_key'].parsed.native

            to_hash = '%s:' % key_object.curve[1]
            to_hash = to_hash.encode('utf-8')
            to_hash += public_key

        if isinstance(to_hash, str_cls):
            to_hash = to_hash.encode('utf-8')

        return hashlib.sha256(to_hash).digest()

    if isinstance(key_object, PublicKeyInfo):
        if key_object.algorithm == 'rsa':
            key = key_object['public_key'].parsed

            to_hash = '%d:%d' % (
                key['modulus'].native,
                key['public_exponent'].native,
            )

        elif key_object.algorithm == 'dsa':
            key = key_object['public_key'].parsed
            params = key_object['algorithm']['parameters']

            to_hash = '%d:%d:%d:%d' % (
                params['p'].native,
                params['q'].native,
                params['g'].native,
                key.native,
            )

        elif key_object.algorithm == 'ec':
            public_key = key_object['public_key'].native

            to_hash = '%s:' % key_object.curve[1]
            to_hash = to_hash.encode('utf-8')
            to_hash += public_key

        if isinstance(to_hash, str_cls):
            to_hash = to_hash.encode('utf-8')

        return hashlib.sha256(to_hash).digest()

    raise ValueError(pretty_message(
        '''
        key_object must be an instance of the
        asn1crypto.keys.PrivateKeyInfo or asn1crypto.keys.PublicKeyInfo
        classes, not %s
        ''',
        type_name(key_object)
    )) def _parse_pkcs12(data, password, load_private_key):
    """
    Parses a PKCS#12 ANS.1 DER-encoded structure and extracts certs and keys

    :param data:
        A byte string of a DER-encoded PKCS#12 file

    :param password:
        A byte string of the password to any encrypted data

    :param load_private_key:
        A callable that will accept a byte string and return an
        oscrypto.asymmetric.PrivateKey object

    :raises:
        ValueError - when any of the parameters are of the wrong type or value
        OSError - when an error is returned by one of the OS decryption functions

    :return:
        A three-element tuple of:
         1. An asn1crypto.keys.PrivateKeyInfo object
         2. An asn1crypto.x509.Certificate object
         3. A list of zero or more asn1crypto.x509.Certificate objects that are
            "extra" certificates, possibly intermediates from the cert chain
    """

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

    if password is not None:
        if not isinstance(password, byte_cls):
            raise TypeError(pretty_message(
                '''
                password must be a byte string, not %s
                ''',
                type_name(password)
            ))
    else:
        password = b''

    certs = {}
    private_keys = {}

    pfx = Pfx.load(data)

    auth_safe = pfx['auth_safe']
    if auth_safe['content_type'].native != 'data':
        raise ValueError(pretty_message(
            '''
            Only password-protected PKCS12 files are currently supported
            '''
        ))
    authenticated_safe = pfx.authenticated_safe

    mac_data = pfx['mac_data']
    if mac_data:
        mac_algo = mac_data['mac']['digest_algorithm']['algorithm'].native
        key_length = {
            'sha1': 20,
            'sha224': 28,
            'sha256': 32,
            'sha384': 48,
            'sha512': 64,
            'sha512_224': 28,
            'sha512_256': 32,
        }[mac_algo]
        mac_key = pkcs12_kdf(
            mac_algo,
            password,
            mac_data['mac_salt'].native,
            mac_data['iterations'].native,
            key_length,
            3  # ID 3 is for generating an HMAC key
        )
        hash_mod = getattr(hashlib, mac_algo)
        computed_hmac = hmac.new(mac_key, auth_safe['content'].contents, hash_mod).digest()
        stored_hmac = mac_data['mac']['digest'].native
        if not constant_compare(computed_hmac, stored_hmac):
            raise ValueError('Password provided is invalid')

    for content_info in authenticated_safe:
        content = content_info['content']

        if isinstance(content, OctetString):
            _parse_safe_contents(content.native, certs, private_keys, password, load_private_key)

        elif isinstance(content, EncryptedData):
            encrypted_content_info = content['encrypted_content_info']

            encryption_algorithm_info = encrypted_content_info['content_encryption_algorithm']
            encrypted_content = encrypted_content_info['encrypted_content'].native
            decrypted_content = _decrypt_encrypted_data(encryption_algorithm_info, encrypted_content, password)

            _parse_safe_contents(decrypted_content, certs, private_keys, password, load_private_key)

        else:
            raise ValueError(pretty_message(
                '''
                Public-key-based PKCS12 files are not currently supported
                '''
            ))

    key_fingerprints = set(private_keys.keys())
    cert_fingerprints = set(certs.keys())

    common_fingerprints = sorted(list(key_fingerprints & cert_fingerprints))

    key = None
    cert = None
    other_certs = []

    if len(common_fingerprints) >= 1:
        fingerprint = common_fingerprints[0]
        key = private_keys[fingerprint]
        cert = certs[fingerprint]
        other_certs = [certs[f] for f in certs if f != fingerprint]
        return (key, cert, other_certs)

    if len(private_keys) > 0:
        first_key = sorted(list(private_keys.keys()))[0]
        key = private_keys[first_key]

    if len(certs) > 0:
        first_key = sorted(list(certs.keys()))[0]
        cert = certs[first_key]
        del certs[first_key]

    if len(certs) > 0:
        other_certs = sorted(list(certs.values()), key=lambda c: c.subject.human_friendly)

    return (key, cert, other_certs) class _PrivateKeyBase():

    asn1 = None
    _fingerprint = None

    def unwrap(self):
        """
        Unwraps the private key into an asn1crypto.keys.RSAPrivateKey,
        asn1crypto.keys.DSAPrivateKey or asn1crypto.keys.ECPrivateKey object

        :return:
            An asn1crypto.keys.RSAPrivateKey, asn1crypto.keys.DSAPrivateKey or
            asn1crypto.keys.ECPrivateKey object
        """

        if self.algorithm == 'rsa':
            return self.asn1['private_key'].parsed

        if self.algorithm == 'dsa':
            params = self.asn1['private_key_algorithm']['parameters']
            return DSAPrivateKey({
                'version': 0,
                'p': params['p'],
                'q': params['q'],
                'g': params['g'],
                'public_key': self.public_key.unwrap(),
                'private_key': self.asn1['private_key'].parsed,
            })

        if self.algorithm == 'ec':
            output = self.asn1['private_key'].parsed
            output['parameters'] = self.asn1['private_key_algorithm']['parameters']
            output['public_key'] = self.public_key.unwrap()
            return output

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

        return self.asn1.algorithm

    @property
    def curve(self):
        """
        :return:
            A unicode string of EC curve name
        """

        return self.asn1.curve[1]

    @property
    def bit_size(self):
        """
        :return:
            The number of bits in the key, as an integer
        """

        return self.asn1.bit_size

    @property
    def byte_size(self):
        """
        :return:
            The number of bytes in the key, as an integer
        """

        return self.asn1.byte_size class _PublicKeyBase():

    asn1 = None
    _fingerprint = None

    def unwrap(self):
        """
        Unwraps a public key into an asn1crypto.keys.RSAPublicKey,
        asn1crypto.core.Integer (for DSA) or asn1crypto.keys.ECPointBitString
        object

        :return:
            An asn1crypto.keys.RSAPublicKey, asn1crypto.core.Integer or
            asn1crypto.keys.ECPointBitString object
        """

        if self.algorithm == 'ec':
            return self.asn1['public_key']
        return self.asn1['public_key'].parsed

    @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)
        """

        if self._fingerprint is None:
            self._fingerprint = _fingerprint(self.asn1, None)
        return self._fingerprint

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

        return self.asn1.algorithm

    @property
    def curve(self):
        """
        :return:
            A unicode string of EC curve name
        """

        return self.asn1.curve[1]

    @property
    def bit_size(self):
        """
        :return:
            The number of bits in the key, as an integer
        """

        return self.asn1.bit_size

    @property
    def byte_size(self):
        """
        :return:
            The number of bytes in the key, as an integer
        """

        return self.asn1.byte_size def _unwrap_private_key_info(key_info):
    """
    Unwraps an asn1crypto.keys.PrivateKeyInfo object into an
    asn1crypto.keys.RSAPrivateKey, asn1crypto.keys.DSAPrivateKey
    or asn1crypto.keys.ECPrivateKey.

    :param key_info:
        An asn1crypto.keys.PrivateKeyInfo object

    :return:
        One of:
         - asn1crypto.keys.RSAPrivateKey
         - asn1crypto.keys.DSAPrivateKey
         - asn1crypto.keys.ECPrivateKey
    """

    if key_info.algorithm == 'rsa':
        return key_info['private_key'].parsed

    if key_info.algorithm == 'dsa':
        params = key_info['private_key_algorithm']['parameters']
        parsed = key_info['private_key'].parsed
        return DSAPrivateKey({
            'version': 0,
            'p': params['p'],
            'q': params['q'],
            'g': params['g'],
            'public_key': Integer(pow(
                params['g'].native,
                parsed.native,
                params['p'].native
            )),
            'private_key': parsed,
        })

    if key_info.algorithm == 'ec':
        parsed = key_info['private_key'].parsed
        parsed['parameters'] = key_info['private_key_algorithm']['parameters']
        return parsed

    raise ValueError('Unsupported key_info.algorithm "%s"' % key_info.algorithm) def parse_certificate(data):
    """
    Loads a certificate from a DER or PEM-formatted file. Supports X.509
    certificates only.

    :param data:
        A byte string to load the certificate from

    :raises:
        ValueError - when the data does not appear to contain a certificate

    :return:
        An asn1crypto.x509.Certificate object
    """

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

    key_type = None

    # Appears to be PEM formatted
    if re.match(b'\\s*-----', data) is not None:
        key_type, _, data = _unarmor_pem(data)

        if key_type == 'private key':
            raise ValueError(pretty_message(
                '''
                The data specified does not appear to be a certificate, but
                rather a private key
                '''
            ))

        if key_type == 'public key':
            raise ValueError(pretty_message(
                '''
                The data specified does not appear to be a certificate, but
                rather a public key
                '''
            ))

    if key_type is None or key_type == 'certificate':
        try:
            return Certificate.load(data)
        except (ValueError):
            pass  # Data was not a Certificate

    raise ValueError(pretty_message(
        '''
        The data specified does not appear to be a known certificate format
        '''
    )) def parse_private(data, password=None):
    """
    Loads a private key from a DER or PEM-formatted file. Supports RSA, DSA and
    EC private keys. Works with the follow formats:

     - RSAPrivateKey (PKCS#1)
     - ECPrivateKey (SECG SEC1 V2)
     - DSAPrivateKey (OpenSSL)
     - PrivateKeyInfo (RSA/DSA/EC - PKCS#8)
     - EncryptedPrivateKeyInfo (RSA/DSA/EC - PKCS#8)
     - Encrypted RSAPrivateKey (PEM only, OpenSSL)
     - Encrypted DSAPrivateKey (PEM only, OpenSSL)
     - Encrypted ECPrivateKey (PEM only, OpenSSL)

    :param data:
        A byte string to load the private key from

    :param password:
        The password to unencrypt the private key

    :raises:
        ValueError - when the data does not appear to contain a private key, or the password is invalid

    :return:
        An asn1crypto.keys.PrivateKeyInfo object
    """

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

    if password is not None:
        if not isinstance(password, byte_cls):
            raise TypeError(pretty_message(
                '''
                password must be a byte string, not %s
                ''',
                type_name(password)
            ))
    else:
        password = b''

    # Appears to be PEM formatted
    if re.match(b'\\s*-----', data) is not None:
        key_type, _, data = _unarmor_pem(data, password)

        if key_type == 'public key':
            raise ValueError(pretty_message(
                '''
                The data specified does not appear to be a private key, but
                rather a public key
                '''
            ))

        if key_type == 'certificate':
            raise ValueError(pretty_message(
                '''
                The data specified does not appear to be a private key, but
                rather a certificate
                '''
            ))

    try:
        pki = PrivateKeyInfo.load(data)
        # Call .native to fully parse since asn1crypto is lazy
        pki.native
        return pki
    except (ValueError):
        pass  # Data was not PrivateKeyInfo

    try:
        parsed_wrapper = EncryptedPrivateKeyInfo.load(data)
        encryption_algorithm_info = parsed_wrapper['encryption_algorithm']
        encrypted_data = parsed_wrapper['encrypted_data'].native
        decrypted_data = _decrypt_encrypted_data(encryption_algorithm_info, encrypted_data, password)
        pki = PrivateKeyInfo.load(decrypted_data)
        # Call .native to fully parse since asn1crypto is lazy
        pki.native
        return pki
    except (ValueError):
        pass  # Data was not EncryptedPrivateKeyInfo

    try:
        parsed = RSAPrivateKey.load(data)
        # Call .native to fully parse since asn1crypto is lazy
        parsed.native
        return PrivateKeyInfo.wrap(parsed, 'rsa')
    except (ValueError):
        pass  # Data was not an RSAPrivateKey

    try:
        parsed = DSAPrivateKey.load(data)
        # Call .native to fully parse since asn1crypto is lazy
        parsed.native
        return PrivateKeyInfo.wrap(parsed, 'dsa')
    except (ValueError):
        pass  # Data was not a DSAPrivateKey

    try:
        parsed = ECPrivateKey.load(data)
        # Call .native to fully parse since asn1crypto is lazy
        parsed.native
        return PrivateKeyInfo.wrap(parsed, 'ec')
    except (ValueError):
        pass  # Data was not an ECPrivateKey

    raise ValueError(pretty_message(
        '''
        The data specified does not appear to be a known private key format
        '''
    )) def parse_public(data):
    """
    Loads a public key from a DER or PEM-formatted file. Supports RSA, DSA and
    EC public keys. For RSA keys, both the old RSAPublicKey and
    SubjectPublicKeyInfo structures are supported. Also allows extracting a
    public key from an X.509 certificate.

    :param data:
        A byte string to load the public key from

    :raises:
        ValueError - when the data does not appear to contain a public key

    :return:
        An asn1crypto.keys.PublicKeyInfo object
    """

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

    key_type = None

    # Appears to be PEM formatted
    if re.match(b'\\s*-----', data) is not None:
        key_type, algo, data = _unarmor_pem(data)

        if key_type == 'private key':
            raise ValueError(pretty_message(
                '''
                The data specified does not appear to be a public key or
                certificate, but rather a private key
                '''
            ))

        # When a public key returning from _unarmor_pem has a known algorithm
        # of RSA, that means the DER structure is of the type RSAPublicKey, so
        # we need to wrap it in the PublicKeyInfo structure.
        if algo == 'rsa':
            return PublicKeyInfo.wrap(data, 'rsa')

    if key_type is None or key_type == 'public key':
        try:
            pki = PublicKeyInfo.load(data)
            # Call .native to fully parse since asn1crypto is lazy
            pki.native
            return pki
        except (ValueError):
            pass  # Data was not PublicKeyInfo

        try:
            rpk = RSAPublicKey.load(data)
            # Call .native to fully parse since asn1crypto is lazy
            rpk.native
            return PublicKeyInfo.wrap(rpk, 'rsa')
        except (ValueError):
            pass  # Data was not an RSAPublicKey

    if key_type is None or key_type == 'certificate':
        try:
            parsed_cert = Certificate.load(data)
            key_info = parsed_cert['tbs_certificate']['subject_public_key_info']
            return key_info
        except (ValueError):
            pass  # Data was not a cert

    raise ValueError('The data specified does not appear to be a known public key or certificate format') def new(library, type_, value=None):
    ffi_obj = _get_ffi(library)

    params = []
    if value is not None:
        params.append(value)
    if type_ in set(['BCRYPT_KEY_HANDLE', 'BCRYPT_ALG_HANDLE']):
        return ffi_obj.cast(type_, 0)
    return ffi_obj.new(type_, *params) def unwrap(point):
    return point[0] def bytes_from_buffer(buffer, maxlen=None):
    if maxlen is not None:
        return ffi.buffer(buffer, maxlen)[:]
    return ffi.buffer(buffer)[:] def buffer_from_bytes(initializer):
    if sys.platform == 'win32':
        return ffi.new('unsigned char[]', initializer)
    return ffi.new('char[]', initializer) def deref(point):
    return point[0] def null():
    return ffi.NULL def is_null(point):
    if point is None:
        return True
    if point == ffi.NULL:
        return True
    if ffi.getctype(ffi.typeof(point)) == 'void *':
        return False
    if point[0] == ffi.NULL:
        return True
    return False def pointer_set(pointer_, value):
    pointer_[0] = value def handle_sec_error(error, exception_class=None):
def _extract_policy_properties(value):
class SecurityConst():
    CSSMERR_APPLETP_HOSTNAME_MISMATCH = -2147408896
    CSSMERR_TP_CERT_EXPIRED = -2147409654
    CSSMERR_TP_CERT_NOT_VALID_YET = -2147409653
    CSSMERR_TP_CERT_REVOKED = -2147409652
    CSSMERR_TP_NOT_TRUSTED = -2147409622
    CSSMERR_TP_CERT_SUSPENDED = -2147409651
    APPLE_TP_REVOCATION_CRL = b'*\x86H\x86\xf7cd\x01\x06'
    APPLE_TP_REVOCATION_OCSP = b'*\x86H\x86\xf7cd\x01\x07'
    CSSM_APPLE_TP_OCSP_OPTS_VERSION = 0
    CSSM_TP_ACTION_OCSP_DISABLE_NET = 0x00000004
    CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE = 0x00000008
    CSSM_APPLE_TP_CRL_OPTS_VERSION = 0
    CSSM_KEYUSE_SIGN = 0x00000004
    CSSM_KEYUSE_VERIFY = 0x00000008
    CSSM_ALGID_DH = 2
    CSSM_ALGID_RSA = 42
    CSSM_ALGID_DSA = 43
    CSSM_ALGID_ECDSA = 73
    CSSM_KEYATTR_PERMANENT = 0x00000001
    CSSM_KEYATTR_EXTRACTABLE = 0x00000020 def handle_cf_error(error_pointer): class AsymmetricKeyError(Exception):

    """
    An exception when a key is invalid or unsupported
    """

    pass class IncompleteAsymmetricKeyError(AsymmetricKeyError):

    """
    An exception when a key is missing necessary information
    """

    pass class SignatureError(Exception):

    """
    An exception when validating a signature
    """

    pass def add_pss_padding(hash_algorithm, salt_length, key_length, message):
    """
    Pads a byte string using the EMSA-PSS-Encode operation described in PKCS#1
    v2.2.

    :param hash_algorithm:
        The string name of the hash algorithm to use: "sha1", "sha224",
        "sha256", "sha384", "sha512"

    :param salt_length:
        The length of the salt as an integer - typically the same as the length
        of the output from the hash_algorithm

    :param key_length:
        The length of the RSA key, in bits

    :param message:
        A byte string of the message to pad

    :return:
        The encoded (passed) message
    """

    if _backend != 'winlegacy' and sys.platform != 'darwin':
        raise SystemError(pretty_message(
            '''
            Pure-python RSA PSS signature padding addition code is only for
            Windows XP/2003 and OS X
            '''
        ))

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

    if not isinstance(salt_length, int_types):
        raise TypeError(pretty_message(
            '''
            salt_length must be an integer, not %s
            ''',
            type_name(salt_length)
        ))

    if salt_length < 0:
        raise ValueError(pretty_message(
            '''
            salt_length must be 0 or more - is %s
            ''',
            repr(salt_length)
        ))

    if not isinstance(key_length, int_types):
        raise TypeError(pretty_message(
            '''
            key_length must be an integer, not %s
            ''',
            type_name(key_length)
        ))

    if key_length < 512:
        raise ValueError(pretty_message(
            '''
            key_length must be 512 or more - is %s
            ''',
            repr(key_length)
        ))

    if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']):
        raise ValueError(pretty_message(
            '''
            hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384",
            "sha512", not %s
            ''',
            repr(hash_algorithm)
        ))

    hash_func = getattr(hashlib, hash_algorithm)

    # The maximal bit size of a non-negative integer is one less than the bit
    # size of the key since the first bit is used to store sign
    em_bits = key_length - 1
    em_len = int(math.ceil(em_bits / 8))

    message_digest = hash_func(message).digest()
    hash_length = len(message_digest)

    if em_len < hash_length + salt_length + 2:
        raise ValueError(pretty_message(
            '''
            Key is not long enough to use with specified hash_algorithm and
            salt_length
            '''
        ))

    if salt_length > 0:
        salt = os.urandom(salt_length)
    else:
        salt = b''

    m_prime = (b'\x00' * 8) + message_digest + salt

    m_prime_digest = hash_func(m_prime).digest()

    padding = b'\x00' * (em_len - salt_length - hash_length - 2)

    db = padding + b'\x01' + salt

    db_mask = _mgf1(hash_algorithm, m_prime_digest, em_len - hash_length - 1)

    masked_db = int_to_bytes(int_from_bytes(db) ^ int_from_bytes(db_mask))
    masked_db = fill_width(masked_db, len(db_mask))

    zero_bits = (8 * em_len) - em_bits
    left_bit_mask = ('0' * zero_bits) + ('1' * (8 - zero_bits))
    left_int_mask = int(left_bit_mask, 2)

    if left_int_mask != 255:
        masked_db = chr_cls(left_int_mask & ord(masked_db[0:1])) + masked_db[1:]

    return masked_db + m_prime_digest + b'\xBC' def verify_pss_padding(hash_algorithm, salt_length, key_length, message, signature):
    """
    Verifies the PSS padding on an encoded message

    :param hash_algorithm:
        The string name of the hash algorithm to use: "sha1", "sha224",
        "sha256", "sha384", "sha512"

    :param salt_length:
        The length of the salt as an integer - typically the same as the length
        of the output from the hash_algorithm

    :param key_length:
        The length of the RSA key, in bits

    :param message:
        A byte string of the message to pad

    :param signature:
        The signature to verify

    :return:
        A boolean indicating if the signature is invalid
    """

    if _backend != 'winlegacy' and sys.platform != 'darwin':
        raise SystemError(pretty_message(
            '''
            Pure-python RSA PSS signature padding verification code is only for
            Windows XP/2003 and OS X
            '''
        ))

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

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

    if not isinstance(salt_length, int_types):
        raise TypeError(pretty_message(
            '''
            salt_length must be an integer, not %s
            ''',
            type_name(salt_length)
        ))

    if salt_length < 0:
        raise ValueError(pretty_message(
            '''
            salt_length must be 0 or more - is %s
            ''',
            repr(salt_length)
        ))

    if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']):
        raise ValueError(pretty_message(
            '''
            hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384",
            "sha512", not %s
            ''',
            repr(hash_algorithm)
        ))

    hash_func = getattr(hashlib, hash_algorithm)

    em_bits = key_length - 1
    em_len = int(math.ceil(em_bits / 8))

    message_digest = hash_func(message).digest()
    hash_length = len(message_digest)

    if em_len < hash_length + salt_length + 2:
        return False

    if signature[-1:] != b'\xBC':
        return False

    zero_bits = (8 * em_len) - em_bits

    masked_db_length = em_len - hash_length - 1
    masked_db = signature[0:masked_db_length]

    first_byte = ord(masked_db[0:1])
    bits_that_should_be_zero = first_byte >> (8 - zero_bits)
    if bits_that_should_be_zero != 0:
        return False

    m_prime_digest = signature[masked_db_length:masked_db_length + hash_length]

    db_mask = _mgf1(hash_algorithm, m_prime_digest, em_len - hash_length - 1)

    left_bit_mask = ('0' * zero_bits) + ('1' * (8 - zero_bits))
    left_int_mask = int(left_bit_mask, 2)

    if left_int_mask != 255:
        db_mask = chr_cls(left_int_mask & ord(db_mask[0:1])) + db_mask[1:]

    db = int_to_bytes(int_from_bytes(masked_db) ^ int_from_bytes(db_mask))
    if len(db) < len(masked_db):
        db = (b'\x00' * (len(masked_db) - len(db))) + db

    zero_length = em_len - hash_length - salt_length - 2
    zero_string = b'\x00' * zero_length
    if not constant_compare(db[0:zero_length], zero_string):
        return False

    if db[zero_length:zero_length + 1] != b'\x01':
        return False

    salt = db[0 - salt_length:]

    m_prime = (b'\x00' * 8) + message_digest + salt

    h_prime = hash_func(m_prime).digest()

    return constant_compare(m_prime_digest, h_prime) def remove_pkcs1v15_encryption_padding(key_length, data):
    """
    Removes PKCS#1 v1.5 padding from a decrypted message using constant time
    operations

    :param key_length:
        An integer of the number of bytes in the key

    :param data:
        A byte string to unpad

    :return:
        The unpadded data as a byte string
    """

    if not _is_osx_107():
        raise SystemError(pretty_message(
            '''
            Pure-python RSA PKCSv1.5 encryption padding removal code is only
            for OS X 10.7
            '''
        ))

    return _remove_pkcs1v15_padding(key_length, data, 'decrypting')
from base64 import b32encode
from .._asn1 import (
    Certificate as Asn1Certificate,
    ECDomainParameters,
    Integer,
    KeyExchangeAlgorithm,
    Null,
    PrivateKeyInfo,
    PublicKeyAlgorithm,
    PublicKeyInfo,
    RSAPublicKey,
)
from .._asymmetric import (
    _CertificateBase,
    _fingerprint,
    _parse_pkcs12,
    _PrivateKeyBase,
    _PublicKeyBase,
    _unwrap_private_key_info,
    parse_certificate,
    parse_private,
    parse_public,
)
from .._errors import pretty_message
from .._ffi import new, unwrap, bytes_from_buffer, buffer_from_bytes, deref, null, is_null, pointer_set
from ._security import Security, SecurityConst, handle_sec_error, osx_version_info
from ._core_foundation import CoreFoundation, CFHelpers, handle_cf_error
from .util import rand_bytes
from ..errors import AsymmetricKeyError, IncompleteAsymmetricKeyError, SignatureError
from .._pkcs1 import add_pss_padding, verify_pss_padding, remove_pkcs1v15_encryption_padding
from .._types import type_name, str_cls, byte_cls, int_types
import os
import shutil
import tempfile
        :return:
            A PublicKey object corresponding to this private key.
        """

        if self._public_key is None:
            cf_data_private = None
            try:
                # We export here so that Security.framework will fill in the EC
                # public key for us, instead of us having to compute it
                cf_data_private_pointer = new(CoreFoundation, 'CFDataRef *')
                result = Security.SecItemExport(self.sec_key_ref, 0, 0, null(), cf_data_private_pointer)
                handle_sec_error(result)
                cf_data_private = unwrap(cf_data_private_pointer)
                private_key_bytes = CFHelpers.cf_data_to_bytes(cf_data_private)

                key = parse_private(private_key_bytes)

                if key.algorithm == 'rsa':
                    public_asn1 = PublicKeyInfo({
                        'algorithm': PublicKeyAlgorithm({
                            'algorithm': 'rsa',
                            'parameters': Null()
                        }),
                        'public_key': RSAPublicKey({
                            'modulus': key['private_key'].parsed['modulus'],
                            'public_exponent': key['private_key'].parsed['public_exponent'],
                        })
                    })

                elif key.algorithm == 'dsa':
                    params = key['private_key_algorithm']['parameters']
                    public_asn1 = PublicKeyInfo({
                        'algorithm': PublicKeyAlgorithm({
                            'algorithm': 'dsa',
                            'parameters': params.copy()
                        }),
                        'public_key': Integer(pow(
                            params['g'].native,
                            key['private_key'].parsed.native,
                            params['p'].native
                        ))
                    })

                elif key.algorithm == 'ec':
                    public_asn1 = PublicKeyInfo({
                        'algorithm': PublicKeyAlgorithm({
                            'algorithm': 'ec',
                            'parameters': ECDomainParameters(
                                name='named',
                                value=self.curve
                            )
                        }),
                        'public_key': key['private_key'].parsed['public_key'],
                    })

            finally:
                if cf_data_private:
                    CoreFoundation.CFRelease(cf_data_private)

            self._public_key = _load_key(public_asn1)

        return self._public_key

    @property
    def fingerprint(self):
        """
        Creates a fingerprint that can be compared with a public 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)
        """

        if self._fingerprint is None:
            self._fingerprint = _fingerprint(self.asn1, load_private_key)
        return self._fingerprint

    def __del__(self):
        if self.sec_key_ref:
            self._lib.CFRelease(self.sec_key_ref)
            self._lib = None
            self.sec_key_ref = None


class PublicKey(_PublicKeyBase):
    """
    Container for the OS crypto library representation of a public key
    """

    sec_key_ref = None

    # A reference to the library used in the destructor to make sure it hasn't
    # been garbage collected by the time this object is garbage collected
    _lib = None

    def __init__(self, sec_key_ref, asn1):
        """
        :param sec_key_ref:
            A Security framework SecKeyRef value from loading/importing the
            key

        :param asn1:
            An asn1crypto.keys.PublicKeyInfo object
        """

        self.sec_key_ref = sec_key_ref
        self.asn1 = asn1
        self._lib = CoreFoundation

    def __del__(self):
        if self.sec_key_ref:
            self._lib.CFRelease(self.sec_key_ref)
            self._lib = None
            self.sec_key_ref = None



class Certificate(_CertificateBase):