def test_check_startup_errors_are_allowed(self):
     b = Binding()
     b.lib.ERR_put_error(b.lib.ERR_LIB_EVP, b.lib.EVP_F_EVP_ENCRYPTFINAL_EX,
                         b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH, b"",
                         -1)
     b._register_osrandom_engine()
     assert _consume_errors(b.lib) == []
    def test_fallback_crypto_lock_via_binding_api(self):
        b = Binding()
        b.init_static_locks()

        self._skip_if_not_fallback_lock(b)

        lock = b._locks[b.lib.CRYPTO_LOCK_SSL]

        with pytest.raises(RuntimeError):
            b._lock_cb(0, b.lib.CRYPTO_LOCK_SSL, "<test>", 1)

        # errors shouldn't cause locking
        assert lock.acquire(False)
        lock.release()

        b._lock_cb(b.lib.CRYPTO_LOCK | b.lib.CRYPTO_READ,
                   b.lib.CRYPTO_LOCK_SSL, "<test>", 1)
        # locked
        assert not lock.acquire(False)

        b._lock_cb(b.lib.CRYPTO_UNLOCK | b.lib.CRYPTO_READ,
                   b.lib.CRYPTO_LOCK_SSL, "<test>", 1)
        # unlocked
        assert lock.acquire(False)
        lock.release()
    def test_fallback_crypto_lock_via_openssl_api(self):
        b = Binding()
        b.init_static_locks()

        self._skip_if_not_fallback_lock(b)

        # check that the lock state changes appropriately
        lock = b._locks[b.lib.CRYPTO_LOCK_SSL]

        # starts out unlocked
        assert lock.acquire(False)
        lock.release()

        b.lib.CRYPTO_lock(b.lib.CRYPTO_LOCK | b.lib.CRYPTO_READ,
                          b.lib.CRYPTO_LOCK_SSL, b.ffi.NULL, 0)

        # becomes locked
        assert not lock.acquire(False)

        b.lib.CRYPTO_lock(b.lib.CRYPTO_UNLOCK | b.lib.CRYPTO_READ,
                          b.lib.CRYPTO_LOCK_SSL, b.ffi.NULL, 0)

        # then unlocked
        assert lock.acquire(False)
        lock.release()
Exemple #4
0
    def test_fallback_crypto_lock_via_openssl_api(self):
        b = Binding()
        b.init_static_locks()

        self._skip_if_not_fallback_lock(b)

        # check that the lock state changes appropriately
        lock = b._locks[b.lib.CRYPTO_LOCK_SSL]

        # starts out unlocked
        assert lock.acquire(False)
        lock.release()

        b.lib.CRYPTO_lock(
            b.lib.CRYPTO_LOCK | b.lib.CRYPTO_READ,
            b.lib.CRYPTO_LOCK_SSL, b.ffi.NULL, 0
        )

        # becomes locked
        assert not lock.acquire(False)

        b.lib.CRYPTO_lock(
            b.lib.CRYPTO_UNLOCK | b.lib.CRYPTO_READ,
            b.lib.CRYPTO_LOCK_SSL, b.ffi.NULL, 0
        )

        # then unlocked
        assert lock.acquire(False)
        lock.release()
Exemple #5
0
    def test_crypto_lock_init(self):
        b = Binding()
        if b.lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER:
            pytest.skip("Requires an older OpenSSL. Must be < 1.1.0")

        b.init_static_locks()
        lock_cb = b.lib.CRYPTO_get_locking_callback()
        assert lock_cb != b.ffi.NULL
    def test_crypto_lock_init(self):
        b = Binding()
        if b.lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER:
            pytest.skip("Requires an older OpenSSL. Must be < 1.1.0")

        b.init_static_locks()
        lock_cb = b.lib.CRYPTO_get_locking_callback()
        assert lock_cb != b.ffi.NULL
Exemple #7
0
    def test_fallback_crypto_lock_via_binding_api(self):
        b = Binding()
        b.init_static_locks()

        self._skip_if_not_fallback_lock(b)

        lock = b._locks[b.lib.CRYPTO_LOCK_SSL]

        with pytest.raises(RuntimeError):
            b._lock_cb(0, b.lib.CRYPTO_LOCK_SSL, "<test>", 1)

        # errors shouldn't cause locking
        assert lock.acquire(False)
        lock.release()

        b._lock_cb(b.lib.CRYPTO_LOCK | b.lib.CRYPTO_READ,
                   b.lib.CRYPTO_LOCK_SSL, "<test>", 1)
        # locked
        assert not lock.acquire(False)

        b._lock_cb(b.lib.CRYPTO_UNLOCK | b.lib.CRYPTO_READ,
                   b.lib.CRYPTO_LOCK_SSL, "<test>", 1)
        # unlocked
        assert lock.acquire(False)
        lock.release()
 def test_check_startup_errors_are_allowed(self):
     b = Binding()
     b.lib.ERR_put_error(
         b.lib.ERR_LIB_EVP,
         b.lib.EVP_F_EVP_ENCRYPTFINAL_EX,
         b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
         b"",
         -1
     )
     b._register_osrandom_engine()
     assert _consume_errors(b.lib) == []
    def test_crypto_lock_init(self):
        b = Binding()

        b.init_static_locks()
        lock_cb = b.lib.CRYPTO_get_locking_callback()
        if b.lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER:
            assert lock_cb == b.ffi.NULL
            assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 0
        else:
            assert lock_cb != b.ffi.NULL
            assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 1
Exemple #10
0
    def test_crypto_lock_init(self):
        b = Binding()

        b.init_static_locks()
        lock_cb = b.lib.CRYPTO_get_locking_callback()
        if not b.lib.CRYPTOGRAPHY_IS_LIBRESSL:
            assert lock_cb == b.ffi.NULL
            assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 0
        else:
            assert lock_cb != b.ffi.NULL
            assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 1
 def __init__(self, host: str, port: int):
     self.host = host
     self.port = port
     self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     binding = Binding()
     binding.init_static_locks()
     # Binding the DTLSv1 method to SSL context methods 1st entry
     # Required as this is UDP
     OpenSSL.SSL.Context._methods[0] = getattr(binding.lib,
                                               "DTLSv1_client_method")
     ctx = OpenSSL.SSL.Context(0)
     self.socket = OpenSSL.SSL.Connection(ctx, self.s)
     self.connect()
Exemple #12
0
    def test_conditional_removal(self):
        b = Binding()

        if b.lib.CRYPTOGRAPHY_OPENSSL_101_OR_GREATER:
            assert b.lib.CMAC_Init
        else:
            with pytest.raises(AttributeError):
                b.lib.CMAC_Init
Exemple #13
0
    def test_conditional_removal(self):
        b = Binding()

        if not b.lib.CRYPTOGRAPHY_IS_LIBRESSL:
            assert b.lib.TLS_ST_OK
        else:
            with pytest.raises(AttributeError):
                b.lib.TLS_ST_OK
Exemple #14
0
    def __init__(self):
        self._binding = Binding()
        self._ffi = self._binding.ffi
        self._lib = self._binding.lib

        self._binding.init_static_locks()

        # adds all ciphers/digests for EVP
        self._lib.OpenSSL_add_all_algorithms()
        # registers available SSL/TLS ciphers and digests
        self._lib.SSL_library_init()
        # loads error strings for libcrypto and libssl functions
        self._lib.SSL_load_error_strings()

        self._cipher_registry = {}
        self._register_default_ciphers()
        self.activate_osrandom_engine()
Exemple #15
0
    def test_conditional_removal(self):
        b = Binding()

        if b.lib.OPENSSL_VERSION_NUMBER >= 0x10001000:
            assert b.lib.CMAC_Init
        else:
            with pytest.raises(AttributeError):
                b.lib.CMAC_Init
    def test_conditional_removal(self):
        b = Binding()

        if (b.lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER
                and not b.lib.CRYPTOGRAPHY_IS_LIBRESSL):
            assert b.lib.TLS_ST_OK
        else:
            with pytest.raises(AttributeError):
                b.lib.TLS_ST_OK
Exemple #17
0
 def test_ssl_ctx_options(self):
     # Test that we're properly handling 32-bit unsigned on all platforms.
     b = Binding()
     assert b.lib.SSL_OP_ALL > 0
     ctx = b.lib.SSL_CTX_new(b.lib.TLSv1_method())
     ctx = b.ffi.gc(ctx, b.lib.SSL_CTX_free)
     resp = b.lib.SSL_CTX_set_options(ctx, b.lib.SSL_OP_ALL)
     assert resp == b.lib.SSL_OP_ALL
     assert b.lib.SSL_OP_ALL == b.lib.SSL_CTX_get_options(ctx)
Exemple #18
0
    def __init__(self, sleeper=None):
        ctx = SSL.Context(SSL.SSLv23_METHOD)
        # TLS 1.3 removes renegotiation support. Which is great for them, but
        # we still have to support versions before that, and that means we
        # need to test renegotiation support, which means we need to force this
        # to use a lower version where this test server can trigger
        # renegotiations. Of course TLS 1.3 support isn't released yet, but
        # I'm told that this will work once it is. (And once it is we can
        # remove the pragma: no cover too.) Alternatively, we could switch to
        # using TLSv1_2_METHOD.
        #
        # Discussion: https://github.com/pyca/pyopenssl/issues/624

        # This is the right way, but we can't use it until this PR is in a
        # released:
        #     https://github.com/pyca/pyopenssl/pull/861
        #
        # if hasattr(SSL, "OP_NO_TLSv1_3"):
        #     ctx.set_options(SSL.OP_NO_TLSv1_3)
        #
        # Fortunately pyopenssl uses cryptography under the hood, so we can be
        # confident that they're using the same version of openssl
        from cryptography.hazmat.bindings.openssl.binding import Binding

        b = Binding()
        if hasattr(b.lib, "SSL_OP_NO_TLSv1_3"):
            ctx.set_options(b.lib.SSL_OP_NO_TLSv1_3)

        # Unfortunately there's currently no way to say "use 1.3 or worse", we
        # can only disable specific versions. And if the two sides start
        # negotiating 1.4 at some point in the future, it *might* mean that
        # our tests silently stop working properly. So the next line is a
        # tripwire to remind us we need to revisit this stuff in 5 years or
        # whatever when the next TLS version is released:
        assert not hasattr(SSL, "OP_NO_TLSv1_4")
        TRIO_TEST_1_CERT.configure_cert(ctx)
        self._conn = SSL.Connection(ctx, None)
        self._conn.set_accept_state()
        self._lot = _core.ParkingLot()
        self._pending_cleartext = bytearray()

        self._send_all_conflict_detector = ConflictDetector(
            "simultaneous calls to PyOpenSSLEchoStream.send_all"
        )
        self._receive_some_conflict_detector = ConflictDetector(
            "simultaneous calls to PyOpenSSLEchoStream.receive_some"
        )

        if sleeper is None:

            async def no_op_sleeper(_):
                return

            self.sleeper = no_op_sleeper
        else:
            self.sleeper = sleeper
Exemple #19
0
 def test_ssl_ctx_options(self):
     # Test that we're properly handling 32-bit unsigned on all platforms.
     b = Binding()
     assert b.lib.SSL_OP_ALL > 0
     ctx = b.lib.SSL_CTX_new(b.lib.SSLv23_method())
     assert ctx != b.ffi.NULL
     ctx = b.ffi.gc(ctx, b.lib.SSL_CTX_free)
     current_options = b.lib.SSL_CTX_get_options(ctx)
     resp = b.lib.SSL_CTX_set_options(ctx, b.lib.SSL_OP_ALL)
     expected_options = current_options | b.lib.SSL_OP_ALL
     assert resp == expected_options
     assert b.lib.SSL_CTX_get_options(ctx) == expected_options
 def test_ssl_mode(self):
     # Test that we're properly handling 32-bit unsigned on all platforms.
     b = Binding()
     assert b.lib.SSL_OP_ALL > 0
     ctx = b.lib.SSL_CTX_new(b.lib.TLSv1_method())
     ctx = b.ffi.gc(ctx, b.lib.SSL_CTX_free)
     ssl = b.lib.SSL_new(ctx)
     ssl = b.ffi.gc(ssl, b.lib.SSL_free)
     current_options = b.lib.SSL_get_mode(ssl)
     resp = b.lib.SSL_set_mode(ssl, b.lib.SSL_OP_ALL)
     expected_options = current_options | b.lib.SSL_OP_ALL
     assert resp == expected_options
     assert b.lib.SSL_get_mode(ssl) == expected_options
    def test_actual_osrandom_bytes(self, monkeypatch):
        b = Binding()
        skip_if_libre_ssl(b.ffi.string(b.lib.OPENSSL_VERSION_TEXT))
        sample_data = (b"\x01\x02\x03\x04" * 4)
        length = len(sample_data)

        def notrandom(size):
            assert size == length
            return sample_data

        monkeypatch.setattr(os, "urandom", notrandom)
        buf = b.ffi.new("char[]", length)
        b.lib.RAND_bytes(buf, length)
        assert b.ffi.buffer(buf)[0:length] == sample_data
    def test_openssl_assert_error_on_stack(self):
        b = Binding()
        b.lib.ERR_put_error(b.lib.ERR_LIB_EVP, b.lib.EVP_F_EVP_ENCRYPTFINAL_EX,
                            b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH, b"",
                            -1)
        with pytest.raises(InternalError) as exc_info:
            _openssl_assert(b.lib, False)

        error = exc_info.value.err_code[0]
        assert error.code == 101183626
        assert error.lib == b.lib.ERR_LIB_EVP
        assert error.func == b.lib.EVP_F_EVP_ENCRYPTFINAL_EX
        assert error.reason == b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
        assert b"data not multiple of block length" in error.reason_text
Exemple #23
0
def _available_backends():
    global _available_backends_list

    if _available_backends_list is None:
        _available_backends_list = []

        if CommonCryptoBinding.is_available():
            from cryptography.hazmat.backends import commoncrypto
            _available_backends_list.append(commoncrypto.backend)

        if OpenSSLBinding.is_available():
            from cryptography.hazmat.backends import openssl
            _available_backends_list.append(openssl.backend)

    return _available_backends_list
Exemple #24
0
def _available_backends():
    global _available_backends_list

    if _available_backends_list is None:
        _available_backends_list = []

        if CommonCryptoBinding.is_available():
            from cryptography.hazmat.backends import commoncrypto
            _available_backends_list.append(commoncrypto.backend)

        if OpenSSLBinding.is_available():
            from cryptography.hazmat.backends import openssl
            _available_backends_list.append(openssl.backend)

    return _available_backends_list
Exemple #25
0
 def test_ssl_mode(self):
     # Test that we're properly handling 32-bit unsigned on all platforms.
     b = Binding()
     # SSL_OP_ALL is 0 on BoringSSL
     if not b.lib.CRYPTOGRAPHY_IS_BORINGSSL:
         assert b.lib.SSL_OP_ALL > 0
     ctx = b.lib.SSL_CTX_new(b.lib.SSLv23_method())
     assert ctx != b.ffi.NULL
     ctx = b.ffi.gc(ctx, b.lib.SSL_CTX_free)
     ssl = b.lib.SSL_new(ctx)
     ssl = b.ffi.gc(ssl, b.lib.SSL_free)
     current_options = b.lib.SSL_get_mode(ssl)
     resp = b.lib.SSL_set_mode(ssl, b.lib.SSL_OP_ALL)
     expected_options = current_options | b.lib.SSL_OP_ALL
     assert resp == expected_options
     assert b.lib.SSL_get_mode(ssl) == expected_options
Exemple #26
0
    def __init__(self):
        self._binding = Binding()
        self._ffi = self._binding.ffi
        self._lib = self._binding.lib

        self._binding.init_static_locks()

        # adds all ciphers/digests for EVP
        self._lib.OpenSSL_add_all_algorithms()
        # registers available SSL/TLS ciphers and digests
        self._lib.SSL_library_init()
        # loads error strings for libcrypto and libssl functions
        self._lib.SSL_load_error_strings()

        self._cipher_registry = {}
        self._register_default_ciphers()
    def test_conditional_removal(self):
        b = Binding()
        if b.lib.OPENSSL_VERSION_NUMBER >= 0x10000000:
            assert b.lib.X509_V_ERR_DIFFERENT_CRL_SCOPE
            assert b.lib.X509_V_ERR_CRL_PATH_VALIDATION_ERROR
        else:
            with pytest.raises(AttributeError):
                b.lib.X509_V_ERR_DIFFERENT_CRL_SCOPE

            with pytest.raises(AttributeError):
                b.lib.X509_V_ERR_CRL_PATH_VALIDATION_ERROR

        if b.lib.OPENSSL_VERSION_NUMBER >= 0x10001000:
            assert b.lib.CMAC_Init
        else:
            with pytest.raises(AttributeError):
                b.lib.CMAC_Init
Exemple #28
0
def _openssl_get_supported_curves():
    if hasattr(_openssl_get_supported_curves, '_curves'):
        return _openssl_get_supported_curves._curves

    # use cryptography's cffi bindings to get an array of curve names
    b = Binding()
    cn = b.lib.EC_get_builtin_curves(b.ffi.NULL, 0)
    cs = b.ffi.new('EC_builtin_curve[]', cn)
    b.lib.EC_get_builtin_curves(cs, cn)

    # store the result so we don't have to do all of this every time
    curves = {
        b.ffi.string(b.lib.OBJ_nid2sn(c.nid)).decode('utf-8')
        for c in cs
    }
    _openssl_get_supported_curves._curves = curves
    return curves
    def test_openssl_assert_error_on_stack(self):
        b = Binding()
        b.lib.ERR_put_error(b.lib.ERR_LIB_EVP, b.lib.EVP_F_EVP_ENCRYPTFINAL_EX,
                            b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH, b"",
                            -1)
        with pytest.raises(InternalError) as exc_info:
            _openssl_assert(b.lib, False)

        assert exc_info.value.err_code == [
            _OpenSSLErrorWithText(
                code=101183626,
                lib=b.lib.ERR_LIB_EVP,
                func=b.lib.EVP_F_EVP_ENCRYPTFINAL_EX,
                reason=b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
                reason_text=
                (b'error:0607F08A:digital envelope routines:EVP_EncryptFinal_'
                 b'ex:data not multiple of block length'))
        ]
    def test_openssl_assert_error_on_stack(self):
        b = Binding()
        b.lib.ERR_put_error(
            b.lib.ERR_LIB_EVP,
            b.lib.EVP_F_EVP_ENCRYPTFINAL_EX,
            b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
            b"",
            -1,
        )
        with pytest.raises(InternalError) as exc_info:
            _openssl_assert(b.lib, False)

        error = exc_info.value.err_code[0]
        # As of 3.0.0 OpenSSL no longer sets func codes (which we now also
        # ignore), so the combined code is a different value
        assert error.code in (101183626, 50331786)
        assert error.lib == b.lib.ERR_LIB_EVP
        assert error.reason == b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
        assert b"data not multiple of block length" in error.reason_text
Exemple #31
0
def _openssl_get_supported_curves():
    if hasattr(_openssl_get_supported_curves, '_curves'):
        return _openssl_get_supported_curves._curves

    # use cryptography's cffi bindings to get an array of curve names
    b = Binding()
    cn = b.lib.EC_get_builtin_curves(b.ffi.NULL, 0)
    cs = b.ffi.new('EC_builtin_curve[]', cn)
    b.lib.EC_get_builtin_curves(cs, cn)

    # store the result so we don't have to do all of this every time
    curves = {
        b.ffi.string(b.lib.OBJ_nid2sn(c.nid)).decode('utf-8')
        for c in cs
    }
    # Ed25519 and X25519 are always present in cryptography>=2.6
    # The python cryptography lib provides a different interface for these curves,
    # so they are handled differently in the ECDHPriv/Pub and EdDSAPriv/Pub classes
    curves |= {'X25519', 'ed25519'}
    _openssl_get_supported_curves._curves = curves
    return curves
def get_random_bytes(num_bytes):
    """
    Get random bytes

    Currently, Cryptography returns OS random bytes. If you want OpenSSL
    generated random bytes, you'll have to switch the RAND engine after
    initializing the OpenSSL backend
    Args:
        num_bytes (int): Number of random bytes to generate and return
    Returns:
        bytes: Random bytes
    """
    global _binding

    if _binding is None:
        _binding = Binding()

    buf = _binding.ffi.new("char[]", num_bytes)
    _binding.lib.RAND_bytes(buf, num_bytes)
    rand_bytes = _binding.ffi.buffer(buf, num_bytes)[:]
    return rand_bytes
Exemple #33
0
 def test_is_available(self):
     assert Binding.is_available() is True
Exemple #34
0
class Backend(object):
    """
    OpenSSL API binding interfaces.
    """
    name = "openssl"

    def __init__(self):
        self._binding = Binding()
        self._ffi = self._binding.ffi
        self._lib = self._binding.lib

        self._binding.init_static_locks()

        # adds all ciphers/digests for EVP
        self._lib.OpenSSL_add_all_algorithms()
        # registers available SSL/TLS ciphers and digests
        self._lib.SSL_library_init()
        # loads error strings for libcrypto and libssl functions
        self._lib.SSL_load_error_strings()

        self._cipher_registry = {}
        self._register_default_ciphers()
        self.activate_osrandom_engine()

    def activate_builtin_random(self):
        # Obtain a new structural reference.
        e = self._lib.ENGINE_get_default_RAND()
        if e != self._ffi.NULL:
            self._lib.ENGINE_unregister_RAND(e)
            # Reset the RNG to use the new engine.
            self._lib.RAND_cleanup()
            # decrement the structural reference from get_default_RAND
            res = self._lib.ENGINE_finish(e)
            assert res == 1

    def activate_osrandom_engine(self):
        # Unregister and free the current engine.
        self.activate_builtin_random()
        # Fetches an engine by id and returns it. This creates a structural
        # reference.
        e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id)
        assert e != self._ffi.NULL
        # Initialize the engine for use. This adds a functional reference.
        res = self._lib.ENGINE_init(e)
        assert res == 1
        # Set the engine as the default RAND provider.
        res = self._lib.ENGINE_set_default_RAND(e)
        assert res == 1
        # Decrement the structural ref incremented by ENGINE_by_id.
        res = self._lib.ENGINE_free(e)
        assert res == 1
        # Decrement the functional ref incremented by ENGINE_init.
        res = self._lib.ENGINE_finish(e)
        assert res == 1
        # Reset the RNG to use the new engine.
        self._lib.RAND_cleanup()

    def openssl_version_text(self):
        """
        Friendly string name of the loaded OpenSSL library. This is not
        necessarily the same version as it was compiled against.

        Example: OpenSSL 1.0.1e 11 Feb 2013
        """
        return self._ffi.string(
            self._lib.SSLeay_version(self._lib.SSLEAY_VERSION)
        ).decode("ascii")

    def create_hmac_ctx(self, key, algorithm):
        return _HMACContext(self, key, algorithm)

    def hash_supported(self, algorithm):
        digest = self._lib.EVP_get_digestbyname(algorithm.name.encode("ascii"))
        return digest != self._ffi.NULL

    def hmac_supported(self, algorithm):
        return self.hash_supported(algorithm)

    def create_hash_ctx(self, algorithm):
        return _HashContext(self, algorithm)

    def cipher_supported(self, cipher, mode):
        if self._evp_cipher_supported(cipher, mode):
            return True
        elif isinstance(mode, CTR) and isinstance(cipher, AES):
            return True
        else:
            return False

    def _evp_cipher_supported(self, cipher, mode):
        try:
            adapter = self._cipher_registry[type(cipher), type(mode)]
        except KeyError:
            return False
        evp_cipher = adapter(self, cipher, mode)
        return self._ffi.NULL != evp_cipher

    def register_cipher_adapter(self, cipher_cls, mode_cls, adapter):
        if (cipher_cls, mode_cls) in self._cipher_registry:
            raise ValueError("Duplicate registration for: {0} {1}.".format(
                cipher_cls, mode_cls)
            )
        self._cipher_registry[cipher_cls, mode_cls] = adapter

    def _register_default_ciphers(self):
        for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8]:
            self.register_cipher_adapter(
                AES,
                mode_cls,
                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
            )
        for mode_cls in [CBC, CTR, ECB, OFB, CFB]:
            self.register_cipher_adapter(
                Camellia,
                mode_cls,
                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
            )
        for mode_cls in [CBC, CFB, CFB8, OFB]:
            self.register_cipher_adapter(
                TripleDES,
                mode_cls,
                GetCipherByName("des-ede3-{mode.name}")
            )
        self.register_cipher_adapter(
            TripleDES,
            ECB,
            GetCipherByName("des-ede3")
        )
        for mode_cls in [CBC, CFB, OFB, ECB]:
            self.register_cipher_adapter(
                Blowfish,
                mode_cls,
                GetCipherByName("bf-{mode.name}")
            )
        for mode_cls in [CBC, CFB, OFB, ECB]:
            self.register_cipher_adapter(
                SEED,
                mode_cls,
                GetCipherByName("seed-{mode.name}")
            )
        for cipher_cls, mode_cls in itertools.product(
            [CAST5, IDEA],
            [CBC, OFB, CFB, ECB],
        ):
            self.register_cipher_adapter(
                cipher_cls,
                mode_cls,
                GetCipherByName("{cipher.name}-{mode.name}")
            )
        self.register_cipher_adapter(
            ARC4,
            type(None),
            GetCipherByName("rc4")
        )
        self.register_cipher_adapter(
            AES,
            GCM,
            GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
        )

    def create_symmetric_encryption_ctx(self, cipher, mode):
        if (isinstance(mode, CTR) and isinstance(cipher, AES)
                and not self._evp_cipher_supported(cipher, mode)):
            # This is needed to provide support for AES CTR mode in OpenSSL
            # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5
            # extended life ends 2020).
            return _AESCTRCipherContext(self, cipher, mode)
        else:
            return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)

    def create_symmetric_decryption_ctx(self, cipher, mode):
        if (isinstance(mode, CTR) and isinstance(cipher, AES)
                and not self._evp_cipher_supported(cipher, mode)):
            # This is needed to provide support for AES CTR mode in OpenSSL
            # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5
            # extended life ends 2020).
            return _AESCTRCipherContext(self, cipher, mode)
        else:
            return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)

    def pbkdf2_hmac_supported(self, algorithm):
        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
            return self.hmac_supported(algorithm)
        else:
            # OpenSSL < 1.0.0 has an explicit PBKDF2-HMAC-SHA1 function,
            # so if the PBKDF2_HMAC function is missing we only support
            # SHA1 via PBKDF2_HMAC_SHA1.
            return isinstance(algorithm, hashes.SHA1)

    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
                           key_material):
        buf = self._ffi.new("char[]", length)
        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
            evp_md = self._lib.EVP_get_digestbyname(
                algorithm.name.encode("ascii"))
            assert evp_md != self._ffi.NULL
            res = self._lib.PKCS5_PBKDF2_HMAC(
                key_material,
                len(key_material),
                salt,
                len(salt),
                iterations,
                evp_md,
                length,
                buf
            )
            assert res == 1
        else:
            if not isinstance(algorithm, hashes.SHA1):
                raise UnsupportedAlgorithm(
                    "This version of OpenSSL only supports PBKDF2HMAC with "
                    "SHA1.",
                    _Reasons.UNSUPPORTED_HASH
                )
            res = self._lib.PKCS5_PBKDF2_HMAC_SHA1(
                key_material,
                len(key_material),
                salt,
                len(salt),
                iterations,
                length,
                buf
            )
            assert res == 1

        return self._ffi.buffer(buf)[:]

    def _err_string(self, code):
        err_buf = self._ffi.new("char[]", 256)
        self._lib.ERR_error_string_n(code, err_buf, 256)
        return self._ffi.string(err_buf, 256)[:]

    def _consume_errors(self):
        errors = []
        while True:
            code = self._lib.ERR_get_error()
            if code == 0:
                break

            lib = self._lib.ERR_GET_LIB(code)
            func = self._lib.ERR_GET_FUNC(code)
            reason = self._lib.ERR_GET_REASON(code)

            errors.append(_OpenSSLError(code, lib, func, reason))
        return errors

    def _unknown_error(self, error):
        return InternalError(
            "Unknown error code {0} from OpenSSL, "
            "you should probably file a bug. {1}.".format(
                error.code, self._err_string(error.code)
            )
        )

    def _bn_to_int(self, bn):
        if six.PY3:
            # Python 3 has constant time from_bytes, so use that.

            bn_num_bytes = (self._lib.BN_num_bits(bn) + 7) // 8
            bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes)
            bin_len = self._lib.BN_bn2bin(bn, bin_ptr)
            assert bin_len > 0
            assert bin_ptr != self._ffi.NULL
            return int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big")

        else:
            # Under Python 2 the best we can do is hex()

            hex_cdata = self._lib.BN_bn2hex(bn)
            assert hex_cdata != self._ffi.NULL
            hex_str = self._ffi.string(hex_cdata)
            self._lib.OPENSSL_free(hex_cdata)
            return int(hex_str, 16)

    def _int_to_bn(self, num, bn=None):
        """
        Converts a python integer to a BIGNUM. The returned BIGNUM will not
        be garbage collected (to support adding them to structs that take
        ownership of the object). Be sure to register it for GC if it will
        be discarded after use.
        """

        if bn is None:
            bn = self._ffi.NULL

        if six.PY3:
            # Python 3 has constant time to_bytes, so use that.

            binary = num.to_bytes(int(num.bit_length() / 8.0 + 1), "big")
            bn_ptr = self._lib.BN_bin2bn(binary, len(binary), bn)
            assert bn_ptr != self._ffi.NULL
            return bn_ptr

        else:
            # Under Python 2 the best we can do is hex()

            hex_num = hex(num).rstrip("L").lstrip("0x").encode("ascii") or b"0"
            bn_ptr = self._ffi.new("BIGNUM **")
            bn_ptr[0] = bn
            res = self._lib.BN_hex2bn(bn_ptr, hex_num)
            assert res != 0
            assert bn_ptr[0] != self._ffi.NULL
            return bn_ptr[0]

    def generate_rsa_private_key(self, public_exponent, key_size):
        rsa._verify_rsa_parameters(public_exponent, key_size)

        rsa_cdata = self._lib.RSA_new()
        assert rsa_cdata != self._ffi.NULL
        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)

        bn = self._int_to_bn(public_exponent)
        bn = self._ffi.gc(bn, self._lib.BN_free)

        res = self._lib.RSA_generate_key_ex(
            rsa_cdata, key_size, bn, self._ffi.NULL
        )
        assert res == 1

        return _RSAPrivateKey(self, rsa_cdata)

    def generate_rsa_parameters_supported(self, public_exponent, key_size):
        return (public_exponent >= 3 and public_exponent & 1 != 0 and
                key_size >= 512)

    def load_rsa_private_numbers(self, numbers):
        rsa._check_private_key_components(
            numbers.p,
            numbers.q,
            numbers.d,
            numbers.dmp1,
            numbers.dmq1,
            numbers.iqmp,
            numbers.public_numbers.e,
            numbers.public_numbers.n
        )
        rsa_cdata = self._lib.RSA_new()
        assert rsa_cdata != self._ffi.NULL
        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
        rsa_cdata.p = self._int_to_bn(numbers.p)
        rsa_cdata.q = self._int_to_bn(numbers.q)
        rsa_cdata.d = self._int_to_bn(numbers.d)
        rsa_cdata.dmp1 = self._int_to_bn(numbers.dmp1)
        rsa_cdata.dmq1 = self._int_to_bn(numbers.dmq1)
        rsa_cdata.iqmp = self._int_to_bn(numbers.iqmp)
        rsa_cdata.e = self._int_to_bn(numbers.public_numbers.e)
        rsa_cdata.n = self._int_to_bn(numbers.public_numbers.n)
        res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL)
        assert res == 1

        return _RSAPrivateKey(self, rsa_cdata)

    def load_rsa_public_numbers(self, numbers):
        rsa._check_public_key_components(numbers.e, numbers.n)
        rsa_cdata = self._lib.RSA_new()
        assert rsa_cdata != self._ffi.NULL
        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
        rsa_cdata.e = self._int_to_bn(numbers.e)
        rsa_cdata.n = self._int_to_bn(numbers.n)
        res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL)
        assert res == 1

        return _RSAPublicKey(self, rsa_cdata)

    def _bytes_to_bio(self, data):
        """
        Return a _MemoryBIO namedtuple of (BIO, char*).

        The char* is the storage for the BIO and it must stay alive until the
        BIO is finished with.
        """
        data_char_p = self._ffi.new("char[]", data)
        bio = self._lib.BIO_new_mem_buf(
            data_char_p, len(data)
        )
        assert bio != self._ffi.NULL

        return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_char_p)

    def _create_mem_bio(self):
        """
        Creates an empty memory BIO.
        """
        bio_method = self._lib.BIO_s_mem()
        assert bio_method != self._ffi.NULL
        bio = self._lib.BIO_new(bio_method)
        assert bio != self._ffi.NULL
        bio = self._ffi.gc(bio, self._lib.BIO_free)
        return bio

    def _read_mem_bio(self, bio):
        """
        Reads a memory BIO. This only works on memory BIOs.
        """
        buf = self._ffi.new("char **")
        buf_len = self._lib.BIO_get_mem_data(bio, buf)
        assert buf_len > 0
        assert buf[0] != self._ffi.NULL
        bio_data = self._ffi.buffer(buf[0], buf_len)[:]
        return bio_data

    def _evp_pkey_to_private_key(self, evp_pkey):
        """
        Return the appropriate type of PrivateKey given an evp_pkey cdata
        pointer.
        """

        type = evp_pkey.type

        if type == self._lib.EVP_PKEY_RSA:
            rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
            assert rsa_cdata != self._ffi.NULL
            rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
            return _RSAPrivateKey(self, rsa_cdata)
        elif type == self._lib.EVP_PKEY_DSA:
            dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey)
            assert dsa_cdata != self._ffi.NULL
            dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)
            return _DSAPrivateKey(self, dsa_cdata)
        elif (self._lib.Cryptography_HAS_EC == 1 and
              type == self._lib.EVP_PKEY_EC):
            ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey)
            assert ec_cdata != self._ffi.NULL
            ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
            return _EllipticCurvePrivateKey(self, ec_cdata)
        else:
            raise UnsupportedAlgorithm("Unsupported key type.")

    def _evp_pkey_to_public_key(self, evp_pkey):
        """
        Return the appropriate type of PublicKey given an evp_pkey cdata
        pointer.
        """

        type = evp_pkey.type

        if type == self._lib.EVP_PKEY_RSA:
            rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
            assert rsa_cdata != self._ffi.NULL
            rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
            return _RSAPublicKey(self, rsa_cdata)
        elif type == self._lib.EVP_PKEY_DSA:
            dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey)
            assert dsa_cdata != self._ffi.NULL
            dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)
            return _DSAPublicKey(self, dsa_cdata)
        elif (self._lib.Cryptography_HAS_EC == 1 and
              type == self._lib.EVP_PKEY_EC):
            ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey)
            assert ec_cdata != self._ffi.NULL
            ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
            return _EllipticCurvePublicKey(self, ec_cdata)
        else:
            raise UnsupportedAlgorithm("Unsupported key type.")

    def _pem_password_cb(self, password):
        """
        Generate a pem_password_cb function pointer that copied the password to
        OpenSSL as required and returns the number of bytes copied.

        typedef int pem_password_cb(char *buf, int size,
                                    int rwflag, void *userdata);

        Useful for decrypting PKCS8 files and so on.

        Returns a tuple of (cdata function pointer, callback function).
        """

        def pem_password_cb(buf, size, writing, userdata):
            pem_password_cb.called += 1

            if not password:
                pem_password_cb.exception = TypeError(
                    "Password was not given but private key is encrypted."
                )
                return 0
            elif len(password) < size:
                pw_buf = self._ffi.buffer(buf, size)
                pw_buf[:len(password)] = password
                return len(password)
            else:
                pem_password_cb.exception = ValueError(
                    "Passwords longer than {0} bytes are not supported "
                    "by this backend.".format(size - 1)
                )
                return 0

        pem_password_cb.called = 0
        pem_password_cb.exception = None

        return (
            self._ffi.callback("int (char *, int, int, void *)",
                               pem_password_cb),
            pem_password_cb
        )

    def _mgf1_hash_supported(self, algorithm):
        if self._lib.Cryptography_HAS_MGF1_MD:
            return self.hash_supported(algorithm)
        else:
            return isinstance(algorithm, hashes.SHA1)

    def rsa_padding_supported(self, padding):
        if isinstance(padding, PKCS1v15):
            return True
        elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1):
            return self._mgf1_hash_supported(padding._mgf._algorithm)
        elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
            return isinstance(padding._mgf._algorithm, hashes.SHA1)
        else:
            return False

    def generate_dsa_parameters(self, key_size):
        if key_size not in (1024, 2048, 3072):
            raise ValueError(
                "Key size must be 1024 or 2048 or 3072 bits.")

        if (self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f and
                key_size > 1024):
            raise ValueError(
                "Key size must be 1024 because OpenSSL < 1.0.0 doesn't "
                "support larger key sizes.")

        ctx = self._lib.DSA_new()
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.DSA_free)

        res = self._lib.DSA_generate_parameters_ex(
            ctx, key_size, self._ffi.NULL, 0,
            self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
        )

        assert res == 1

        return _DSAParameters(self, ctx)

    def generate_dsa_private_key(self, parameters):
        ctx = self._lib.DSA_new()
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.DSA_free)
        ctx.p = self._lib.BN_dup(parameters._dsa_cdata.p)
        ctx.q = self._lib.BN_dup(parameters._dsa_cdata.q)
        ctx.g = self._lib.BN_dup(parameters._dsa_cdata.g)

        self._lib.DSA_generate_key(ctx)

        return _DSAPrivateKey(self, ctx)

    def generate_dsa_private_key_and_parameters(self, key_size):
        parameters = self.generate_dsa_parameters(key_size)
        return self.generate_dsa_private_key(parameters)

    def load_dsa_private_numbers(self, numbers):
        dsa._check_dsa_private_numbers(numbers)
        parameter_numbers = numbers.public_numbers.parameter_numbers

        dsa_cdata = self._lib.DSA_new()
        assert dsa_cdata != self._ffi.NULL
        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)

        dsa_cdata.p = self._int_to_bn(parameter_numbers.p)
        dsa_cdata.q = self._int_to_bn(parameter_numbers.q)
        dsa_cdata.g = self._int_to_bn(parameter_numbers.g)
        dsa_cdata.pub_key = self._int_to_bn(numbers.public_numbers.y)
        dsa_cdata.priv_key = self._int_to_bn(numbers.x)

        return _DSAPrivateKey(self, dsa_cdata)

    def load_dsa_public_numbers(self, numbers):
        dsa._check_dsa_parameters(numbers.parameter_numbers)
        dsa_cdata = self._lib.DSA_new()
        assert dsa_cdata != self._ffi.NULL
        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)

        dsa_cdata.p = self._int_to_bn(numbers.parameter_numbers.p)
        dsa_cdata.q = self._int_to_bn(numbers.parameter_numbers.q)
        dsa_cdata.g = self._int_to_bn(numbers.parameter_numbers.g)
        dsa_cdata.pub_key = self._int_to_bn(numbers.y)

        return _DSAPublicKey(self, dsa_cdata)

    def load_dsa_parameter_numbers(self, numbers):
        dsa._check_dsa_parameters(numbers)
        dsa_cdata = self._lib.DSA_new()
        assert dsa_cdata != self._ffi.NULL
        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)

        dsa_cdata.p = self._int_to_bn(numbers.p)
        dsa_cdata.q = self._int_to_bn(numbers.q)
        dsa_cdata.g = self._int_to_bn(numbers.g)

        return _DSAParameters(self, dsa_cdata)

    def dsa_hash_supported(self, algorithm):
        if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f:
            return isinstance(algorithm, hashes.SHA1)
        else:
            return self.hash_supported(algorithm)

    def dsa_parameters_supported(self, p, q, g):
        if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f:
            return (utils.bit_length(p) <= 1024 and utils.bit_length(q) <= 160)
        else:
            return True

    def cmac_algorithm_supported(self, algorithm):
        return (
            self._lib.Cryptography_HAS_CMAC == 1
            and self.cipher_supported(algorithm, CBC(
                b"\x00" * algorithm.block_size))
        )

    def create_cmac_ctx(self, algorithm):
        return _CMACContext(self, algorithm)

    def load_pem_private_key(self, data, password):
        return self._load_key(
            self._lib.PEM_read_bio_PrivateKey,
            self._evp_pkey_to_private_key,
            data,
            password,
        )

    def load_pem_public_key(self, data):
        return self._load_key(
            self._lib.PEM_read_bio_PUBKEY,
            self._evp_pkey_to_public_key,
            data,
            None,
        )

    def load_pem_x509_certificate(self, data):
        mem_bio = self._bytes_to_bio(data)
        x509 = self._lib.PEM_read_bio_X509(
            mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
        )
        if x509 == self._ffi.NULL:
            self._consume_errors()
            raise ValueError("Unable to load certificate")

        x509 = self._ffi.gc(x509, self._lib.X509_free)
        return _Certificate(self, x509)

    def load_der_x509_certificate(self, data):
        mem_bio = self._bytes_to_bio(data)
        x509 = self._lib.d2i_X509_bio(mem_bio.bio, self._ffi.NULL)
        if x509 == self._ffi.NULL:
            self._consume_errors()
            raise ValueError("Unable to load certificate")

        x509 = self._ffi.gc(x509, self._lib.X509_free)
        return _Certificate(self, x509)

    def load_traditional_openssl_pem_private_key(self, data, password):
        warnings.warn(
            "load_traditional_openssl_pem_private_key is deprecated and will "
            "be removed in a future version, use load_pem_private_key "
            "instead.",
            utils.DeprecatedIn06,
            stacklevel=2
        )
        return self.load_pem_private_key(data, password)

    def load_pkcs8_pem_private_key(self, data, password):
        warnings.warn(
            "load_pkcs8_pem_private_key is deprecated and will be removed in a"
            " future version, use load_pem_private_key instead.",
            utils.DeprecatedIn06,
            stacklevel=2
        )
        return self.load_pem_private_key(data, password)

    def _load_key(self, openssl_read_func, convert_func, data, password):
        mem_bio = self._bytes_to_bio(data)

        password_callback, password_func = self._pem_password_cb(password)

        evp_pkey = openssl_read_func(
            mem_bio.bio,
            self._ffi.NULL,
            password_callback,
            self._ffi.NULL
        )

        if evp_pkey == self._ffi.NULL:
            if password_func.exception is not None:
                errors = self._consume_errors()
                assert errors
                raise password_func.exception
            else:
                self._handle_key_loading_error()

        evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)

        if password is not None and password_func.called == 0:
            raise TypeError(
                "Password was given but private key is not encrypted.")

        assert (
            (password is not None and password_func.called == 1) or
            password is None
        )

        return convert_func(evp_pkey)

    def _handle_key_loading_error(self):
        errors = self._consume_errors()

        if not errors:
            raise ValueError("Could not unserialize key data.")

        elif errors[0][1:] in (
            (
                self._lib.ERR_LIB_EVP,
                self._lib.EVP_F_EVP_DECRYPTFINAL_EX,
                self._lib.EVP_R_BAD_DECRYPT
            ),
            (
                self._lib.ERR_LIB_PKCS12,
                self._lib.PKCS12_F_PKCS12_PBE_CRYPT,
                self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR,
            )
        ):
            raise ValueError("Bad decrypt. Incorrect password?")

        elif errors[0][1:] in (
            (
                self._lib.ERR_LIB_PEM,
                self._lib.PEM_F_PEM_GET_EVP_CIPHER_INFO,
                self._lib.PEM_R_UNSUPPORTED_ENCRYPTION
            ),

            (
                self._lib.ERR_LIB_EVP,
                self._lib.EVP_F_EVP_PBE_CIPHERINIT,
                self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM
            )
        ):
            raise UnsupportedAlgorithm(
                "PEM data is encrypted with an unsupported cipher",
                _Reasons.UNSUPPORTED_CIPHER
            )

        elif any(
            error[1:] == (
                self._lib.ERR_LIB_EVP,
                self._lib.EVP_F_EVP_PKCS82PKEY,
                self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM
            )
            for error in errors
        ):
            raise UnsupportedAlgorithm(
                "Unsupported public key algorithm.",
                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
            )

        else:
            assert errors[0][1] in (
                self._lib.ERR_LIB_EVP,
                self._lib.ERR_LIB_PEM,
                self._lib.ERR_LIB_ASN1,
            )
            raise ValueError("Could not unserialize key data.")

    def elliptic_curve_supported(self, curve):
        if self._lib.Cryptography_HAS_EC != 1:
            return False

        try:
            curve_nid = self._elliptic_curve_to_nid(curve)
        except UnsupportedAlgorithm:
            curve_nid = self._lib.NID_undef

        ctx = self._lib.EC_GROUP_new_by_curve_name(curve_nid)

        if ctx == self._ffi.NULL:
            errors = self._consume_errors()
            assert (
                curve_nid == self._lib.NID_undef or
                errors[0][1:] == (
                    self._lib.ERR_LIB_EC,
                    self._lib.EC_F_EC_GROUP_NEW_BY_CURVE_NAME,
                    self._lib.EC_R_UNKNOWN_GROUP
                )
            )
            return False
        else:
            assert curve_nid != self._lib.NID_undef
            self._lib.EC_GROUP_free(ctx)
            return True

    def elliptic_curve_signature_algorithm_supported(
        self, signature_algorithm, curve
    ):
        if self._lib.Cryptography_HAS_EC != 1:
            return False

        # We only support ECDSA right now.
        if not isinstance(signature_algorithm, ec.ECDSA):
            return False

        # Before 0.9.8m OpenSSL can't cope with digests longer than the curve.
        if (
            self._lib.OPENSSL_VERSION_NUMBER < 0x009080df and
            curve.key_size < signature_algorithm.algorithm.digest_size * 8
        ):
            return False

        return self.elliptic_curve_supported(curve)

    def generate_elliptic_curve_private_key(self, curve):
        """
        Generate a new private key on the named curve.
        """

        if self.elliptic_curve_supported(curve):
            curve_nid = self._elliptic_curve_to_nid(curve)

            ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid)
            assert ec_cdata != self._ffi.NULL
            ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)

            res = self._lib.EC_KEY_generate_key(ec_cdata)
            assert res == 1

            res = self._lib.EC_KEY_check_key(ec_cdata)
            assert res == 1

            return _EllipticCurvePrivateKey(self, ec_cdata)
        else:
            raise UnsupportedAlgorithm(
                "Backend object does not support {0}.".format(curve.name),
                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
            )

    def elliptic_curve_private_key_from_numbers(self, numbers):
        warnings.warn(
            "elliptic_curve_private_key_from_numbers is deprecated and will "
            "be removed in a future version.",
            utils.DeprecatedIn06,
            stacklevel=2
        )
        return self.load_elliptic_curve_private_numbers(numbers)

    def load_elliptic_curve_private_numbers(self, numbers):
        public = numbers.public_numbers

        curve_nid = self._elliptic_curve_to_nid(public.curve)

        ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid)
        assert ec_cdata != self._ffi.NULL
        ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)

        ec_cdata = self._ec_key_set_public_key_affine_coordinates(
            ec_cdata, public.x, public.y)

        res = self._lib.EC_KEY_set_private_key(
            ec_cdata, self._int_to_bn(numbers.private_value))
        assert res == 1

        return _EllipticCurvePrivateKey(self, ec_cdata)

    def elliptic_curve_public_key_from_numbers(self, numbers):
        warnings.warn(
            "elliptic_curve_public_key_from_numbers is deprecated and will be "
            "removed in a future version.",
            utils.DeprecatedIn06,
            stacklevel=2
        )
        return self.load_elliptic_curve_public_numbers(numbers)

    def load_elliptic_curve_public_numbers(self, numbers):
        curve_nid = self._elliptic_curve_to_nid(numbers.curve)

        ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid)
        assert ec_cdata != self._ffi.NULL
        ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)

        ec_cdata = self._ec_key_set_public_key_affine_coordinates(
            ec_cdata, numbers.x, numbers.y)

        return _EllipticCurvePublicKey(self, ec_cdata)

    def _elliptic_curve_to_nid(self, curve):
        """
        Get the NID for a curve name.
        """

        curve_aliases = {
            "secp192r1": "prime192v1",
            "secp256r1": "prime256v1"
        }

        curve_name = curve_aliases.get(curve.name, curve.name)

        curve_nid = self._lib.OBJ_sn2nid(curve_name.encode())
        if curve_nid == self._lib.NID_undef:
            raise UnsupportedAlgorithm(
                "{0} is not a supported elliptic curve".format(curve.name),
                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
            )
        return curve_nid

    @contextmanager
    def _tmp_bn_ctx(self):
        bn_ctx = self._lib.BN_CTX_new()
        assert bn_ctx != self._ffi.NULL
        bn_ctx = self._ffi.gc(bn_ctx, self._lib.BN_CTX_free)
        self._lib.BN_CTX_start(bn_ctx)
        try:
            yield bn_ctx
        finally:
            self._lib.BN_CTX_end(bn_ctx)

    def _ec_key_determine_group_get_set_funcs(self, ctx):
        """
        Given an EC_KEY determine the group and what methods are required to
        get/set point coordinates.
        """
        assert ctx != self._ffi.NULL

        nid_two_field = self._lib.OBJ_sn2nid(b"characteristic-two-field")
        assert nid_two_field != self._lib.NID_undef

        group = self._lib.EC_KEY_get0_group(ctx)
        assert group != self._ffi.NULL

        method = self._lib.EC_GROUP_method_of(group)
        assert method != self._ffi.NULL

        nid = self._lib.EC_METHOD_get_field_type(method)
        assert nid != self._lib.NID_undef

        if nid == nid_two_field and self._lib.Cryptography_HAS_EC2M:
            set_func = self._lib.EC_POINT_set_affine_coordinates_GF2m
            get_func = self._lib.EC_POINT_get_affine_coordinates_GF2m
        else:
            set_func = self._lib.EC_POINT_set_affine_coordinates_GFp
            get_func = self._lib.EC_POINT_get_affine_coordinates_GFp

        assert set_func and get_func

        return set_func, get_func, group

    def _ec_key_set_public_key_affine_coordinates(self, ctx, x, y):
        """
        This is a port of EC_KEY_set_public_key_affine_coordinates that was
        added in 1.0.1.

        Sets the public key point in the EC_KEY context to the affine x and y
        values.
        """

        if x < 0 or y < 0:
            raise ValueError(
                "Invalid EC key. Both x and y must be non-negative."
            )

        bn_x = self._int_to_bn(x)
        bn_y = self._int_to_bn(y)

        set_func, get_func, group = (
            self._ec_key_determine_group_get_set_funcs(ctx)
        )

        point = self._lib.EC_POINT_new(group)
        assert point != self._ffi.NULL
        point = self._ffi.gc(point, self._lib.EC_POINT_free)

        with self._tmp_bn_ctx() as bn_ctx:
            check_x = self._lib.BN_CTX_get(bn_ctx)
            check_y = self._lib.BN_CTX_get(bn_ctx)

            res = set_func(group, point, bn_x, bn_y, bn_ctx)
            assert res == 1

            res = get_func(group, point, check_x, check_y, bn_ctx)
            assert res == 1

            assert self._lib.BN_cmp(bn_x, check_x) == 0
            assert self._lib.BN_cmp(bn_y, check_y) == 0

        res = self._lib.EC_KEY_set_public_key(ctx, point)
        assert res == 1

        res = self._lib.EC_KEY_check_key(ctx)
        if res != 1:
            self._consume_errors()
            raise ValueError("Invalid EC key.")

        return ctx
Exemple #35
0
 def test_add_engine_more_than_once(self):
     b = Binding()
     b._register_osrandom_engine()
     assert b.lib.ERR_get_error() == 0
Exemple #36
0
 def test_binding_loads(self):
     binding = Binding()
     assert binding
     assert binding.lib
     assert binding.ffi
Exemple #37
0
 def test_add_engine_more_than_once(self):
     b = Binding()
     b._register_osrandom_engine()
     assert b.lib.ERR_get_error() == 0
Exemple #38
0
class Backend(object):
    """
    OpenSSL API binding interfaces.
    """
    name = "openssl"

    def __init__(self):
        self._binding = Binding()
        self._ffi = self._binding.ffi
        self._lib = self._binding.lib

        self._binding.init_static_locks()

        # adds all ciphers/digests for EVP
        self._lib.OpenSSL_add_all_algorithms()
        # registers available SSL/TLS ciphers and digests
        self._lib.SSL_library_init()
        # loads error strings for libcrypto and libssl functions
        self._lib.SSL_load_error_strings()

        self._cipher_registry = {}
        self._register_default_ciphers()
        self.activate_osrandom_engine()

    def activate_builtin_random(self):
        # Obtain a new structural reference.
        e = self._lib.ENGINE_get_default_RAND()
        if e != self._ffi.NULL:
            self._lib.ENGINE_unregister_RAND(e)
            # Reset the RNG to use the new engine.
            self._lib.RAND_cleanup()
            # decrement the structural reference from get_default_RAND
            res = self._lib.ENGINE_finish(e)
            assert res == 1

    def activate_osrandom_engine(self):
        # Unregister and free the current engine.
        self.activate_builtin_random()
        # Fetches an engine by id and returns it. This creates a structural
        # reference.
        e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id)
        assert e != self._ffi.NULL
        # Initialize the engine for use. This adds a functional reference.
        res = self._lib.ENGINE_init(e)
        assert res == 1
        # Set the engine as the default RAND provider.
        res = self._lib.ENGINE_set_default_RAND(e)
        assert res == 1
        # Decrement the structural ref incremented by ENGINE_by_id.
        res = self._lib.ENGINE_free(e)
        assert res == 1
        # Decrement the functional ref incremented by ENGINE_init.
        res = self._lib.ENGINE_finish(e)
        assert res == 1
        # Reset the RNG to use the new engine.
        self._lib.RAND_cleanup()

    def openssl_version_text(self):
        """
        Friendly string name of linked OpenSSL.

        Example: OpenSSL 1.0.1e 11 Feb 2013
        """
        return self._ffi.string(self._lib.OPENSSL_VERSION_TEXT).decode("ascii")

    def create_hmac_ctx(self, key, algorithm):
        return _HMACContext(self, key, algorithm)

    def hash_supported(self, algorithm):
        digest = self._lib.EVP_get_digestbyname(algorithm.name.encode("ascii"))
        return digest != self._ffi.NULL

    def hmac_supported(self, algorithm):
        return self.hash_supported(algorithm)

    def create_hash_ctx(self, algorithm):
        return _HashContext(self, algorithm)

    def cipher_supported(self, cipher, mode):
        try:
            adapter = self._cipher_registry[type(cipher), type(mode)]
        except KeyError:
            return False
        evp_cipher = adapter(self, cipher, mode)
        return self._ffi.NULL != evp_cipher

    def register_cipher_adapter(self, cipher_cls, mode_cls, adapter):
        if (cipher_cls, mode_cls) in self._cipher_registry:
            raise ValueError("Duplicate registration for: {0} {1}".format(
                cipher_cls, mode_cls)
            )
        self._cipher_registry[cipher_cls, mode_cls] = adapter

    def _register_default_ciphers(self):
        for cipher_cls, mode_cls in itertools.product(
            [AES, Camellia],
            [CBC, CTR, ECB, OFB, CFB],
        ):
            self.register_cipher_adapter(
                cipher_cls,
                mode_cls,
                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
            )
        for mode_cls in [CBC, CFB, OFB]:
            self.register_cipher_adapter(
                TripleDES,
                mode_cls,
                GetCipherByName("des-ede3-{mode.name}")
            )
        for mode_cls in [CBC, CFB, OFB, ECB]:
            self.register_cipher_adapter(
                Blowfish,
                mode_cls,
                GetCipherByName("bf-{mode.name}")
            )
        self.register_cipher_adapter(
            ARC4,
            type(None),
            GetCipherByName("rc4")
        )
        self.register_cipher_adapter(
            AES,
            GCM,
            GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
        )

    def create_symmetric_encryption_ctx(self, cipher, mode):
        return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)

    def create_symmetric_decryption_ctx(self, cipher, mode):
        return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)

    def pbkdf2_hmac_supported(self, algorithm):
        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
            return self.hmac_supported(algorithm)
        else:
            # OpenSSL < 1.0.0 has an explicit PBKDF2-HMAC-SHA1 function,
            # so if the PBKDF2_HMAC function is missing we only support
            # SHA1 via PBKDF2_HMAC_SHA1.
            return isinstance(algorithm, hashes.SHA1)

    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
                           key_material):
        buf = self._ffi.new("char[]", length)
        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
            evp_md = self._lib.EVP_get_digestbyname(
                algorithm.name.encode("ascii"))
            assert evp_md != self._ffi.NULL
            res = self._lib.PKCS5_PBKDF2_HMAC(
                key_material,
                len(key_material),
                salt,
                len(salt),
                iterations,
                evp_md,
                length,
                buf
            )
            assert res == 1
        else:
            if not isinstance(algorithm, hashes.SHA1):
                raise UnsupportedAlgorithm(
                    "This version of OpenSSL only supports PBKDF2HMAC with "
                    "SHA1"
                )
            res = self._lib.PKCS5_PBKDF2_HMAC_SHA1(
                key_material,
                len(key_material),
                salt,
                len(salt),
                iterations,
                length,
                buf
            )
            assert res == 1

        return self._ffi.buffer(buf)[:]

    def _err_string(self, code):
        err_buf = self._ffi.new("char[]", 256)
        self._lib.ERR_error_string_n(code, err_buf, 256)
        return self._ffi.string(err_buf, 256)[:]

    def _handle_error(self, mode):
        code = self._lib.ERR_get_error()
        if not code and isinstance(mode, GCM):
            raise InvalidTag
        assert code != 0

        # consume any remaining errors on the stack
        ignored_code = None
        while ignored_code != 0:
            ignored_code = self._lib.ERR_get_error()

        # raise the first error we found
        return self._handle_error_code(code)

    def _handle_error_code(self, code):
        lib = self._lib.ERR_GET_LIB(code)
        func = self._lib.ERR_GET_FUNC(code)
        reason = self._lib.ERR_GET_REASON(code)

        if lib == self._lib.ERR_LIB_EVP:
            if func == self._lib.EVP_F_EVP_ENCRYPTFINAL_EX:
                if reason == self._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
                    raise ValueError(
                        "The length of the provided data is not a multiple of "
                        "the block length"
                    )
            elif func == self._lib.EVP_F_EVP_DECRYPTFINAL_EX:
                if reason == self._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
                    raise ValueError(
                        "The length of the provided data is not a multiple of "
                        "the block length"
                    )

        raise InternalError(
            "Unknown error code {0} from OpenSSL, "
            "you should probably file a bug. {1}".format(
                code, self._err_string(code)
            )
        )

    def _bn_to_int(self, bn):
        hex_cdata = self._lib.BN_bn2hex(bn)
        assert hex_cdata != self._ffi.NULL
        hex_str = self._ffi.string(hex_cdata)
        self._lib.OPENSSL_free(hex_cdata)
        return int(hex_str, 16)

    def generate_rsa_private_key(self, public_exponent, key_size):
        if public_exponent < 3:
            raise ValueError("public_exponent must be >= 3")

        if public_exponent & 1 == 0:
            raise ValueError("public_exponent must be odd")

        if key_size < 512:
            raise ValueError("key_size must be at least 512-bits")

        ctx = backend._lib.RSA_new()
        ctx = backend._ffi.gc(ctx, backend._lib.RSA_free)

        bn = backend._lib.BN_new()
        assert bn != self._ffi.NULL
        bn = backend._ffi.gc(bn, backend._lib.BN_free)

        res = backend._lib.BN_set_word(bn, public_exponent)
        assert res == 1

        res = backend._lib.RSA_generate_key_ex(
            ctx, key_size, bn, backend._ffi.NULL
        )
        assert res == 1

        return rsa.RSAPrivateKey(
            p=self._bn_to_int(ctx.p),
            q=self._bn_to_int(ctx.q),
            private_exponent=self._bn_to_int(ctx.d),
            public_exponent=self._bn_to_int(ctx.e),
            modulus=self._bn_to_int(ctx.n),
        )
Exemple #39
0
 def test_add_engine_more_than_once(self):
     b = Binding()
     with pytest.raises(RuntimeError):
         b._register_osrandom_engine()
Exemple #40
0
 def test_crypto_lock_init(self):
     b = Binding()
     b.init_static_locks()
     lock_cb = b.lib.CRYPTO_get_locking_callback()
     assert lock_cb != b.ffi.NULL
Exemple #41
0
import sys
import warnings

from six import PY3, binary_type, text_type

from cryptography.hazmat.bindings.openssl.binding import Binding


binding = Binding()
binding.init_static_locks()
ffi = binding.ffi
lib = binding.lib


def text(charp):
    """
    Get a native string type representing of the given CFFI ``char*`` object.

    :param charp: A C-style string represented using CFFI.

    :return: :class:`str`
    """
    if not charp:
        return ""
    return native(ffi.string(charp))


def exception_from_error_queue(exception_type):
    """
    Convert an OpenSSL library failure into a Python exception.
Exemple #42
0
class Backend(object):
    """
    OpenSSL API binding interfaces.
    """
    name = "openssl"

    def __init__(self):
        self._binding = Binding()
        self._ffi = self._binding.ffi
        self._lib = self._binding.lib

        self._binding.init_static_locks()

        # adds all ciphers/digests for EVP
        self._lib.OpenSSL_add_all_algorithms()
        # registers available SSL/TLS ciphers and digests
        self._lib.SSL_library_init()
        # loads error strings for libcrypto and libssl functions
        self._lib.SSL_load_error_strings()

        self._cipher_registry = {}
        self._register_default_ciphers()
        self.activate_osrandom_engine()

    def activate_builtin_random(self):
        # Obtain a new structural reference.
        e = self._lib.ENGINE_get_default_RAND()
        if e != self._ffi.NULL:
            self._lib.ENGINE_unregister_RAND(e)
            # Reset the RNG to use the new engine.
            self._lib.RAND_cleanup()
            # decrement the structural reference from get_default_RAND
            res = self._lib.ENGINE_finish(e)
            assert res == 1

    def activate_osrandom_engine(self):
        # Unregister and free the current engine.
        self.activate_builtin_random()
        # Fetches an engine by id and returns it. This creates a structural
        # reference.
        e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id)
        assert e != self._ffi.NULL
        # Initialize the engine for use. This adds a functional reference.
        res = self._lib.ENGINE_init(e)
        assert res == 1
        # Set the engine as the default RAND provider.
        res = self._lib.ENGINE_set_default_RAND(e)
        assert res == 1
        # Decrement the structural ref incremented by ENGINE_by_id.
        res = self._lib.ENGINE_free(e)
        assert res == 1
        # Decrement the functional ref incremented by ENGINE_init.
        res = self._lib.ENGINE_finish(e)
        assert res == 1
        # Reset the RNG to use the new engine.
        self._lib.RAND_cleanup()

    def openssl_version_text(self):
        """
        Friendly string name of the loaded OpenSSL library. This is not
        necessarily the same version as it was compiled against.

        Example: OpenSSL 1.0.1e 11 Feb 2013
        """
        return self._ffi.string(
            self._lib.SSLeay_version(self._lib.SSLEAY_VERSION)).decode("ascii")

    def create_hmac_ctx(self, key, algorithm):
        return _HMACContext(self, key, algorithm)

    def hash_supported(self, algorithm):
        digest = self._lib.EVP_get_digestbyname(algorithm.name.encode("ascii"))
        return digest != self._ffi.NULL

    def hmac_supported(self, algorithm):
        return self.hash_supported(algorithm)

    def create_hash_ctx(self, algorithm):
        return _HashContext(self, algorithm)

    def cipher_supported(self, cipher, mode):
        if self._evp_cipher_supported(cipher, mode):
            return True
        elif isinstance(mode, CTR) and isinstance(cipher, AES):
            return True
        else:
            return False

    def _evp_cipher_supported(self, cipher, mode):
        try:
            adapter = self._cipher_registry[type(cipher), type(mode)]
        except KeyError:
            return False
        evp_cipher = adapter(self, cipher, mode)
        return self._ffi.NULL != evp_cipher

    def register_cipher_adapter(self, cipher_cls, mode_cls, adapter):
        if (cipher_cls, mode_cls) in self._cipher_registry:
            raise ValueError("Duplicate registration for: {0} {1}.".format(
                cipher_cls, mode_cls))
        self._cipher_registry[cipher_cls, mode_cls] = adapter

    def _register_default_ciphers(self):
        for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8]:
            self.register_cipher_adapter(
                AES, mode_cls,
                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"))
        for mode_cls in [CBC, CTR, ECB, OFB, CFB]:
            self.register_cipher_adapter(
                Camellia, mode_cls,
                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"))
        for mode_cls in [CBC, CFB, CFB8, OFB]:
            self.register_cipher_adapter(
                TripleDES, mode_cls, GetCipherByName("des-ede3-{mode.name}"))
        self.register_cipher_adapter(TripleDES, ECB,
                                     GetCipherByName("des-ede3"))
        for mode_cls in [CBC, CFB, OFB, ECB]:
            self.register_cipher_adapter(Blowfish, mode_cls,
                                         GetCipherByName("bf-{mode.name}"))
        for mode_cls in [CBC, CFB, OFB, ECB]:
            self.register_cipher_adapter(SEED, mode_cls,
                                         GetCipherByName("seed-{mode.name}"))
        for cipher_cls, mode_cls in itertools.product(
            [CAST5, IDEA],
            [CBC, OFB, CFB, ECB],
        ):
            self.register_cipher_adapter(
                cipher_cls, mode_cls,
                GetCipherByName("{cipher.name}-{mode.name}"))
        self.register_cipher_adapter(ARC4, type(None), GetCipherByName("rc4"))
        self.register_cipher_adapter(
            AES, GCM,
            GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"))

    def create_symmetric_encryption_ctx(self, cipher, mode):
        if (isinstance(mode, CTR) and isinstance(cipher, AES)
                and not self._evp_cipher_supported(cipher, mode)):
            # This is needed to provide support for AES CTR mode in OpenSSL
            # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5
            # extended life ends 2020).
            return _AESCTRCipherContext(self, cipher, mode)
        else:
            return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)

    def create_symmetric_decryption_ctx(self, cipher, mode):
        if (isinstance(mode, CTR) and isinstance(cipher, AES)
                and not self._evp_cipher_supported(cipher, mode)):
            # This is needed to provide support for AES CTR mode in OpenSSL
            # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5
            # extended life ends 2020).
            return _AESCTRCipherContext(self, cipher, mode)
        else:
            return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)

    def pbkdf2_hmac_supported(self, algorithm):
        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
            return self.hmac_supported(algorithm)
        else:
            # OpenSSL < 1.0.0 has an explicit PBKDF2-HMAC-SHA1 function,
            # so if the PBKDF2_HMAC function is missing we only support
            # SHA1 via PBKDF2_HMAC_SHA1.
            return isinstance(algorithm, hashes.SHA1)

    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
                           key_material):
        buf = self._ffi.new("char[]", length)
        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
            evp_md = self._lib.EVP_get_digestbyname(
                algorithm.name.encode("ascii"))
            assert evp_md != self._ffi.NULL
            res = self._lib.PKCS5_PBKDF2_HMAC(key_material, len(key_material),
                                              salt, len(salt), iterations,
                                              evp_md, length, buf)
            assert res == 1
        else:
            if not isinstance(algorithm, hashes.SHA1):
                raise UnsupportedAlgorithm(
                    "This version of OpenSSL only supports PBKDF2HMAC with "
                    "SHA1.", _Reasons.UNSUPPORTED_HASH)
            res = self._lib.PKCS5_PBKDF2_HMAC_SHA1(key_material,
                                                   len(key_material), salt,
                                                   len(salt), iterations,
                                                   length, buf)
            assert res == 1

        return self._ffi.buffer(buf)[:]

    def _err_string(self, code):
        err_buf = self._ffi.new("char[]", 256)
        self._lib.ERR_error_string_n(code, err_buf, 256)
        return self._ffi.string(err_buf, 256)[:]

    def _consume_errors(self):
        errors = []
        while True:
            code = self._lib.ERR_get_error()
            if code == 0:
                break

            lib = self._lib.ERR_GET_LIB(code)
            func = self._lib.ERR_GET_FUNC(code)
            reason = self._lib.ERR_GET_REASON(code)

            errors.append(_OpenSSLError(code, lib, func, reason))
        return errors

    def _unknown_error(self, error):
        return InternalError("Unknown error code {0} from OpenSSL, "
                             "you should probably file a bug. {1}.".format(
                                 error.code, self._err_string(error.code)))

    def _bn_to_int(self, bn):
        if six.PY3:
            # Python 3 has constant time from_bytes, so use that.

            bn_num_bytes = (self._lib.BN_num_bits(bn) + 7) // 8
            bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes)
            bin_len = self._lib.BN_bn2bin(bn, bin_ptr)
            assert bin_len > 0
            assert bin_ptr != self._ffi.NULL
            return int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big")

        else:
            # Under Python 2 the best we can do is hex()

            hex_cdata = self._lib.BN_bn2hex(bn)
            assert hex_cdata != self._ffi.NULL
            hex_str = self._ffi.string(hex_cdata)
            self._lib.OPENSSL_free(hex_cdata)
            return int(hex_str, 16)

    def _int_to_bn(self, num, bn=None):
        """
        Converts a python integer to a BIGNUM. The returned BIGNUM will not
        be garbage collected (to support adding them to structs that take
        ownership of the object). Be sure to register it for GC if it will
        be discarded after use.
        """

        if bn is None:
            bn = self._ffi.NULL

        if six.PY3:
            # Python 3 has constant time to_bytes, so use that.

            binary = num.to_bytes(int(num.bit_length() / 8.0 + 1), "big")
            bn_ptr = self._lib.BN_bin2bn(binary, len(binary), bn)
            assert bn_ptr != self._ffi.NULL
            return bn_ptr

        else:
            # Under Python 2 the best we can do is hex()

            hex_num = hex(num).rstrip("L").lstrip("0x").encode("ascii") or b"0"
            bn_ptr = self._ffi.new("BIGNUM **")
            bn_ptr[0] = bn
            res = self._lib.BN_hex2bn(bn_ptr, hex_num)
            assert res != 0
            assert bn_ptr[0] != self._ffi.NULL
            return bn_ptr[0]

    def generate_rsa_private_key(self, public_exponent, key_size):
        rsa._verify_rsa_parameters(public_exponent, key_size)

        rsa_cdata = self._lib.RSA_new()
        assert rsa_cdata != self._ffi.NULL
        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)

        bn = self._int_to_bn(public_exponent)
        bn = self._ffi.gc(bn, self._lib.BN_free)

        res = self._lib.RSA_generate_key_ex(rsa_cdata, key_size, bn,
                                            self._ffi.NULL)
        assert res == 1

        return _RSAPrivateKey(self, rsa_cdata)

    def generate_rsa_parameters_supported(self, public_exponent, key_size):
        return (public_exponent >= 3 and public_exponent & 1 != 0
                and key_size >= 512)

    def load_rsa_private_numbers(self, numbers):
        rsa._check_private_key_components(numbers.p, numbers.q, numbers.d,
                                          numbers.dmp1, numbers.dmq1,
                                          numbers.iqmp,
                                          numbers.public_numbers.e,
                                          numbers.public_numbers.n)
        rsa_cdata = self._lib.RSA_new()
        assert rsa_cdata != self._ffi.NULL
        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
        rsa_cdata.p = self._int_to_bn(numbers.p)
        rsa_cdata.q = self._int_to_bn(numbers.q)
        rsa_cdata.d = self._int_to_bn(numbers.d)
        rsa_cdata.dmp1 = self._int_to_bn(numbers.dmp1)
        rsa_cdata.dmq1 = self._int_to_bn(numbers.dmq1)
        rsa_cdata.iqmp = self._int_to_bn(numbers.iqmp)
        rsa_cdata.e = self._int_to_bn(numbers.public_numbers.e)
        rsa_cdata.n = self._int_to_bn(numbers.public_numbers.n)
        res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL)
        assert res == 1

        return _RSAPrivateKey(self, rsa_cdata)

    def load_rsa_public_numbers(self, numbers):
        rsa._check_public_key_components(numbers.e, numbers.n)
        rsa_cdata = self._lib.RSA_new()
        assert rsa_cdata != self._ffi.NULL
        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
        rsa_cdata.e = self._int_to_bn(numbers.e)
        rsa_cdata.n = self._int_to_bn(numbers.n)
        res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL)
        assert res == 1

        return _RSAPublicKey(self, rsa_cdata)

    def _bytes_to_bio(self, data):
        """
        Return a _MemoryBIO namedtuple of (BIO, char*).

        The char* is the storage for the BIO and it must stay alive until the
        BIO is finished with.
        """
        data_char_p = self._ffi.new("char[]", data)
        bio = self._lib.BIO_new_mem_buf(data_char_p, len(data))
        assert bio != self._ffi.NULL

        return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_char_p)

    def _evp_pkey_to_private_key(self, evp_pkey):
        """
        Return the appropriate type of PrivateKey given an evp_pkey cdata
        pointer.
        """

        type = evp_pkey.type

        if type == self._lib.EVP_PKEY_RSA:
            rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
            assert rsa_cdata != self._ffi.NULL
            rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
            return _RSAPrivateKey(self, rsa_cdata)
        elif type == self._lib.EVP_PKEY_DSA:
            dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey)
            assert dsa_cdata != self._ffi.NULL
            dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)
            return _DSAPrivateKey(self, dsa_cdata)
        else:
            raise UnsupportedAlgorithm("Unsupported key type.")

    def _pem_password_cb(self, password):
        """
        Generate a pem_password_cb function pointer that copied the password to
        OpenSSL as required and returns the number of bytes copied.

        typedef int pem_password_cb(char *buf, int size,
                                    int rwflag, void *userdata);

        Useful for decrypting PKCS8 files and so on.

        Returns a tuple of (cdata function pointer, callback function).
        """
        def pem_password_cb(buf, size, writing, userdata):
            pem_password_cb.called += 1

            if not password:
                pem_password_cb.exception = TypeError(
                    "Password was not given but private key is encrypted.")
                return 0
            elif len(password) < size:
                pw_buf = self._ffi.buffer(buf, size)
                pw_buf[:len(password)] = password
                return len(password)
            else:
                pem_password_cb.exception = ValueError(
                    "Passwords longer than {0} bytes are not supported "
                    "by this backend.".format(size - 1))
                return 0

        pem_password_cb.called = 0
        pem_password_cb.exception = None

        return (self._ffi.callback("int (char *, int, int, void *)",
                                   pem_password_cb), pem_password_cb)

    def _rsa_cdata_from_private_key(self, private_key):
        ctx = self._lib.RSA_new()
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.RSA_free)

        ctx.p = self._int_to_bn(private_key.p)
        ctx.q = self._int_to_bn(private_key.q)
        ctx.d = self._int_to_bn(private_key.d)
        ctx.e = self._int_to_bn(private_key.e)
        ctx.n = self._int_to_bn(private_key.n)
        ctx.dmp1 = self._int_to_bn(private_key.dmp1)
        ctx.dmq1 = self._int_to_bn(private_key.dmq1)
        ctx.iqmp = self._int_to_bn(private_key.iqmp)
        res = self._lib.RSA_blinding_on(ctx, self._ffi.NULL)
        assert res == 1

        return ctx

    def _rsa_cdata_from_public_key(self, public_key):
        ctx = self._lib.RSA_new()
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.RSA_free)

        ctx.e = self._int_to_bn(public_key.e)
        ctx.n = self._int_to_bn(public_key.n)
        res = self._lib.RSA_blinding_on(ctx, self._ffi.NULL)
        assert res == 1

        return ctx

    def create_rsa_signature_ctx(self, private_key, padding, algorithm):
        warnings.warn(
            "create_rsa_signature_ctx is deprecated and will be removed in a "
            "future version.",
            utils.DeprecatedIn05,
            stacklevel=2)
        rsa_cdata = self._rsa_cdata_from_private_key(private_key)
        key = _RSAPrivateKey(self, rsa_cdata)
        return _RSASignatureContext(self, key, padding, algorithm)

    def create_rsa_verification_ctx(self, public_key, signature, padding,
                                    algorithm):
        warnings.warn(
            "create_rsa_verification_ctx is deprecated and will be removed in "
            "a future version.",
            utils.DeprecatedIn05,
            stacklevel=2)
        rsa_cdata = self._rsa_cdata_from_public_key(public_key)
        key = _RSAPublicKey(self, rsa_cdata)
        return _RSAVerificationContext(self, key, signature, padding,
                                       algorithm)

    def mgf1_hash_supported(self, algorithm):
        warnings.warn(
            "mgf1_hash_supported is deprecated and will be removed in "
            "a future version.",
            utils.DeprecatedIn05,
            stacklevel=2)
        return self._mgf1_hash_supported(algorithm)

    def _mgf1_hash_supported(self, algorithm):
        if self._lib.Cryptography_HAS_MGF1_MD:
            return self.hash_supported(algorithm)
        else:
            return isinstance(algorithm, hashes.SHA1)

    def rsa_padding_supported(self, padding):
        if isinstance(padding, PKCS1v15):
            return True
        elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1):
            return self._mgf1_hash_supported(padding._mgf._algorithm)
        elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
            return isinstance(padding._mgf._algorithm, hashes.SHA1)
        else:
            return False

    def generate_dsa_parameters(self, key_size):
        if key_size not in (1024, 2048, 3072):
            raise ValueError("Key size must be 1024 or 2048 or 3072 bits.")

        if (self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f and key_size > 1024):
            raise ValueError(
                "Key size must be 1024 because OpenSSL < 1.0.0 doesn't "
                "support larger key sizes.")

        ctx = self._lib.DSA_new()
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.DSA_free)

        res = self._lib.DSA_generate_parameters_ex(ctx, key_size,
                                                   self._ffi.NULL, 0,
                                                   self._ffi.NULL,
                                                   self._ffi.NULL,
                                                   self._ffi.NULL)

        assert res == 1

        return _DSAParameters(self, ctx)

    def generate_dsa_private_key(self, parameters):
        ctx = self._lib.DSA_new()
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.DSA_free)
        if isinstance(parameters, dsa.DSAParameters):
            ctx.p = self._int_to_bn(parameters.p)
            ctx.q = self._int_to_bn(parameters.q)
            ctx.g = self._int_to_bn(parameters.g)
        else:
            ctx.p = self._lib.BN_dup(parameters._dsa_cdata.p)
            ctx.q = self._lib.BN_dup(parameters._dsa_cdata.q)
            ctx.g = self._lib.BN_dup(parameters._dsa_cdata.g)

        self._lib.DSA_generate_key(ctx)

        return _DSAPrivateKey(self, ctx)

    def generate_dsa_private_key_and_parameters(self, key_size):
        parameters = self.generate_dsa_parameters(key_size)
        return self.generate_dsa_private_key(parameters)

    def create_dsa_signature_ctx(self, private_key, algorithm):
        warnings.warn(
            "create_dsa_signature_ctx is deprecated and will be removed in "
            "a future version.",
            utils.DeprecatedIn05,
            stacklevel=2)
        dsa_cdata = self._dsa_cdata_from_private_key(private_key)
        key = _DSAPrivateKey(self, dsa_cdata)
        return _DSASignatureContext(self, key, algorithm)

    def create_dsa_verification_ctx(self, public_key, signature, algorithm):
        warnings.warn(
            "create_dsa_verification_ctx is deprecated and will be removed in "
            "a future version.",
            utils.DeprecatedIn05,
            stacklevel=2)
        dsa_cdata = self._dsa_cdata_from_public_key(public_key)
        key = _DSAPublicKey(self, dsa_cdata)
        return _DSAVerificationContext(self, key, signature, algorithm)

    def load_dsa_private_numbers(self, numbers):
        dsa._check_dsa_private_numbers(numbers)
        parameter_numbers = numbers.public_numbers.parameter_numbers

        dsa_cdata = self._lib.DSA_new()
        assert dsa_cdata != self._ffi.NULL
        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)

        dsa_cdata.p = self._int_to_bn(parameter_numbers.p)
        dsa_cdata.q = self._int_to_bn(parameter_numbers.q)
        dsa_cdata.g = self._int_to_bn(parameter_numbers.g)
        dsa_cdata.pub_key = self._int_to_bn(numbers.public_numbers.y)
        dsa_cdata.priv_key = self._int_to_bn(numbers.x)

        return _DSAPrivateKey(self, dsa_cdata)

    def load_dsa_public_numbers(self, numbers):
        dsa._check_dsa_parameters(numbers.parameter_numbers)
        dsa_cdata = self._lib.DSA_new()
        assert dsa_cdata != self._ffi.NULL
        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)

        dsa_cdata.p = self._int_to_bn(numbers.parameter_numbers.p)
        dsa_cdata.q = self._int_to_bn(numbers.parameter_numbers.q)
        dsa_cdata.g = self._int_to_bn(numbers.parameter_numbers.g)
        dsa_cdata.pub_key = self._int_to_bn(numbers.y)

        return _DSAPublicKey(self, dsa_cdata)

    def load_dsa_parameter_numbers(self, numbers):
        dsa._check_dsa_parameters(numbers)
        dsa_cdata = self._lib.DSA_new()
        assert dsa_cdata != self._ffi.NULL
        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)

        dsa_cdata.p = self._int_to_bn(numbers.p)
        dsa_cdata.q = self._int_to_bn(numbers.q)
        dsa_cdata.g = self._int_to_bn(numbers.g)

        return _DSAParameters(self, dsa_cdata)

    def _dsa_cdata_from_public_key(self, public_key):
        ctx = self._lib.DSA_new()
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.DSA_free)
        parameters = public_key.parameters()
        ctx.p = self._int_to_bn(parameters.p)
        ctx.q = self._int_to_bn(parameters.q)
        ctx.g = self._int_to_bn(parameters.g)
        ctx.pub_key = self._int_to_bn(public_key.y)
        return ctx

    def _dsa_cdata_from_private_key(self, private_key):
        ctx = self._lib.DSA_new()
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.DSA_free)
        parameters = private_key.parameters()
        ctx.p = self._int_to_bn(parameters.p)
        ctx.q = self._int_to_bn(parameters.q)
        ctx.g = self._int_to_bn(parameters.g)
        ctx.priv_key = self._int_to_bn(private_key.x)
        ctx.pub_key = self._int_to_bn(private_key.y)
        return ctx

    def dsa_hash_supported(self, algorithm):
        if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f:
            return isinstance(algorithm, hashes.SHA1)
        else:
            return self.hash_supported(algorithm)

    def dsa_parameters_supported(self, p, q, g):
        if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f:
            return (utils.bit_length(p) <= 1024 and utils.bit_length(q) <= 160)
        else:
            return True

    def decrypt_rsa(self, private_key, ciphertext, padding):
        warnings.warn(
            "decrypt_rsa is deprecated and will be removed in a future "
            "version.",
            utils.DeprecatedIn05,
            stacklevel=2)
        rsa_cdata = self._rsa_cdata_from_private_key(private_key)
        key = _RSAPrivateKey(self, rsa_cdata)
        return key.decrypt(ciphertext, padding)

    def encrypt_rsa(self, public_key, plaintext, padding):
        warnings.warn(
            "encrypt_rsa is deprecated and will be removed in a future "
            "version.",
            utils.DeprecatedIn05,
            stacklevel=2)
        rsa_cdata = self._rsa_cdata_from_public_key(public_key)
        key = _RSAPublicKey(self, rsa_cdata)
        return key.encrypt(plaintext, padding)

    def cmac_algorithm_supported(self, algorithm):
        return (self._lib.Cryptography_HAS_CMAC == 1 and self.cipher_supported(
            algorithm, CBC(b"\x00" * algorithm.block_size)))

    def create_cmac_ctx(self, algorithm):
        return _CMACContext(self, algorithm)

    def load_traditional_openssl_pem_private_key(self, data, password):
        # OpenSSLs API for loading PKCS#8 certs can also load the traditional
        # format so we just use that for both of them.
        return self.load_pkcs8_pem_private_key(data, password)

    def load_pkcs8_pem_private_key(self, data, password):
        return self._load_key(
            self._lib.PEM_read_bio_PrivateKey,
            self._evp_pkey_to_private_key,
            data,
            password,
        )

    def _load_key(self, openssl_read_func, convert_func, data, password):
        mem_bio = self._bytes_to_bio(data)

        password_callback, password_func = self._pem_password_cb(password)

        evp_pkey = openssl_read_func(mem_bio.bio, self._ffi.NULL,
                                     password_callback, self._ffi.NULL)

        if evp_pkey == self._ffi.NULL:
            if password_func.exception is not None:
                errors = self._consume_errors()
                assert errors
                raise password_func.exception
            else:
                self._handle_key_loading_error()

        evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)

        if password is not None and password_func.called == 0:
            raise TypeError(
                "Password was given but private key is not encrypted.")

        assert ((password is not None and password_func.called == 1)
                or password is None)

        return convert_func(evp_pkey)

    def _handle_key_loading_error(self):
        errors = self._consume_errors()

        if not errors:
            raise ValueError("Could not unserialize key data.")

        elif errors[0][1:] == (self._lib.ERR_LIB_EVP,
                               self._lib.EVP_F_EVP_DECRYPTFINAL_EX,
                               self._lib.EVP_R_BAD_DECRYPT):
            raise ValueError("Bad decrypt. Incorrect password?")

        elif errors[0][1:] in ((self._lib.ERR_LIB_PEM,
                                self._lib.PEM_F_PEM_GET_EVP_CIPHER_INFO,
                                self._lib.PEM_R_UNSUPPORTED_ENCRYPTION),
                               (self._lib.ERR_LIB_EVP,
                                self._lib.EVP_F_EVP_PBE_CIPHERINIT,
                                self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM)):
            raise UnsupportedAlgorithm(
                "PEM data is encrypted with an unsupported cipher",
                _Reasons.UNSUPPORTED_CIPHER)

        elif any(error[1:] == (
                self._lib.ERR_LIB_EVP, self._lib.EVP_F_EVP_PKCS82PKEY,
                self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM)
                 for error in errors):
            raise UnsupportedAlgorithm(
                "Unsupported public key algorithm.",
                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)

        else:
            assert errors[0][1] in (
                self._lib.ERR_LIB_EVP,
                self._lib.ERR_LIB_PEM,
                self._lib.ERR_LIB_ASN1,
            )
            raise ValueError("Could not unserialize key data.")

    def elliptic_curve_supported(self, curve):
        if self._lib.Cryptography_HAS_EC != 1:
            return False

        try:
            curve_nid = self._elliptic_curve_to_nid(curve)
        except UnsupportedAlgorithm:
            curve_nid = self._lib.NID_undef

        ctx = self._lib.EC_GROUP_new_by_curve_name(curve_nid)

        if ctx == self._ffi.NULL:
            errors = self._consume_errors()
            assert (curve_nid == self._lib.NID_undef or errors[0][1:]
                    == (self._lib.ERR_LIB_EC,
                        self._lib.EC_F_EC_GROUP_NEW_BY_CURVE_NAME,
                        self._lib.EC_R_UNKNOWN_GROUP))
            return False
        else:
            assert curve_nid != self._lib.NID_undef
            self._lib.EC_GROUP_free(ctx)
            return True

    def elliptic_curve_signature_algorithm_supported(self, signature_algorithm,
                                                     curve):
        if self._lib.Cryptography_HAS_EC != 1:
            return False

        # We only support ECDSA right now.
        if not isinstance(signature_algorithm, ec.ECDSA):
            return False

        # Before 0.9.8m OpenSSL can't cope with digests longer than the curve.
        if (self._lib.OPENSSL_VERSION_NUMBER < 0x009080df and curve.key_size <
                signature_algorithm.algorithm.digest_size * 8):
            return False

        return self.elliptic_curve_supported(curve)

    def generate_elliptic_curve_private_key(self, curve):
        """
        Generate a new private key on the named curve.
        """

        if self.elliptic_curve_supported(curve):
            curve_nid = self._elliptic_curve_to_nid(curve)

            ctx = self._lib.EC_KEY_new_by_curve_name(curve_nid)
            assert ctx != self._ffi.NULL
            ctx = self._ffi.gc(ctx, self._lib.EC_KEY_free)

            res = self._lib.EC_KEY_generate_key(ctx)
            assert res == 1

            res = self._lib.EC_KEY_check_key(ctx)
            assert res == 1

            return _EllipticCurvePrivateKey(self, ctx, curve)
        else:
            raise UnsupportedAlgorithm(
                "Backend object does not support {0}.".format(curve.name),
                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE)

    def elliptic_curve_private_key_from_numbers(self, numbers):
        public = numbers.public_numbers

        curve_nid = self._elliptic_curve_to_nid(public.curve)

        ctx = self._lib.EC_KEY_new_by_curve_name(curve_nid)
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.EC_KEY_free)

        ctx = self._ec_key_set_public_key_affine_coordinates(
            ctx, public.x, public.y)

        res = self._lib.EC_KEY_set_private_key(
            ctx, self._int_to_bn(numbers.private_value))
        assert res == 1

        return _EllipticCurvePrivateKey(self, ctx,
                                        numbers.public_numbers.curve)

    def elliptic_curve_public_key_from_numbers(self, numbers):
        curve_nid = self._elliptic_curve_to_nid(numbers.curve)

        ctx = self._lib.EC_KEY_new_by_curve_name(curve_nid)
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.EC_KEY_free)

        ctx = self._ec_key_set_public_key_affine_coordinates(
            ctx, numbers.x, numbers.y)

        return _EllipticCurvePublicKey(self, ctx, numbers.curve)

    def _elliptic_curve_to_nid(self, curve):
        """
        Get the NID for a curve name.
        """

        curve_aliases = {"secp192r1": "prime192v1", "secp256r1": "prime256v1"}

        curve_name = curve_aliases.get(curve.name, curve.name)

        curve_nid = self._lib.OBJ_sn2nid(curve_name.encode())
        if curve_nid == self._lib.NID_undef:
            raise UnsupportedAlgorithm(
                "{0} is not a supported elliptic curve".format(curve.name),
                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE)
        return curve_nid

    def _ec_key_set_public_key_affine_coordinates(self, ctx, x, y):
        """
        This is a port of EC_KEY_set_public_key_affine_coordinates that was
        added in 1.0.1.

        Sets the public key point in the EC_KEY context to the affine x and y
        values.
        """

        assert ctx != self._ffi.NULL

        bn_x = self._int_to_bn(x)
        bn_y = self._int_to_bn(y)

        nid_two_field = self._lib.OBJ_sn2nid(b"characteristic-two-field")
        assert nid_two_field != self._lib.NID_undef

        bn_ctx = self._lib.BN_CTX_new()
        assert bn_ctx != self._ffi.NULL
        bn_ctx = self._ffi.gc(bn_ctx, self._lib.BN_CTX_free)

        group = self._lib.EC_KEY_get0_group(ctx)
        assert group != self._ffi.NULL

        point = self._lib.EC_POINT_new(group)
        assert point != self._ffi.NULL
        point = self._ffi.gc(point, self._lib.EC_POINT_free)

        method = self._lib.EC_GROUP_method_of(group)
        assert method != self._ffi.NULL

        nid = self._lib.EC_METHOD_get_field_type(method)
        assert nid != self._lib.NID_undef

        check_x = self._lib.BN_CTX_get(bn_ctx)
        check_y = self._lib.BN_CTX_get(bn_ctx)

        if nid == nid_two_field and self._lib.Cryptography_HAS_EC2M:
            set_func = self._lib.EC_POINT_set_affine_coordinates_GF2m
            get_func = self._lib.EC_POINT_get_affine_coordinates_GF2m
        else:
            set_func = self._lib.EC_POINT_set_affine_coordinates_GFp
            get_func = self._lib.EC_POINT_get_affine_coordinates_GFp

        assert set_func and get_func

        res = set_func(group, point, bn_x, bn_y, bn_ctx)
        assert res == 1

        res = get_func(group, point, check_x, check_y, bn_ctx)
        assert res == 1

        assert (self._lib.BN_cmp(bn_x, check_x) == 0
                and self._lib.BN_cmp(bn_y, check_y) == 0)

        res = self._lib.EC_KEY_set_public_key(ctx, point)
        assert res == 1

        res = self._lib.EC_KEY_check_key(ctx)
        assert res == 1

        return ctx
Exemple #43
0
def init_dtls():
    binding = Binding()
    binding.init_static_locks()
    SSL.Context._methods[0] = getattr(binding.lib, "DTLSv1_client_method")
Exemple #44
0
class Backend(object):
    """
    OpenSSL API binding interfaces.
    """
    name = "openssl"

    def __init__(self):
        self._binding = Binding()
        self._ffi = self._binding.ffi
        self._lib = self._binding.lib

        self._binding.init_static_locks()

        # adds all ciphers/digests for EVP
        self._lib.OpenSSL_add_all_algorithms()
        # registers available SSL/TLS ciphers and digests
        self._lib.SSL_library_init()
        # loads error strings for libcrypto and libssl functions
        self._lib.SSL_load_error_strings()

        self._cipher_registry = {}
        self._register_default_ciphers()
        self.activate_osrandom_engine()

    def activate_builtin_random(self):
        # Obtain a new structural reference.
        e = self._lib.ENGINE_get_default_RAND()
        if e != self._ffi.NULL:
            self._lib.ENGINE_unregister_RAND(e)
            # Reset the RNG to use the new engine.
            self._lib.RAND_cleanup()
            # decrement the structural reference from get_default_RAND
            res = self._lib.ENGINE_finish(e)
            assert res == 1

    def activate_osrandom_engine(self):
        # Unregister and free the current engine.
        self.activate_builtin_random()
        # Fetches an engine by id and returns it. This creates a structural
        # reference.
        e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id)
        assert e != self._ffi.NULL
        # Initialize the engine for use. This adds a functional reference.
        res = self._lib.ENGINE_init(e)
        assert res == 1
        # Set the engine as the default RAND provider.
        res = self._lib.ENGINE_set_default_RAND(e)
        assert res == 1
        # Decrement the structural ref incremented by ENGINE_by_id.
        res = self._lib.ENGINE_free(e)
        assert res == 1
        # Decrement the functional ref incremented by ENGINE_init.
        res = self._lib.ENGINE_finish(e)
        assert res == 1
        # Reset the RNG to use the new engine.
        self._lib.RAND_cleanup()

    def openssl_version_text(self):
        """
        Friendly string name of linked OpenSSL.

        Example: OpenSSL 1.0.1e 11 Feb 2013
        """
        return self._ffi.string(self._lib.OPENSSL_VERSION_TEXT).decode("ascii")

    def create_hmac_ctx(self, key, algorithm):
        return _HMACContext(self, key, algorithm)

    def hash_supported(self, algorithm):
        digest = self._lib.EVP_get_digestbyname(algorithm.name.encode("ascii"))
        return digest != self._ffi.NULL

    def hmac_supported(self, algorithm):
        return self.hash_supported(algorithm)

    def create_hash_ctx(self, algorithm):
        return _HashContext(self, algorithm)

    def cipher_supported(self, cipher, mode):
        try:
            adapter = self._cipher_registry[type(cipher), type(mode)]
        except KeyError:
            return False
        evp_cipher = adapter(self, cipher, mode)
        return self._ffi.NULL != evp_cipher

    def register_cipher_adapter(self, cipher_cls, mode_cls, adapter):
        if (cipher_cls, mode_cls) in self._cipher_registry:
            raise ValueError("Duplicate registration for: {0} {1}".format(
                cipher_cls, mode_cls)
            )
        self._cipher_registry[cipher_cls, mode_cls] = adapter

    def _register_default_ciphers(self):
        for cipher_cls, mode_cls in itertools.product(
            [AES, Camellia],
            [CBC, CTR, ECB, OFB, CFB],
        ):
            self.register_cipher_adapter(
                cipher_cls,
                mode_cls,
                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
            )
        for mode_cls in [CBC, CFB, OFB]:
            self.register_cipher_adapter(
                TripleDES,
                mode_cls,
                GetCipherByName("des-ede3-{mode.name}")
            )
        for mode_cls in [CBC, CFB, OFB, ECB]:
            self.register_cipher_adapter(
                Blowfish,
                mode_cls,
                GetCipherByName("bf-{mode.name}")
            )
        for cipher_cls, mode_cls in itertools.product(
            [CAST5, IDEA],
            [CBC, OFB, CFB, ECB],
        ):
            self.register_cipher_adapter(
                cipher_cls,
                mode_cls,
                GetCipherByName("{cipher.name}-{mode.name}")
            )
        self.register_cipher_adapter(
            ARC4,
            type(None),
            GetCipherByName("rc4")
        )
        self.register_cipher_adapter(
            AES,
            GCM,
            GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
        )

    def create_symmetric_encryption_ctx(self, cipher, mode):
        return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)

    def create_symmetric_decryption_ctx(self, cipher, mode):
        return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)

    def pbkdf2_hmac_supported(self, algorithm):
        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
            return self.hmac_supported(algorithm)
        else:
            # OpenSSL < 1.0.0 has an explicit PBKDF2-HMAC-SHA1 function,
            # so if the PBKDF2_HMAC function is missing we only support
            # SHA1 via PBKDF2_HMAC_SHA1.
            return isinstance(algorithm, hashes.SHA1)

    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
                           key_material):
        buf = self._ffi.new("char[]", length)
        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
            evp_md = self._lib.EVP_get_digestbyname(
                algorithm.name.encode("ascii"))
            assert evp_md != self._ffi.NULL
            res = self._lib.PKCS5_PBKDF2_HMAC(
                key_material,
                len(key_material),
                salt,
                len(salt),
                iterations,
                evp_md,
                length,
                buf
            )
            assert res == 1
        else:
            if not isinstance(algorithm, hashes.SHA1):
                raise UnsupportedHash(
                    "This version of OpenSSL only supports PBKDF2HMAC with "
                    "SHA1"
                )
            res = self._lib.PKCS5_PBKDF2_HMAC_SHA1(
                key_material,
                len(key_material),
                salt,
                len(salt),
                iterations,
                length,
                buf
            )
            assert res == 1

        return self._ffi.buffer(buf)[:]

    def _err_string(self, code):
        err_buf = self._ffi.new("char[]", 256)
        self._lib.ERR_error_string_n(code, err_buf, 256)
        return self._ffi.string(err_buf, 256)[:]

    def _consume_errors(self):
        errors = []
        while True:
            code = self._lib.ERR_get_error()
            if code == 0:
                break

            lib = self._lib.ERR_GET_LIB(code)
            func = self._lib.ERR_GET_FUNC(code)
            reason = self._lib.ERR_GET_REASON(code)

            errors.append(_OpenSSLError(code, lib, func, reason))
        return errors

    def _unknown_error(self, error):
        return InternalError(
            "Unknown error code {0} from OpenSSL, "
            "you should probably file a bug. {1}".format(
                error.code, self._err_string(error.code)
            )
        )

    def _bn_to_int(self, bn):
        hex_cdata = self._lib.BN_bn2hex(bn)
        assert hex_cdata != self._ffi.NULL
        hex_str = self._ffi.string(hex_cdata)
        self._lib.OPENSSL_free(hex_cdata)
        return int(hex_str, 16)

    def _int_to_bn(self, num):
        """
        Converts a python integer to a BIGNUM. The returned BIGNUM will not
        be garbage collected (to support adding them to structs that take
        ownership of the object). Be sure to register it for GC if it will
        be discarded after use.
        """
        hex_num = hex(num).rstrip("L").lstrip("0x").encode("ascii") or b"0"
        bn_ptr = self._ffi.new("BIGNUM **")
        res = self._lib.BN_hex2bn(bn_ptr, hex_num)
        assert res != 0
        assert bn_ptr[0] != self._ffi.NULL
        return bn_ptr[0]

    def generate_rsa_private_key(self, public_exponent, key_size):
        if public_exponent < 3:
            raise ValueError("public_exponent must be >= 3")

        if public_exponent & 1 == 0:
            raise ValueError("public_exponent must be odd")

        if key_size < 512:
            raise ValueError("key_size must be at least 512-bits")

        ctx = self._lib.RSA_new()
        assert ctx != self._ffi.NULL
        ctx = self._ffi.gc(ctx, self._lib.RSA_free)

        bn = self._int_to_bn(public_exponent)
        bn = self._ffi.gc(bn, self._lib.BN_free)

        res = self._lib.RSA_generate_key_ex(
            ctx, key_size, bn, self._ffi.NULL
        )
        assert res == 1

        return self._rsa_cdata_to_private_key(ctx)

    def _new_evp_pkey(self):
        evp_pkey = self._lib.EVP_PKEY_new()
        assert evp_pkey != self._ffi.NULL
        return self._ffi.gc(evp_pkey, backend._lib.EVP_PKEY_free)

    def _rsa_private_key_to_evp_pkey(self, private_key):
        evp_pkey = self._new_evp_pkey()
        rsa_cdata = self._rsa_cdata_from_private_key(private_key)

        res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL)
        assert res == 1

        res = self._lib.EVP_PKEY_assign_RSA(evp_pkey, rsa_cdata)
        assert res == 1

        return evp_pkey

    def _rsa_public_key_to_evp_pkey(self, public_key):
        evp_pkey = self._new_evp_pkey()
        rsa_cdata = self._rsa_cdata_from_public_key(public_key)

        res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL)
        assert res == 1

        res = self._lib.EVP_PKEY_assign_RSA(evp_pkey, rsa_cdata)
        assert res == 1

        return evp_pkey

    def _rsa_cdata_to_private_key(self, cdata):
        return rsa.RSAPrivateKey(
            p=self._bn_to_int(cdata.p),
            q=self._bn_to_int(cdata.q),
            dmp1=self._bn_to_int(cdata.dmp1),
            dmq1=self._bn_to_int(cdata.dmq1),
            iqmp=self._bn_to_int(cdata.iqmp),
            private_exponent=self._bn_to_int(cdata.d),
            public_exponent=self._bn_to_int(cdata.e),
            modulus=self._bn_to_int(cdata.n),
        )

    def _rsa_cdata_from_private_key(self, private_key):
        # Does not GC the RSA cdata. You *must* make sure it's freed
        # correctly yourself!
        ctx = self._lib.RSA_new()
        assert ctx != self._ffi.NULL
        ctx.p = self._int_to_bn(private_key.p)
        ctx.q = self._int_to_bn(private_key.q)
        ctx.d = self._int_to_bn(private_key.d)
        ctx.e = self._int_to_bn(private_key.e)
        ctx.n = self._int_to_bn(private_key.n)
        ctx.dmp1 = self._int_to_bn(private_key.dmp1)
        ctx.dmq1 = self._int_to_bn(private_key.dmq1)
        ctx.iqmp = self._int_to_bn(private_key.iqmp)
        return ctx

    def _rsa_cdata_from_public_key(self, public_key):
        # Does not GC the RSA cdata. You *must* make sure it's freed
        # correctly yourself!

        ctx = self._lib.RSA_new()
        assert ctx != self._ffi.NULL
        ctx.e = self._int_to_bn(public_key.e)
        ctx.n = self._int_to_bn(public_key.n)
        return ctx

    def create_rsa_signature_ctx(self, private_key, padding, algorithm):
        return _RSASignatureContext(self, private_key, padding, algorithm)

    def create_rsa_verification_ctx(self, public_key, signature, padding,
                                    algorithm):
        return _RSAVerificationContext(self, public_key, signature, padding,
                                       algorithm)
Exemple #45
0
import attr
import pylibsrtp
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.bindings.openssl.binding import Binding
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import (Encoding,
                                                          NoEncryption,
                                                          PrivateFormat)
from OpenSSL import crypto
from pyee import EventEmitter
from pylibsrtp import Policy, Session

from .rtp import RtcpPacket, RtpPacket, get_header_extensions, is_rtcp
from .utils import first_completed

binding = Binding()
binding.init_static_locks()
ffi = binding.ffi
lib = binding.lib

SRTP_KEY_LEN = 16
SRTP_SALT_LEN = 14

logger = logging.getLogger('dtls')


class DtlsError(Exception):
    pass


def _openssl_assert(ok):
Exemple #46
0
class Backend(object):
    """
    OpenSSL API binding interfaces.
    """
    name = "openssl"

    def __init__(self):
        self._binding = Binding()
        self._ffi = self._binding.ffi
        self._lib = self._binding.lib

        self._binding.init_static_locks()

        # adds all ciphers/digests for EVP
        self._lib.OpenSSL_add_all_algorithms()
        # registers available SSL/TLS ciphers and digests
        self._lib.SSL_library_init()
        # loads error strings for libcrypto and libssl functions
        self._lib.SSL_load_error_strings()

        self._cipher_registry = {}
        self._register_default_ciphers()

    def openssl_version_text(self):
        """
        Friendly string name of linked OpenSSL.

        Example: OpenSSL 1.0.1e 11 Feb 2013
        """
        return self._ffi.string(self._lib.OPENSSL_VERSION_TEXT).decode("ascii")

    def create_hmac_ctx(self, key, algorithm):
        return _HMACContext(self, key, algorithm)

    def hash_supported(self, algorithm):
        digest = self._lib.EVP_get_digestbyname(algorithm.name.encode("ascii"))
        return digest != self._ffi.NULL

    def hmac_supported(self, algorithm):
        return self.hash_supported(algorithm)

    def create_hash_ctx(self, algorithm):
        return _HashContext(self, algorithm)

    def cipher_supported(self, cipher, mode):
        try:
            adapter = self._cipher_registry[type(cipher), type(mode)]
        except KeyError:
            return False
        evp_cipher = adapter(self, cipher, mode)
        return self._ffi.NULL != evp_cipher

    def register_cipher_adapter(self, cipher_cls, mode_cls, adapter):
        if (cipher_cls, mode_cls) in self._cipher_registry:
            raise ValueError("Duplicate registration for: {0} {1}".format(
                cipher_cls, mode_cls)
            )
        self._cipher_registry[cipher_cls, mode_cls] = adapter

    def _register_default_ciphers(self):
        for cipher_cls, mode_cls in itertools.product(
            [AES, Camellia],
            [CBC, CTR, ECB, OFB, CFB],
        ):
            self.register_cipher_adapter(
                cipher_cls,
                mode_cls,
                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
            )
        for mode_cls in [CBC, CFB, OFB]:
            self.register_cipher_adapter(
                TripleDES,
                mode_cls,
                GetCipherByName("des-ede3-{mode.name}")
            )
        for mode_cls in [CBC, CFB, OFB, ECB]:
            self.register_cipher_adapter(
                Blowfish,
                mode_cls,
                GetCipherByName("bf-{mode.name}")
            )
        self.register_cipher_adapter(
            ARC4,
            type(None),
            GetCipherByName("rc4")
        )
        self.register_cipher_adapter(
            AES,
            GCM,
            GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
        )

    def create_symmetric_encryption_ctx(self, cipher, mode):
        return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)

    def create_symmetric_decryption_ctx(self, cipher, mode):
        return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)

    def pbkdf2_hmac_supported(self, algorithm):
        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
            return self.hmac_supported(algorithm)
        else:
            # OpenSSL < 1.0.0 has an explicit PBKDF2-HMAC-SHA1 function,
            # so if the PBKDF2_HMAC function is missing we only support
            # SHA1 via PBKDF2_HMAC_SHA1.
            return isinstance(algorithm, hashes.SHA1)

    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
                           key_material):
        buf = self._ffi.new("char[]", length)
        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
            evp_md = self._lib.EVP_get_digestbyname(
                algorithm.name.encode("ascii"))
            assert evp_md != self._ffi.NULL
            res = self._lib.PKCS5_PBKDF2_HMAC(
                key_material,
                len(key_material),
                salt,
                len(salt),
                iterations,
                evp_md,
                length,
                buf
            )
            assert res == 1
        else:
            # OpenSSL < 1.0.0
            assert isinstance(algorithm, hashes.SHA1)
            res = self._lib.PKCS5_PBKDF2_HMAC_SHA1(
                key_material,
                len(key_material),
                salt,
                len(salt),
                iterations,
                length,
                buf
            )
            assert res == 1

        return self._ffi.buffer(buf)[:]

    def _handle_error(self, mode):
        code = self._lib.ERR_get_error()
        if not code and isinstance(mode, GCM):
            raise InvalidTag
        assert code != 0
        lib = self._lib.ERR_GET_LIB(code)
        func = self._lib.ERR_GET_FUNC(code)
        reason = self._lib.ERR_GET_REASON(code)
        return self._handle_error_code(lib, func, reason)

    def _handle_error_code(self, lib, func, reason):
        if lib == self._lib.ERR_LIB_EVP:
            if func == self._lib.EVP_F_EVP_ENCRYPTFINAL_EX:
                if reason == self._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
                    raise ValueError(
                        "The length of the provided data is not a multiple of "
                        "the block length"
                    )
            elif func == self._lib.EVP_F_EVP_DECRYPTFINAL_EX:
                if reason == self._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
                    raise ValueError(
                        "The length of the provided data is not a multiple of "
                        "the block length"
                    )

        raise InternalError(
            "Unknown error code from OpenSSL, you should probably file a bug."
        )