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()
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()
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_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
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()
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
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
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 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
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)
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
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
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
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
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
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
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
def test_is_available(self): assert Binding.is_available() is True
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
def test_add_engine_more_than_once(self): b = Binding() b._register_osrandom_engine() assert b.lib.ERR_get_error() == 0
def test_binding_loads(self): binding = Binding() assert binding assert binding.lib assert binding.ffi
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), )
def test_add_engine_more_than_once(self): b = Binding() with pytest.raises(RuntimeError): b._register_osrandom_engine()
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
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.
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
def init_dtls(): binding = Binding() binding.init_static_locks() SSL.Context._methods[0] = getattr(binding.lib, "DTLSv1_client_method")
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)
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):
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." )