Merge pull request #2646 from reaperhulk/static-callbacks

Static callbacks
diff --git a/src/_cffi_src/openssl/evp.py b/src/_cffi_src/openssl/evp.py
index 6d17cb7..1d37b81 100644
--- a/src/_cffi_src/openssl/evp.py
+++ b/src/_cffi_src/openssl/evp.py
@@ -21,10 +21,7 @@
     ...;
 } 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_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 @@
 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 9dfe2d4..c21d542 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -1036,10 +1036,14 @@
 
         return _RSAPublicKey(self, rsa_cdata, evp_pkey)
 
-    def _rsa_cdata_to_evp_pkey(self, rsa_cdata):
+    def _create_evp_pkey_gc(self):
         evp_pkey = self._lib.EVP_PKEY_new()
         self.openssl_assert(evp_pkey != self._ffi.NULL)
         evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+        return evp_pkey
+
+    def _rsa_cdata_to_evp_pkey(self, rsa_cdata):
+        evp_pkey = self._create_evp_pkey_gc()
         res = self._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata)
         self.openssl_assert(res == 1)
         return evp_pkey
@@ -1059,7 +1063,7 @@
 
         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.
         """
@@ -1087,7 +1091,7 @@
         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)
@@ -1114,7 +1118,7 @@
         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)
@@ -1257,9 +1261,7 @@
         return _DSAParameters(self, dsa_cdata)
 
     def _dsa_cdata_to_evp_pkey(self, dsa_cdata):
-        evp_pkey = self._lib.EVP_PKEY_new()
-        self.openssl_assert(evp_pkey != self._ffi.NULL)
-        evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+        evp_pkey = self._create_evp_pkey_gc()
         res = self._lib.EVP_PKEY_set1_DSA(evp_pkey, dsa_cdata)
         self.openssl_assert(res == 1)
         return evp_pkey
@@ -1984,9 +1986,7 @@
         )
 
     def _ec_cdata_to_evp_pkey(self, ec_cdata):
-        evp_pkey = self._lib.EVP_PKEY_new()
-        self.openssl_assert(evp_pkey != self._ffi.NULL)
-        evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+        evp_pkey = self._create_evp_pkey_gc()
         res = self._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, ec_cdata)
         self.openssl_assert(res == 1)
         return evp_pkey
@@ -2140,19 +2140,20 @@
         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
@@ -2166,9 +2167,7 @@
                         "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
@@ -2176,7 +2175,7 @@
         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,
@@ -2199,7 +2198,7 @@
             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)
@@ -2218,7 +2217,9 @@
             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:
@@ -2231,7 +2232,7 @@
                 "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 b8614e0..7692086 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -353,7 +353,7 @@
         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 @@
 
     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 @@
         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 @@
         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/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index ad2daf7..e055568 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -10,8 +10,6 @@
 import sys
 import textwrap
 
-import pretend
-
 import pytest
 
 from cryptography import utils, x509
@@ -621,7 +619,7 @@
         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):