diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/_cffi_src/build_openssl.py | 1 | ||||
-rw-r--r-- | src/_cffi_src/openssl/callbacks.py | 50 | ||||
-rw-r--r-- | src/_cffi_src/openssl/evp.py | 16 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 49 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 8 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/binding.py | 34 |
6 files changed, 124 insertions, 34 deletions
diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py index c47b3082..ebbe8865 100644 --- a/src/_cffi_src/build_openssl.py +++ b/src/_cffi_src/build_openssl.py @@ -90,6 +90,7 @@ ffi = build_ffi_for_binding( "x509v3", "x509_vfy", "pkcs7", + "callbacks", ], pre_include=_OSX_PRE_INCLUDE, post_include=_OSX_POST_INCLUDE, diff --git a/src/_cffi_src/openssl/callbacks.py b/src/_cffi_src/openssl/callbacks.py new file mode 100644 index 00000000..3e4ef572 --- /dev/null +++ b/src/_cffi_src/openssl/callbacks.py @@ -0,0 +1,50 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import cffi + +INCLUDES = """ +#include <openssl/ssl.h> +#include <openssl/x509.h> +#include <openssl/x509_vfy.h> +""" + +TYPES = """ +static const long Cryptography_STATIC_CALLBACKS; + +/* crypto.h + * CRYPTO_set_locking_callback + * void (*cb)(int mode, int type, const char *file, int line) + */ +extern "Python" void Cryptography_locking_cb(int, int, const char *, int); + +/* pem.h + * int pem_password_cb(char *buf, int size, int rwflag, void *userdata); + */ +extern "Python" int Cryptography_pem_password_cb(char *, int, int, void *); + +/* rand.h + * int (*bytes)(unsigned char *buf, int num); + * int (*status)(void); + */ +extern "Python" int Cryptography_rand_bytes(unsigned char *, int); +extern "Python" int Cryptography_rand_status(void); +""" + +FUNCTIONS = """ +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +static const long Cryptography_STATIC_CALLBACKS = 1; +""" + +if cffi.__version_info__ < (1, 4, 0): + # backwards compatibility for old cffi version on PyPy + TYPES = "static const long Cryptography_STATIC_CALLBACKS;" + CUSTOMIZATIONS = "static const long Cryptography_STATIC_CALLBACKS = 0;" diff --git a/src/_cffi_src/openssl/evp.py b/src/_cffi_src/openssl/evp.py index 6d17cb7c..1d37b814 100644 --- a/src/_cffi_src/openssl/evp.py +++ b/src/_cffi_src/openssl/evp.py @@ -21,10 +21,7 @@ typedef struct env_md_ctx_st { ...; } EVP_MD_CTX; -typedef struct evp_pkey_st { - int type; - ...; -} EVP_PKEY; +typedef ... EVP_PKEY; typedef ... EVP_PKEY_CTX; static const int EVP_PKEY_RSA; static const int EVP_PKEY_DSA; @@ -122,6 +119,8 @@ int EVP_PKEY_add1_attr_by_txt(EVP_PKEY *, const char *, int, int EVP_PKEY_cmp(const EVP_PKEY *, const EVP_PKEY *); EVP_PKEY *EVP_PKCS82PKEY(PKCS8_PRIV_KEY_INFO *); + +int Cryptography_EVP_PKEY_id(const EVP_PKEY *); """ MACROS = """ @@ -230,4 +229,13 @@ int (*EVP_PKEY_assign_EC_KEY)(EVP_PKEY *, EC_KEY *) = NULL; EC_KEY *(*EVP_PKEY_get1_EC_KEY)(EVP_PKEY *) = NULL; int (*EVP_PKEY_set1_EC_KEY)(EVP_PKEY *, EC_KEY *) = NULL; #endif +/* EVP_PKEY_id is not available on 0.9.8 so we'll define our own. This can + be removed when we remove 0.9.8 support. */ +int Cryptography_EVP_PKEY_id(const EVP_PKEY *key) { + #if OPENSSL_VERSION_NUMBER >= 0x10000000L + return EVP_PKEY_id(key); + #else + return key->type; + #endif +} """ diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 3c615e87..c21d5427 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -648,7 +648,21 @@ class _PasswordUserdata(object): self.exception = None +@binding.ffi_callback("int (char *, int, int, void *)", + name="Cryptography_pem_password_cb") def _pem_password_cb(buf, size, writing, userdata_handle): + """ + 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. + + The userdata pointer must point to a cffi handle of a + _PasswordUserdata instance. + """ ud = _ffi.from_handle(userdata_handle) ud.called += 1 @@ -1049,7 +1063,7 @@ class Backend(object): return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_char_p) - def _create_mem_bio(self): + def _create_mem_bio_gc(self): """ Creates an empty memory BIO. """ @@ -1077,7 +1091,7 @@ class Backend(object): pointer. """ - key_type = evp_pkey.type + key_type = self._lib.Cryptography_EVP_PKEY_id(evp_pkey) if key_type == self._lib.EVP_PKEY_RSA: rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) @@ -1104,7 +1118,7 @@ class Backend(object): pointer. """ - key_type = evp_pkey.type + key_type = self._lib.Cryptography_EVP_PKEY_id(evp_pkey) if key_type == self._lib.EVP_PKEY_RSA: rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) @@ -1143,13 +1157,7 @@ class Backend(object): # globally. The backend is passed in as userdata argument. userdata = _PasswordUserdata(password=password) - - pem_password_cb = self._ffi.callback( - "int (char *, int, int, void *)", - _pem_password_cb, - ) - - return pem_password_cb, userdata + return _pem_password_cb, userdata def _mgf1_hash_supported(self, algorithm): if self._lib.Cryptography_HAS_MGF1_MD: @@ -2132,19 +2140,20 @@ class Backend(object): else: raise ValueError("Unsupported encryption type") + key_type = self._lib.Cryptography_EVP_PKEY_id(evp_pkey) if encoding is serialization.Encoding.PEM: if format is serialization.PrivateFormat.PKCS8: write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey key = evp_pkey else: assert format is serialization.PrivateFormat.TraditionalOpenSSL - if evp_pkey.type == self._lib.EVP_PKEY_RSA: + if key_type == self._lib.EVP_PKEY_RSA: write_bio = self._lib.PEM_write_bio_RSAPrivateKey - elif evp_pkey.type == self._lib.EVP_PKEY_DSA: + elif key_type == self._lib.EVP_PKEY_DSA: write_bio = self._lib.PEM_write_bio_DSAPrivateKey else: assert self._lib.Cryptography_HAS_EC == 1 - assert evp_pkey.type == self._lib.EVP_PKEY_EC + assert key_type == self._lib.EVP_PKEY_EC write_bio = self._lib.PEM_write_bio_ECPrivateKey key = cdata @@ -2158,9 +2167,7 @@ class Backend(object): "traditional OpenSSL keys" ) - return self._private_key_bytes_traditional_der( - evp_pkey.type, cdata - ) + return self._private_key_bytes_traditional_der(key_type, cdata) else: assert format is serialization.PrivateFormat.PKCS8 write_bio = self._lib.i2d_PKCS8PrivateKey_bio @@ -2168,7 +2175,7 @@ class Backend(object): else: raise TypeError("encoding must be an item from the Encoding enum") - bio = self._create_mem_bio() + bio = self._create_mem_bio_gc() res = write_bio( bio, key, @@ -2191,7 +2198,7 @@ class Backend(object): self.openssl_assert(key_type == self._lib.EVP_PKEY_DSA) write_bio = self._lib.i2d_DSAPrivateKey_bio - bio = self._create_mem_bio() + bio = self._create_mem_bio_gc() res = write_bio(bio, cdata) self.openssl_assert(res == 1) return self._read_mem_bio(bio) @@ -2210,7 +2217,9 @@ class Backend(object): key = evp_pkey elif format is serialization.PublicFormat.PKCS1: # Only RSA is supported here. - assert evp_pkey.type == self._lib.EVP_PKEY_RSA + assert self._lib.Cryptography_EVP_PKEY_id( + evp_pkey + ) == self._lib.EVP_PKEY_RSA if encoding is serialization.Encoding.PEM: write_bio = self._lib.PEM_write_bio_RSAPublicKey else: @@ -2223,7 +2232,7 @@ class Backend(object): "format must be an item from the PublicFormat enum" ) - bio = self._create_mem_bio() + bio = self._create_mem_bio_gc() res = write_bio(bio, key) self.openssl_assert(res == 1) return self._read_mem_bio(bio) diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index b8614e0b..76920867 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -353,7 +353,7 @@ class _Certificate(object): return self._backend._ffi.buffer(pp[0], res)[:] def public_bytes(self, encoding): - bio = self._backend._create_mem_bio() + bio = self._backend._create_mem_bio_gc() if encoding is serialization.Encoding.PEM: res = self._backend._lib.PEM_write_bio_X509(bio, self._x509) elif encoding is serialization.Encoding.DER: @@ -827,7 +827,7 @@ class _CertificateRevocationList(object): def fingerprint(self, algorithm): h = hashes.Hash(algorithm, self._backend) - bio = self._backend._create_mem_bio() + bio = self._backend._create_mem_bio_gc() res = self._backend._lib.i2d_X509_CRL_bio( bio, self._x509_crl ) @@ -880,7 +880,7 @@ class _CertificateRevocationList(object): return self._backend._ffi.buffer(pp[0], res)[:] def public_bytes(self, encoding): - bio = self._backend._create_mem_bio() + bio = self._backend._create_mem_bio_gc() if encoding is serialization.Encoding.PEM: res = self._backend._lib.PEM_write_bio_X509_CRL( bio, self._x509_crl @@ -975,7 +975,7 @@ class _CertificateSigningRequest(object): return _CSR_EXTENSION_PARSER.parse(self._backend, x509_exts) def public_bytes(self, encoding): - bio = self._backend._create_mem_bio() + bio = self._backend._create_mem_bio_gc() if encoding is serialization.Encoding.PEM: res = self._backend._lib.PEM_write_bio_X509_REQ( bio, self._x509_req diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index 8e419439..1cfe8162 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -14,7 +14,6 @@ from cryptography.exceptions import InternalError from cryptography.hazmat.bindings._openssl import ffi, lib from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES - _OpenSSLError = collections.namedtuple("_OpenSSLError", ["code", "lib", "func", "reason"]) @@ -45,7 +44,28 @@ def _openssl_assert(lib, ok): ) -@ffi.callback("int (*)(unsigned char *, int)", error=-1) +def ffi_callback(signature, name, **kwargs): + """Callback dispatcher + + The ffi_callback() dispatcher keeps callbacks compatible between dynamic + and static callbacks. + """ + def wrapper(func): + if lib.Cryptography_STATIC_CALLBACKS: + # def_extern() returns a decorator that sets the internal + # function pointer and returns the original function unmodified. + ffi.def_extern(name=name, **kwargs)(func) + callback = getattr(lib, name) + else: + # callback() wraps the function in a cdata function. + callback = ffi.callback(signature, **kwargs)(func) + return callback + return wrapper + + +@ffi_callback("int (*)(unsigned char *, int)", + name="Cryptography_rand_bytes", + error=-1) def _osrandom_rand_bytes(buf, size): signed = ffi.cast("char *", buf) result = os.urandom(size) @@ -53,7 +73,7 @@ def _osrandom_rand_bytes(buf, size): return 1 -@ffi.callback("int (*)(void)") +@ffi_callback("int (*)(void)", name="Cryptography_rand_status") def _osrandom_rand_status(): return 1 @@ -88,7 +108,8 @@ class Binding(object): _osrandom_engine_name = ffi.new("const char[]", b"osrandom_engine") _osrandom_method = ffi.new( "RAND_METHOD *", - dict(bytes=_osrandom_rand_bytes, pseudorand=_osrandom_rand_bytes, + dict(bytes=_osrandom_rand_bytes, + pseudorand=_osrandom_rand_bytes, status=_osrandom_rand_status) ) @@ -140,10 +161,11 @@ class Binding(object): cls._ensure_ffi_initialized() if not cls._lock_cb_handle: - cls._lock_cb_handle = cls.ffi.callback( + wrapper = ffi_callback( "void(int, int, const char *, int)", - cls._lock_cb + name="Cryptography_locking_cb", ) + cls._lock_cb_handle = wrapper(cls._lock_cb) # Use Python's implementation if available, importing _ssl triggers # the setup for this. |