aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--setup.py4
-rw-r--r--src/_cffi_src/build_openssl.py1
-rw-r--r--src/_cffi_src/openssl/callbacks.py50
-rw-r--r--src/_cffi_src/openssl/evp.py16
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py49
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py8
-rw-r--r--src/cryptography/hazmat/bindings/openssl/binding.py34
-rw-r--r--tests/hazmat/backends/test_openssl.py4
8 files changed, 127 insertions, 39 deletions
diff --git a/setup.py b/setup.py
index 3b67e8ff..f79b0e25 100644
--- a/setup.py
+++ b/setup.py
@@ -54,8 +54,8 @@ if platform.python_implementation() == "PyPy":
"upgrade PyPy to use this library."
)
else:
- requirements.append("cffi>=1.1.0")
- setup_requirements.append("cffi>=1.1.0")
+ requirements.append("cffi>=1.4.1")
+ setup_requirements.append("cffi>=1.4.1")
# If you add a new dep here you probably need to add it in the tox.ini as well
test_requirements = [
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.
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index ad2daf7d..e0555686 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -10,8 +10,6 @@ import subprocess
import sys
import textwrap
-import pretend
-
import pytest
from cryptography import utils, x509
@@ -621,7 +619,7 @@ class TestOpenSSLSerializationWithOpenSSL(object):
assert backend._ffi.string(buf, len(password)) == password
def test_unsupported_evp_pkey_type(self):
- key = pretend.stub(type="unsupported")
+ key = backend._create_evp_pkey_gc()
with raises_unsupported_algorithm(None):
backend._evp_pkey_to_private_key(key)
with raises_unsupported_algorithm(None):