[pypy-commit] pypy cffi-libs: add _ssl and _cffi_ssl from py3.6

mattip pypy.commits at gmail.com
Sat May 11 19:11:43 EDT 2019


Author: Matti Picus <matti.picus at gmail.com>
Branch: cffi-libs
Changeset: r96599:b7f0f92d6f30
Date: 2019-05-10 06:09 -0700
http://bitbucket.org/pypy/pypy/changeset/b7f0f92d6f30/

Log:	add _ssl and _cffi_ssl from py3.6

diff too long, truncating to 2000 out of 3406 lines

diff --git a/lib_pypy/_cffi_ssl/LICENSE b/lib_pypy/_cffi_ssl/LICENSE
new file mode 100644
--- /dev/null
+++ b/lib_pypy/_cffi_ssl/LICENSE
@@ -0,0 +1,26 @@
+
+Except when otherwise stated (look for LICENSE files in directories or
+information at the beginning of each file) all software and
+documentation is licensed as follows: 
+
+    The MIT License
+
+    Permission is hereby granted, free of charge, to any person 
+    obtaining a copy of this software and associated documentation 
+    files (the "Software"), to deal in the Software without 
+    restriction, including without limitation the rights to use, 
+    copy, modify, merge, publish, distribute, sublicense, and/or 
+    sell copies of the Software, and to permit persons to whom the 
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included 
+    in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+    DEALINGS IN THE SOFTWARE.
+
diff --git a/lib_pypy/_cffi_ssl/README.md b/lib_pypy/_cffi_ssl/README.md
new file mode 100644
--- /dev/null
+++ b/lib_pypy/_cffi_ssl/README.md
@@ -0,0 +1,35 @@
+# PyPy's SSL module
+
+All of the CFFI code is copied from cryptography, wich patches contributed
+back to cryptography. PyPy vendors it's own copy of the cffi backend thus
+it renames the compiled shared object to _pypy_openssl.so (which means
+that cryptography can ship their own cffi backend)
+
+NOTE: currently, we have the following changes:
+
+* ``_cffi_src/openssl/callbacks.py`` to not rely on the CPython C API
+  (this change is now backported)
+
+* ``_cffi_src/utils.py`` for issue #2575 (29c9a89359e4)
+
+* ``_cffi_src/openssl/x509_vfy.py`` for issue #2605 (ca4d0c90f5a1)
+
+* ``_cffi_src/openssl/pypy_win32_extra.py`` for Win32-only functionality like ssl.enum_certificates()
+
+
+# Tests?
+
+Currently this module is tested using CPython's standard library test suite.
+
+# Install it into PyPy's source tree
+
+Copy over all the sources into the folder `lib_pypy/_cffi_ssl/*`. Updating the cffi backend can be simply done by the following command::
+
+    $ cp -r <cloned cryptography folder>/src/_cffi_src/* .
+
+NOTE: you need to keep our version of ``_cffi_src/openssl/callbacks.py``
+for now!
+
+# Crpytography version
+
+Copied over release version `1.7.2`
diff --git a/lib_pypy/_cffi_ssl/__init__.py b/lib_pypy/_cffi_ssl/__init__.py
new file mode 100644
diff --git a/lib_pypy/_cffi_ssl/_stdssl/__init__.py b/lib_pypy/_cffi_ssl/_stdssl/__init__.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/_cffi_ssl/_stdssl/__init__.py
@@ -0,0 +1,1566 @@
+import sys
+import time
+import _thread
+import socket
+import weakref
+from _pypy_openssl import ffi
+from _pypy_openssl import lib
+from _cffi_ssl._stdssl.certificate import (_test_decode_cert,
+    _decode_certificate, _certificate_to_der)
+from _cffi_ssl._stdssl.utility import (_str_with_len, _bytes_with_len,
+    _str_to_ffi_buffer, _str_from_buf, _cstr_decode_fs)
+from _cffi_ssl._stdssl.error import (ssl_error, pyssl_error,
+        SSLError, SSLZeroReturnError, SSLWantReadError,
+        SSLWantWriteError, SSLSyscallError,
+        SSLEOFError)
+from _cffi_ssl._stdssl.error import (SSL_ERROR_NONE,
+        SSL_ERROR_SSL, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
+        SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_SYSCALL,
+        SSL_ERROR_ZERO_RETURN, SSL_ERROR_WANT_CONNECT,
+        SSL_ERROR_EOF, SSL_ERROR_NO_SOCKET, SSL_ERROR_INVALID_ERROR_CODE,
+        pyerr_write_unraisable)
+from _cffi_ssl._stdssl import error
+from select import select
+from enum import IntEnum as _IntEnum
+
+if sys.platform == 'win32':
+    from _cffi_ssl._stdssl.win32_extra import enum_certificates, enum_crls
+    HAVE_POLL = False
+else:
+    from select import poll, POLLIN, POLLOUT
+    HAVE_POLL = True
+
+OPENSSL_VERSION = ffi.string(lib.OPENSSL_VERSION_TEXT).decode('utf-8')
+OPENSSL_VERSION_NUMBER = lib.OPENSSL_VERSION_NUMBER
+ver = OPENSSL_VERSION_NUMBER
+ver, status = divmod(ver, 16)
+ver, patch  = divmod(ver, 256)
+ver, fix    = divmod(ver, 256)
+ver, minor  = divmod(ver, 256)
+ver, major  = divmod(ver, 256)
+version_info = (major, minor, fix, patch, status)
+OPENSSL_VERSION_INFO = version_info
+_OPENSSL_API_VERSION = version_info
+del ver, version_info, status, patch, fix, minor, major
+
+HAS_ECDH = bool(lib.Cryptography_HAS_ECDH)
+HAS_SNI = bool(lib.Cryptography_HAS_TLSEXT_HOSTNAME)
+HAS_ALPN = bool(lib.Cryptography_HAS_ALPN)
+HAS_NPN = bool(lib.OPENSSL_NPN_NEGOTIATED)
+HAS_TLS_UNIQUE = True
+
+CLIENT = 0
+SERVER = 1
+
+VERIFY_DEFAULT = 0
+VERIFY_CRL_CHECK_LEAF = lib.X509_V_FLAG_CRL_CHECK
+VERIFY_CRL_CHECK_CHAIN = lib.X509_V_FLAG_CRL_CHECK | lib.X509_V_FLAG_CRL_CHECK_ALL
+VERIFY_X509_STRICT = lib.X509_V_FLAG_X509_STRICT
+if lib.Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST:
+    VERIFY_X509_TRUSTED_FIRST = lib.X509_V_FLAG_TRUSTED_FIRST
+
+CERT_NONE = 0
+CERT_OPTIONAL = 1
+CERT_REQUIRED = 2
+
+for name in dir(lib):
+    if name.startswith('SSL_OP'):
+        value = getattr(lib, name)
+        if value != 0:
+            globals()[name[4:]] = getattr(lib, name)
+
+OP_ALL = lib.SSL_OP_ALL & ~lib.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+
+SSL_CLIENT = 0
+SSL_SERVER = 1
+
+SSL_CB_MAXLEN=128
+
+if lib.Cryptography_HAS_SSL2:
+    PROTOCOL_SSLv2  = 0
+PROTOCOL_SSLv3  = 1
+PROTOCOL_SSLv23 = 2
+PROTOCOL_TLS    = PROTOCOL_SSLv23
+PROTOCOL_TLSv1    = 3
+if lib.Cryptography_HAS_TLSv1_2:
+    PROTOCOL_TLSv1 = 3
+    PROTOCOL_TLSv1_1 = 4
+    PROTOCOL_TLSv1_2 = 5
+PROTOCOL_TLS_CLIENT = 0x10
+PROTOCOL_TLS_SERVER = 0x11
+
+_PROTOCOL_NAMES = (name for name in dir(lib) if name.startswith('PROTOCOL_'))
+
+_IntEnum._convert('_SSLMethod', __name__,
+        lambda name: name.startswith('PROTOCOL_'))
+
+if HAS_TLS_UNIQUE:
+    CHANNEL_BINDING_TYPES = ['tls-unique']
+else:
+    CHANNEL_BINDING_TYPES = []
+
+for name in error.SSL_AD_NAMES:
+    lib_attr = 'SSL_AD_' + name
+    attr = 'ALERT_DESCRIPTION_' + name
+    if hasattr(lib, lib_attr):
+        globals()[attr] = getattr(lib, lib_attr)
+
+# init open ssl
+lib.SSL_load_error_strings()
+lib.SSL_library_init()
+lib._setup_ssl_threads()
+lib.OpenSSL_add_all_algorithms()
+
+def check_signals():
+    # nothing to do, we are on python level, signals are
+    # checked frequently in the bytecode dispatch loop
+    pass
+
+def _socket_timeout(s):
+    if s is None:
+        return 0.0
+    t = s.gettimeout()
+    if t is None:
+        return -1.0
+    return t
+
+class PasswordInfo(object):
+    callable = None
+    password = None
+    operationerror = None
+    handle = None
+PWINFO_STORAGE = {}
+
+def _Cryptography_pem_password_cb(buf, size, rwflag, userdata):
+    pw_info = ffi.from_handle(userdata)
+
+    password = pw_info.password
+
+    if pw_info.callable:
+        try:
+            password = pw_info.callable()
+        except Exception as e:
+            pw_info.operationerror = e
+            return 0
+
+        if not isinstance(password, (str, bytes, bytearray)):
+            pw_info.operationerror = TypeError("password callback must return a string")
+            return 0
+
+    password = _str_to_ffi_buffer(password)
+
+    if (len(password) > size):
+        pw_info.operationerror = ValueError("password cannot be longer than %d bytes" % size)
+        return 0
+
+    ffi.memmove(buf, password, len(password))
+    return len(password)
+
+if lib.Cryptography_STATIC_CALLBACKS:
+    ffi.def_extern(_Cryptography_pem_password_cb)
+    Cryptography_pem_password_cb = lib.Cryptography_pem_password_cb
+else:
+    Cryptography_pem_password_cb = ffi.callback("int(char*,int,int,void*)")(_Cryptography_pem_password_cb)
+
+if hasattr(time, 'monotonic'):
+    def _monotonic_clock():
+        return time.monotonic()
+else:
+    def _monotonic_clock():
+        return time.clock_gettime(time.CLOCK_MONOTONIC)
+
+def _ssl_select(sock, writing, timeout):
+    if HAVE_POLL:
+        p = poll()
+
+    # Nothing to do unless we're in timeout mode (not non-blocking)
+    if sock is None or timeout == 0:
+        return SOCKET_IS_NONBLOCKING
+    elif timeout < 0:
+        t = _socket_timeout(sock)
+        if t > 0:
+            return SOCKET_HAS_TIMED_OUT
+        else:
+            return SOCKET_IS_BLOCKING
+
+    # Guard against closed socket
+    if sock.fileno() < 0:
+        return SOCKET_HAS_BEEN_CLOSED
+
+    # Prefer poll, if available, since you can poll() any fd
+    # which can't be done with select().
+    if HAVE_POLL:
+        p.register(sock.fileno(), POLLOUT if writing else POLLIN)
+
+        rc = len(p.poll(timeout * 1000.0))
+    else:
+        # currently disabled, see HAVE_POLL
+        fd = sock.fileno()
+        #if (!_PyIsSelectable_fd(s->sock_fd))
+        #    return SOCKET_TOO_LARGE_FOR_SELECT;
+        if writing:
+            rr, wr, xr = select([],[fd],[], timeout)
+        else:
+            rr, wr, xr = select([fd],[],[], timeout)
+        rc = len(rr) + len(wr)
+    if rc != 0:
+        return SOCKET_OPERATION_OK
+    return SOCKET_HAS_TIMED_OUT
+
+SOCKET_IS_NONBLOCKING = 0
+SOCKET_IS_BLOCKING = 1
+SOCKET_HAS_TIMED_OUT = 2
+SOCKET_HAS_BEEN_CLOSED = 3
+SOCKET_TOO_LARGE_FOR_SELECT = 4
+SOCKET_OPERATION_OK = 5
+
+class _SSLSocket(object):
+
+    @staticmethod
+    def _new__ssl_socket(sslctx, sock, socket_type, server_hostname, inbio, outbio):
+        self = _SSLSocket(sslctx)
+        ctx = sslctx.ctx
+
+        if server_hostname:
+            self.server_hostname = server_hostname.decode('idna', 'strict')
+
+        lib.ERR_clear_error()
+        self.ssl = ssl = ffi.gc(lib.SSL_new(ctx), lib.SSL_free)
+
+        self._app_data_handle = ffi.new_handle(self)
+        lib.SSL_set_app_data(ssl, ffi.cast("char*", self._app_data_handle))
+        if sock:
+            lib.SSL_set_fd(ssl, sock.fileno())
+        else:
+            # BIOs are reference counted and SSL_set_bio borrows our reference.
+            # To prevent a double free in memory_bio_dealloc() we need to take an
+            # extra reference here.
+            lib.BIO_up_ref(inbio.bio);
+            lib.BIO_up_ref(outbio.bio);
+            lib.SSL_set_bio(self.ssl, inbio.bio, outbio.bio)
+
+        mode = lib.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
+        if lib.SSL_MODE_AUTO_RETRY:
+            mode |= lib.SSL_MODE_AUTO_RETRY
+        lib.SSL_set_mode(ssl, mode)
+
+        if HAS_SNI and self.server_hostname:
+            name = _str_to_ffi_buffer(self.server_hostname)
+            lib.SSL_set_tlsext_host_name(ssl, name)
+
+
+        # If the socket is in non-blocking mode or timeout mode, set the BIO
+        # to non-blocking mode (blocking is the default)
+        #
+        timeout = _socket_timeout(sock)
+        if sock and timeout >= 0:
+            lib.BIO_set_nbio(lib.SSL_get_rbio(ssl), 1)
+            lib.BIO_set_nbio(lib.SSL_get_wbio(ssl), 1)
+
+        if socket_type == SSL_CLIENT:
+            lib.SSL_set_connect_state(ssl)
+        else:
+            lib.SSL_set_accept_state(ssl)
+        self.socket_type = socket_type
+
+        if sock:
+            self.socket = weakref.ref(sock)
+
+        return self
+
+    def __init__(self, sslctx):
+        self.ctx = sslctx
+        self.peer_cert = ffi.NULL
+        self.ssl = ffi.NULL
+        self.shutdown_seen_zero = 0
+        self.handshake_done = 0
+        self._owner = None
+        self.server_hostname = None
+        self.socket = None
+
+    @property
+    def owner(self):
+        if self._owner is None:
+            return None
+        return self._owner()
+
+    @owner.setter
+    def owner(self, value):
+        if value is None:
+            self._owner = None
+        self._owner = weakref.ref(value)
+
+    @property
+    def context(self):
+        return self.ctx
+
+    @context.setter
+    def context(self, value):
+        if isinstance(value, _SSLContext):
+            if not HAS_SNI:
+                raise NotImplementedError("setting a socket's "
+                        "context is not supported by your OpenSSL library")
+            self.ctx = value
+            lib.SSL_set_SSL_CTX(self.ssl, self.ctx.ctx);
+        else:
+            raise TypeError("The value must be a SSLContext")
+
+    @property
+    def server_side(self):
+        return self.socket_type == SSL_SERVER
+
+    def do_handshake(self):
+        sock = self.get_socket_or_connection_gone()
+        ssl = self.ssl
+        timeout = _socket_timeout(sock)
+        if sock:
+            nonblocking = timeout >= 0
+            lib.BIO_set_nbio(lib.SSL_get_rbio(ssl), nonblocking)
+            lib.BIO_set_nbio(lib.SSL_get_wbio(ssl), nonblocking)
+
+        has_timeout = timeout > 0
+        deadline = -1
+        if has_timeout:
+            deadline = _monotonic_clock() + timeout;
+        # Actually negotiate SSL connection
+        # XXX If SSL_do_handshake() returns 0, it's also a failure.
+        while True:
+            # allow threads
+            ret = lib.SSL_do_handshake(ssl)
+            err = lib.SSL_get_error(ssl, ret)
+            # end allow threads
+
+            check_signals()
+
+            if has_timeout:
+                # REIVIEW monotonic clock?
+                timeout = deadline - _monotonic_clock()
+
+            if err == SSL_ERROR_WANT_READ:
+                sockstate = _ssl_select(sock, 0, timeout)
+            elif err == SSL_ERROR_WANT_WRITE:
+                sockstate = _ssl_select(sock, 1, timeout)
+            else:
+                sockstate = SOCKET_OPERATION_OK
+
+            if sockstate == SOCKET_HAS_TIMED_OUT:
+                raise socket.timeout("The handshake operation timed out")
+            elif sockstate == SOCKET_HAS_BEEN_CLOSED:
+                raise SSLError("Underlying socket has been closed.")
+            elif sockstate == SOCKET_TOO_LARGE_FOR_SELECT:
+                raise SSLError("Underlying socket too large for select().")
+            elif sockstate == SOCKET_IS_NONBLOCKING:
+                break
+            if not (err == SSL_ERROR_WANT_READ or err == SSL_ERROR_WANT_WRITE):
+                break
+        if ret < 1:
+            raise pyssl_error(self, ret)
+
+        peer_cert = lib.SSL_get_peer_certificate(ssl)
+        if peer_cert != ffi.NULL:
+            peer_cert = ffi.gc(peer_cert, lib.X509_free)
+        self.peer_cert = peer_cert
+
+        self.handshake_done = 1
+        return None
+
+    def peer_certificate(self, binary_mode):
+        if not self.handshake_done:
+            raise ValueError("handshake not done yet")
+        if self.peer_cert == ffi.NULL:
+            return None
+
+        if binary_mode:
+            # return cert in DER-encoded format
+            return _certificate_to_der(self.peer_cert)
+        else:
+            verification = lib.SSL_CTX_get_verify_mode(lib.SSL_get_SSL_CTX(self.ssl))
+            if (verification & lib.SSL_VERIFY_PEER) == 0:
+                return {}
+            else:
+                return _decode_certificate(self.peer_cert)
+
+    def write(self, bytestring):
+        deadline = 0
+        b = _str_to_ffi_buffer(bytestring)
+        sock = self.get_socket_or_connection_gone()
+        ssl = self.ssl
+
+        if len(b) > sys.maxsize:
+            raise OverflowError("string longer than %d bytes" % sys.maxsize)
+
+        timeout = _socket_timeout(sock)
+        if sock:
+            nonblocking = timeout >= 0
+            lib.BIO_set_nbio(lib.SSL_get_rbio(ssl), nonblocking)
+            lib.BIO_set_nbio(lib.SSL_get_wbio(ssl), nonblocking)
+
+
+        has_timeout = timeout > 0
+        if has_timeout:
+            deadline = _monotonic_clock() + timeout
+
+        sockstate = _ssl_select(sock, 1, timeout)
+        if sockstate == SOCKET_HAS_TIMED_OUT:
+            raise socket.timeout("The write operation timed out")
+        elif sockstate == SOCKET_HAS_BEEN_CLOSED:
+            raise ssl_error("Underlying socket has been closed.")
+        elif sockstate == SOCKET_TOO_LARGE_FOR_SELECT:
+            raise ssl_error("Underlying socket too large for select().")
+
+        while True:
+            length = lib.SSL_write(self.ssl, b, len(b))
+            err = lib.SSL_get_error(self.ssl, length)
+
+            check_signals()
+
+            if has_timeout:
+                timeout = deadline - _monotonic_clock()
+
+            if err == SSL_ERROR_WANT_READ:
+                sockstate = _ssl_select(sock, 0, timeout)
+            elif err == SSL_ERROR_WANT_WRITE:
+                sockstate = _ssl_select(sock, 1, timeout)
+            else:
+                sockstate = SOCKET_OPERATION_OK
+
+            if sockstate == SOCKET_HAS_TIMED_OUT:
+                raise socket.timeout("The write operation timed out")
+            elif sockstate == SOCKET_HAS_BEEN_CLOSED:
+                raise ssl_error("Underlying socket has been closed.")
+            elif sockstate == SOCKET_IS_NONBLOCKING:
+                break
+            if not (err == SSL_ERROR_WANT_READ or err == SSL_ERROR_WANT_WRITE):
+                break
+
+        if length > 0:
+            return length
+        else:
+            raise pyssl_error(self, length)
+
+    def read(self, length, buffer_into=None):
+        ssl = self.ssl
+
+        if length < 0 and buffer_into is None:
+            raise ValueError("size should not be negative")
+
+        sock = self.get_socket_or_connection_gone()
+
+        if buffer_into is None:
+            dest = ffi.new("char[]", length)
+            if length == 0:
+                return b""
+            mem = dest
+        else:
+            mem = ffi.from_buffer(buffer_into)
+            if length <= 0 or length > len(buffer_into):
+                length = len(buffer_into)
+                if length > sys.maxsize:
+                    raise OverflowError("maximum length can't fit in a C 'int'")
+                if len(buffer_into) == 0:
+                    return 0
+
+        if sock:
+            timeout = _socket_timeout(sock)
+            nonblocking = timeout >= 0
+            lib.BIO_set_nbio(lib.SSL_get_rbio(ssl), nonblocking)
+            lib.BIO_set_nbio(lib.SSL_get_wbio(ssl), nonblocking)
+
+        deadline = 0
+        timeout = _socket_timeout(sock)
+        has_timeout = timeout > 0
+        if has_timeout:
+            deadline = _monotonic_clock() + timeout
+
+        shutdown = False
+        while True:
+            count = lib.SSL_read(self.ssl, mem, length);
+            err = lib.SSL_get_error(self.ssl, count);
+
+            check_signals()
+
+            if has_timeout:
+                timeout = deadline - _monotonic_clock()
+
+            if err == SSL_ERROR_WANT_READ:
+                sockstate = _ssl_select(sock, 0, timeout)
+            elif err == SSL_ERROR_WANT_WRITE:
+                sockstate = _ssl_select(sock, 1, timeout)
+            elif err == SSL_ERROR_ZERO_RETURN and \
+                 lib.SSL_get_shutdown(self.ssl) == lib.SSL_RECEIVED_SHUTDOWN:
+                shutdown = True
+                break;
+            else:
+                sockstate = SOCKET_OPERATION_OK
+
+            if sockstate == SOCKET_HAS_TIMED_OUT:
+                raise socket.timeout("The read operation timed out")
+            elif sockstate == SOCKET_IS_NONBLOCKING:
+                break
+            if not (err == SSL_ERROR_WANT_READ or err == SSL_ERROR_WANT_WRITE):
+                break
+
+        if count <= 0 and not shutdown:
+            raise pyssl_error(self, count)
+
+        if not buffer_into:
+            return _bytes_with_len(dest, count)
+
+        return count
+
+    if HAS_ALPN:
+        def selected_alpn_protocol(self):
+            out = ffi.new("const unsigned char **")
+            outlen = ffi.new("unsigned int*")
+
+            lib.SSL_get0_alpn_selected(self.ssl, out, outlen);
+            if out[0] == ffi.NULL:
+                return None
+            return _str_with_len(out[0], outlen[0]);
+
+    def shared_ciphers(self):
+        ciphers = lib.SSL_get_ciphers(self.ssl)
+        if ciphers == ffi.NULL:
+            return None
+        res = []
+        count = lib.sk_SSL_CIPHER_num(ciphers)
+        for i in range(count):
+            tup = cipher_to_tuple(lib.sk_SSL_CIPHER_value(ciphers, i))
+            if not tup:
+                return None
+            res.append(tup)
+        return res
+
+    def cipher(self):
+        if self.ssl == ffi.NULL:
+            return None
+        current = lib.SSL_get_current_cipher(self.ssl)
+        if current == ffi.NULL:
+            return None
+        return cipher_to_tuple(current)
+
+    def compression(self):
+        if not lib.Cryptography_HAS_COMPRESSION or self.ssl == ffi.NULL:
+            return None
+
+        comp_method = lib.SSL_get_current_compression(self.ssl);
+        if comp_method == ffi.NULL: # or lib.SSL_COMP_get_type(comp_method) == lib.NID_undef:
+            return None
+        short_name = lib.SSL_COMP_get_name(comp_method)
+        if short_name == ffi.NULL:
+            return None
+        return _cstr_decode_fs(short_name)
+
+    def version(self):
+        if self.ssl == ffi.NULL:
+            return None
+        version = _str_from_buf(lib.SSL_get_version(self.ssl))
+        if version == "unknown":
+            return None
+        return version
+
+    def get_socket_or_None(self):
+        if self.socket is None:
+            return None
+        return self.socket()
+
+    def get_socket_or_connection_gone(self):
+        """ There are three states:
+            1) self.socket is None (In C that would mean: self->Socket == NULL)
+            2) self.socket() is None (-> The socket is gone)
+            3) self.socket() is not None
+            This method returns True if there is not weakref object allocated
+        """
+        if self.socket is None:
+            return None
+        sock = self.socket()
+        if not sock:
+            raise ssl_error("Underlying socket connection gone", SSL_ERROR_NO_SOCKET)
+        return sock
+
+    def shutdown(self):
+        sock = self.get_socket_or_None()
+        nonblocking = False
+        ssl = self.ssl
+
+        if self.socket is not None:
+            # Guard against closed socket
+            sock = self.socket()
+            if sock is None or sock.fileno() < 0:
+                raise ssl_error("Underlying socket connection gone", SSL_ERROR_NO_SOCKET)
+
+            timeout = _socket_timeout(sock)
+            nonblocking = timeout >= 0
+            if sock and timeout >= 0:
+                lib.BIO_set_nbio(lib.SSL_get_rbio(ssl), nonblocking)
+                lib.BIO_set_nbio(lib.SSL_get_wbio(ssl), nonblocking)
+        else:
+            timeout = 0
+
+        has_timeout = (timeout > 0);
+        if has_timeout:
+            deadline = _monotonic_clock() + timeout;
+
+        zeros = 0
+
+        while True:
+            # Disable read-ahead so that unwrap can work correctly.
+            # Otherwise OpenSSL might read in too much data,
+            # eating clear text data that happens to be
+            # transmitted after the SSL shutdown.
+            # Should be safe to call repeatedly every time this
+            # function is used and the shutdown_seen_zero != 0
+            # condition is met.
+            #
+            if self.shutdown_seen_zero:
+                lib.SSL_set_read_ahead(self.ssl, 0)
+            err = lib.SSL_shutdown(self.ssl)
+
+            # If err == 1, a secure shutdown with SSL_shutdown() is complete
+            if err > 0:
+                break
+            if err == 0:
+                # Don't loop endlessly; instead preserve legacy
+                #   behaviour of trying SSL_shutdown() only twice.
+                #   This looks necessary for OpenSSL < 0.9.8m
+                zeros += 1
+                if zeros > 1:
+                    break
+                # Shutdown was sent, now try receiving
+                self.shutdown_seen_zero = 1
+                continue
+
+            if has_timeout:
+                timeout = deadline - _monotonic_clock()
+
+            # Possibly retry shutdown until timeout or failure
+            ssl_err = lib.SSL_get_error(self.ssl, err)
+            if ssl_err == SSL_ERROR_WANT_READ:
+                sockstate = _ssl_select(sock, 0, timeout)
+            elif ssl_err == SSL_ERROR_WANT_WRITE:
+                sockstate = _ssl_select(sock, 1, timeout)
+            else:
+                break
+
+            if sockstate == SOCKET_HAS_TIMED_OUT:
+                if ssl_err == SSL_ERROR_WANT_READ:
+                    raise socket.timeout("The read operation timed out")
+                else:
+                    raise socket.timeout("The write operation timed out")
+            elif sockstate == SOCKET_TOO_LARGE_FOR_SELECT:
+                raise ssl_error("Underlying socket too large for select().")
+            elif sockstate != SOCKET_OPERATION_OK:
+                # Retain the SSL error code
+                break;
+
+        if err < 0:
+            raise pyssl_error(self, err)
+        if sock:
+            return sock
+        else:
+            return None
+
+    def pending(self):
+        count = lib.SSL_pending(self.ssl)
+        if count < 0:
+            raise pyssl_error(self, count)
+        else:
+            return count
+
+    def tls_unique_cb(self):
+        buf = ffi.new("char[]", SSL_CB_MAXLEN)
+
+        if lib.SSL_session_reused(self.ssl) ^ (not self.server_side):
+            # if session is resumed XOR we are the client
+            length = lib.SSL_get_finished(self.ssl, buf, SSL_CB_MAXLEN)
+        else:
+            # if a new session XOR we are the server
+            length = lib.SSL_get_peer_finished(self.ssl, buf, SSL_CB_MAXLEN)
+
+        # It cannot be negative in current OpenSSL version as of July 2011
+        if length == 0:
+            return None
+
+        return _bytes_with_len(buf, length)
+
+    if HAS_NPN:
+        def selected_npn_protocol(self):
+            out = ffi.new("unsigned char**")
+            outlen = ffi.new("unsigned int*")
+            lib.SSL_get0_next_proto_negotiated(self.ssl, out, outlen)
+            if (out[0] == ffi.NULL):
+                return None
+            return _str_with_len(out[0], outlen[0])
+
+    @property
+    def session(self):
+        "Get / set SSLSession."
+        return SSLSession(self)
+
+    @session.setter
+    def session(self, value):
+        if not isinstance(value, SSLSession):
+            raise TypeError("Value is not a SSLSession.")
+        if self.ctx.ctx != value._ctx.ctx:
+            raise ValueError("Session refers to a different SSLContext.")
+        if self.socket_type != SSL_CLIENT:
+            raise ValueError("Cannot set session for server-side SSLSocket.")
+        if self.handshake_done:
+            raise ValueError("Cannot set session after handshake.")
+        if not lib.SSL_set_session(self.ssl, value._session):
+            raise pyssl_error(self, 0)
+
+    @property
+    def session_reused(self):
+        "Was the client session reused during handshake?"
+        return bool(lib.SSL_session_reused(self.ssl))
+
+
+def _fs_decode(name):
+    return name.decode(sys.getfilesystemencoding())
+def _fs_converter(name):
+    """ name must not be None """
+    if isinstance(name, str):
+        return name.encode(sys.getfilesystemencoding())
+    return bytes(name)
+
+
+def cipher_to_tuple(cipher):
+    ccipher_name = lib.SSL_CIPHER_get_name(cipher)
+    if ccipher_name == ffi.NULL:
+        cipher_name = None
+    else:
+        cipher_name = _str_from_buf(ccipher_name)
+
+    ccipher_protocol = lib.SSL_CIPHER_get_version(cipher)
+    if ccipher_protocol == ffi.NULL:
+        cipher_protocol = None
+    else:
+        cipher_protocol = _str_from_buf(ccipher_protocol)
+
+    bits = lib.SSL_CIPHER_get_bits(cipher, ffi.NULL)
+    return (cipher_name, cipher_protocol, bits)
+
+
+class SSLSession(object):
+    def __new__(cls, ssl):
+        self = object.__new__(cls)
+        session = lib.SSL_get1_session(ssl.ssl)
+        if not session:
+            return None
+        self._session = ffi.gc(session, lib.SSL_SESSION_free)
+        self._ctx = ssl.ctx
+        return self
+
+    def __eq__(self, other):
+        if not isinstance(other, SSLSession):
+            return NotImplemented;
+        return self.id == other.id
+
+    @property
+    def id(self):
+        lenp = ffi.new("unsigned int*")
+        id = lib.SSL_SESSION_get_id(self._session, lenp)
+        return ffi.unpack(id, lenp[0])
+
+    @property
+    def time(self):
+        return lib.SSL_SESSION_get_time(self._session)
+
+    @property
+    def timeout(self):
+        return lib.SSL_SESSION_get_timeout(self._session)
+
+    @property
+    def has_ticket(self):
+        return bool(lib.SSL_SESSION_has_ticket(self._session))
+
+    @property
+    def ticket_lifetime_hint(self):
+        return lib.SSL_SESSION_get_ticket_lifetime_hint(self._session)
+
+
+SSL_CTX_STATS_NAMES = """
+    number connect connect_good connect_renegotiate accept accept_good
+    accept_renegotiate hits misses timeouts cache_full""".split()
+SSL_CTX_STATS = []
+for name in SSL_CTX_STATS_NAMES:
+    attr = 'SSL_CTX_sess_'+name
+    assert hasattr(lib, attr)
+    SSL_CTX_STATS.append((name, getattr(lib, attr)))
+
+class _SSLContext(object):
+    __slots__ = ('ctx', '_check_hostname', 'servername_callback',
+                 'alpn_protocols', '_alpn_protocols_handle',
+                 'npn_protocols', 'set_hostname',
+                 '_set_hostname_handle', '_npn_protocols_handle')
+
+    def __new__(cls, protocol):
+        self = object.__new__(cls)
+        self.ctx = ffi.NULL
+        if protocol == PROTOCOL_TLSv1:
+            method = lib.TLSv1_method()
+        elif lib.Cryptography_HAS_TLSv1_2 and protocol == PROTOCOL_TLSv1_1:
+            method = lib.TLSv1_1_method()
+        elif lib.Cryptography_HAS_TLSv1_2 and protocol == PROTOCOL_TLSv1_2 :
+            method = lib.TLSv1_2_method()
+        elif protocol == PROTOCOL_SSLv3 and lib.Cryptography_HAS_SSL3_METHOD:
+            method = lib.SSLv3_method()
+        elif lib.Cryptography_HAS_SSL2 and protocol == PROTOCOL_SSLv2:
+            method = lib.SSLv2_method()
+        elif protocol == PROTOCOL_SSLv23:
+            method = lib.SSLv23_method()
+        elif protocol == PROTOCOL_TLS_CLIENT:
+            method = lib.SSLv23_client_method()
+        elif protocol == PROTOCOL_TLS_SERVER:
+            method = lib.SSLv23_server_method()
+        else:
+            raise ValueError("invalid protocol version")
+
+        ctx = lib.SSL_CTX_new(method)
+        if ctx == ffi.NULL:
+            raise ssl_error("failed to allocate SSL context")
+        self.ctx = ffi.gc(lib.SSL_CTX_new(method), lib.SSL_CTX_free)
+
+        # Don't check host name by default
+        self._check_hostname = False
+        if protocol == PROTOCOL_TLS_CLIENT:
+            self._check_hostname = True
+            self.verify_mode = CERT_REQUIRED
+        else:
+            self._check_hostname = False
+            self.verify_mode = CERT_NONE
+
+        # Defaults
+        options = lib.SSL_OP_ALL & ~lib.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+        if not lib.Cryptography_HAS_SSL2 or protocol != PROTOCOL_SSLv2:
+            options |= lib.SSL_OP_NO_SSLv2
+        if protocol != PROTOCOL_SSLv3:
+            options |= lib.SSL_OP_NO_SSLv3
+        # Minimal security flags for server and client side context.
+        # Client sockets ignore server-side parameters.
+        options |= lib.SSL_OP_NO_COMPRESSION
+        options |= lib.SSL_OP_CIPHER_SERVER_PREFERENCE
+        options |= lib.SSL_OP_SINGLE_DH_USE
+        options |= lib.SSL_OP_SINGLE_ECDH_USE
+        lib.SSL_CTX_set_options(self.ctx, options)
+        lib.SSL_CTX_set_session_id_context(self.ctx, b"Python", len(b"Python"))
+
+        # A bare minimum cipher list without completely broken cipher suites.
+        # It's far from perfect but gives users a better head start.
+        if lib.Cryptography_HAS_SSL2 and protocol == PROTOCOL_SSLv2:
+            # SSLv2 needs MD5
+            default_ciphers = b"HIGH:!aNULL:!eNULL"
+        else:
+            default_ciphers = b"HIGH:!aNULL:!eNULL:!MD5"
+        if not lib.SSL_CTX_set_cipher_list(ctx, default_ciphers):
+            lib.ERR_clear_error()
+            raise SSLError("No cipher can be selected.")
+
+        if HAS_ECDH:
+            # Allow automatic ECDH curve selection (on
+            # OpenSSL 1.0.2+), or use prime256v1 by default.
+            # This is Apache mod_ssl's initialization
+            # policy, so we should be safe.
+            if lib.Cryptography_HAS_SET_ECDH_AUTO:
+                lib.SSL_CTX_set_ecdh_auto(self.ctx, 1)
+            else:
+                key = lib.EC_KEY_new_by_curve_name(lib.NID_X9_62_prime256v1)
+                lib.SSL_CTX_set_tmp_ecdh(self.ctx, key)
+                lib.EC_KEY_free(key)
+        if lib.Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST:
+            store = lib.SSL_CTX_get_cert_store(self.ctx)
+            lib.X509_STORE_set_flags(store, lib.X509_V_FLAG_TRUSTED_FIRST)
+        return self
+
+    @property
+    def options(self):
+        return lib.SSL_CTX_get_options(self.ctx)
+
+    @options.setter
+    def options(self, value):
+        new_opts = int(value)
+        opts = lib.SSL_CTX_get_options(self.ctx)
+        clear = opts & ~new_opts
+        set = ~opts & new_opts
+        if clear:
+            if lib.Cryptography_HAS_SSL_CTX_CLEAR_OPTIONS:
+                lib.SSL_CTX_clear_options(self.ctx, clear)
+            else:
+                raise ValueError("can't clear options before OpenSSL 0.9.8m")
+        if set:
+            lib.SSL_CTX_set_options(self.ctx, set)
+
+    @property
+    def verify_mode(self):
+        mode = lib.SSL_CTX_get_verify_mode(self.ctx)
+        if mode == lib.SSL_VERIFY_NONE:
+            return CERT_NONE
+        elif mode == lib.SSL_VERIFY_PEER:
+            return CERT_OPTIONAL
+        elif mode == lib.SSL_VERIFY_PEER | lib.SSL_VERIFY_FAIL_IF_NO_PEER_CERT:
+            return CERT_REQUIRED
+        raise ssl_error("invalid return value from SSL_CTX_get_verify_mode")
+
+    @verify_mode.setter
+    def verify_mode(self, value):
+        n = int(value)
+        if n == CERT_NONE:
+            mode = lib.SSL_VERIFY_NONE
+        elif n == CERT_OPTIONAL:
+            mode = lib.SSL_VERIFY_PEER
+        elif n == CERT_REQUIRED:
+            mode = lib.SSL_VERIFY_PEER | lib.SSL_VERIFY_FAIL_IF_NO_PEER_CERT
+        else:
+            raise ValueError("invalid value for verify_mode")
+        if mode == lib.SSL_VERIFY_NONE and self.check_hostname:
+            raise ValueError("Cannot set verify_mode to CERT_NONE when " \
+                             "check_hostname is enabled.")
+        # Keep current verify cb
+        verify_cb = lib.SSL_CTX_get_verify_callback(self.ctx)
+        lib.SSL_CTX_set_verify(self.ctx, mode, verify_cb)
+
+    @property
+    def verify_flags(self):
+        store = lib.SSL_CTX_get_cert_store(self.ctx)
+        param = lib.X509_STORE_get0_param(store)
+        flags = lib.X509_VERIFY_PARAM_get_flags(param)
+        return int(flags)
+
+    @verify_flags.setter
+    def verify_flags(self, value):
+        new_flags = int(value)
+        store = lib.SSL_CTX_get_cert_store(self.ctx);
+        param = lib.X509_STORE_get0_param(store)
+        flags = lib.X509_VERIFY_PARAM_get_flags(param);
+        clear = flags & ~new_flags;
+        set = ~flags & new_flags;
+        if clear:
+            param = lib.X509_STORE_get0_param(store)
+            if not lib.X509_VERIFY_PARAM_clear_flags(param, clear):
+                raise ssl_error(None, 0)
+        if set:
+            param = lib.X509_STORE_get0_param(store)
+            if not lib.X509_VERIFY_PARAM_set_flags(param, set):
+                raise ssl_error(None, 0)
+
+    @property
+    def check_hostname(self):
+        return self._check_hostname
+
+    @check_hostname.setter
+    def check_hostname(self, value):
+        check_hostname = bool(value)
+        if check_hostname and lib.SSL_CTX_get_verify_mode(self.ctx) == lib.SSL_VERIFY_NONE:
+            raise ValueError("check_hostname needs a SSL context with either "
+                             "CERT_OPTIONAL or CERT_REQUIRED")
+        self._check_hostname = check_hostname
+
+    def set_ciphers(self, cipherlist):
+        cipherlistbuf = _str_to_ffi_buffer(cipherlist)
+        ret = lib.SSL_CTX_set_cipher_list(self.ctx, cipherlistbuf)
+        if ret == 0:
+            # Clearing the error queue is necessary on some OpenSSL
+            # versions, otherwise the error will be reported again
+            # when another SSL call is done.
+            lib.ERR_clear_error()
+            raise ssl_error("No cipher can be selected.")
+
+
+    def load_cert_chain(self, certfile, keyfile=None, password=None):
+        if keyfile is None:
+            keyfile = certfile
+        pw_info = PasswordInfo()
+        index = -1
+        if password is not None:
+
+            if callable(password):
+                pw_info.callable = password
+            else:
+                if isinstance(password, (str, bytes, bytearray)):
+                    pw_info.password = password
+                else:
+                    raise TypeError("password should be a string or callable")
+
+            pw_info.handle = ffi.new_handle(pw_info)
+            index = _thread.get_ident()
+            PWINFO_STORAGE[index] = pw_info
+            lib.SSL_CTX_set_default_passwd_cb(self.ctx, Cryptography_pem_password_cb)
+            lib.SSL_CTX_set_default_passwd_cb_userdata(self.ctx, pw_info.handle)
+
+        try:
+            ffi.errno = 0
+            certfilebuf = _str_to_ffi_buffer(certfile)
+            ret = lib.SSL_CTX_use_certificate_chain_file(self.ctx, certfilebuf)
+            if ret != 1:
+                if pw_info.operationerror:
+                    lib.ERR_clear_error()
+                    raise pw_info.operationerror
+                _errno = ffi.errno
+                if _errno:
+                    lib.ERR_clear_error()
+                    raise OSError(_errno, "Error")
+                else:
+                    raise ssl_error(None)
+
+            ffi.errno = 0
+            buf = _str_to_ffi_buffer(keyfile)
+            ret = lib.SSL_CTX_use_PrivateKey_file(self.ctx, buf,
+                                                  lib.SSL_FILETYPE_PEM)
+            if ret != 1:
+                if pw_info.operationerror:
+                    lib.ERR_clear_error()
+                    raise pw_info.operationerror
+                _errno = ffi.errno
+                if _errno:
+                    lib.ERR_clear_error()
+                    raise OSError(_errno, None)
+                else:
+                    raise ssl_error(None)
+
+            ret = lib.SSL_CTX_check_private_key(self.ctx)
+            if ret != 1:
+                raise ssl_error(None)
+        finally:
+            if index >= 0:
+                del PWINFO_STORAGE[index]
+            lib.SSL_CTX_set_default_passwd_cb(self.ctx, ffi.NULL)
+            lib.SSL_CTX_set_default_passwd_cb_userdata(self.ctx, ffi.NULL)
+
+
+    def _wrap_socket(self, sock, server_side, server_hostname=None):
+        if server_hostname:
+            server_hostname = server_hostname.encode('idna')
+        return _SSLSocket._new__ssl_socket(self, sock, server_side,
+                server_hostname, None, None)
+
+    def load_verify_locations(self, cafile=None, capath=None, cadata=None):
+        ffi.errno = 0
+        if cadata is None:
+            ca_file_type = -1
+        else:
+            if not isinstance(cadata, str):
+                ca_file_type = lib.SSL_FILETYPE_ASN1
+            else:
+                ca_file_type = lib.SSL_FILETYPE_PEM
+                try:
+                    cadata = cadata.encode('ascii')
+                except UnicodeEncodeError:
+                    raise TypeError("cadata should be a ASCII string or a bytes-like object")
+        if cafile is None and capath is None and cadata is None:
+            raise TypeError("cafile and capath cannot be both omitted")
+        # load from cadata
+        if cadata is not None:
+            buf = _str_to_ffi_buffer(cadata)
+            self._add_ca_certs(buf, len(buf), ca_file_type)
+
+        # load cafile or capath
+        if cafile or capath:
+            if cafile is None:
+                cafilebuf = ffi.NULL
+            else:
+                cafilebuf = _str_to_ffi_buffer(cafile)
+            if capath is None:
+                capathbuf = ffi.NULL
+            else:
+                capathbuf = _str_to_ffi_buffer(capath)
+            ret = lib.SSL_CTX_load_verify_locations(self.ctx, cafilebuf, capathbuf)
+            if ret != 1:
+                _errno = ffi.errno
+                if _errno:
+                    lib.ERR_clear_error()
+                    raise OSError(_errno, '')
+                else:
+                    raise ssl_error(None)
+
+    def _add_ca_certs(self, data, size, ca_file_type):
+        biobuf = lib.BIO_new_mem_buf(data, size)
+        if biobuf == ffi.NULL:
+            raise ssl_error("Can't allocate buffer")
+        try:
+            store = lib.SSL_CTX_get_cert_store(self.ctx)
+            loaded = 0
+            while True:
+                if ca_file_type == lib.SSL_FILETYPE_ASN1:
+                    cert = lib.d2i_X509_bio(biobuf, ffi.NULL)
+                else:
+                    cert = lib.PEM_read_bio_X509(biobuf, ffi.NULL, ffi.NULL, ffi.NULL)
+                if not cert:
+                    break
+                try:
+                    r = lib.X509_STORE_add_cert(store, cert)
+                finally:
+                    lib.X509_free(cert)
+                if not r:
+                    err = lib.ERR_peek_last_error()
+                    if (lib.ERR_GET_LIB(err) == lib.ERR_LIB_X509 and
+                        lib.ERR_GET_REASON(err) ==
+                        lib.X509_R_CERT_ALREADY_IN_HASH_TABLE):
+                        # cert already in hash table, not an error
+                        lib.ERR_clear_error()
+                    else:
+                        break
+                loaded += 1
+
+            err = lib.ERR_peek_last_error()
+            if (ca_file_type == lib.SSL_FILETYPE_ASN1 and
+                loaded > 0 and
+                lib.ERR_GET_LIB(err) == lib.ERR_LIB_ASN1 and
+                lib.ERR_GET_REASON(err) == lib.ASN1_R_HEADER_TOO_LONG):
+                # EOF ASN1 file, not an error
+                lib.ERR_clear_error()
+            elif (ca_file_type == lib.SSL_FILETYPE_PEM and
+                  loaded > 0 and
+                  lib.ERR_GET_LIB(err) == lib.ERR_LIB_PEM and
+                  lib.ERR_GET_REASON(err) == lib.PEM_R_NO_START_LINE):
+                # EOF PEM file, not an error
+                lib.ERR_clear_error()
+            else:
+                raise ssl_error(None)
+        finally:
+            lib.BIO_free(biobuf)
+
+    def cert_store_stats(self):
+        store = lib.SSL_CTX_get_cert_store(self.ctx)
+        x509 = 0
+        x509_ca = 0
+        crl = 0
+        objs = lib.X509_STORE_get0_objects(store)
+        count = lib.sk_X509_OBJECT_num(objs)
+        for i in range(count):
+            obj = lib.sk_X509_OBJECT_value(objs, i)
+            _type = lib.X509_OBJECT_get_type(obj)
+            if _type == lib.X509_LU_X509:
+                x509 += 1
+                cert = lib.X509_OBJECT_get0_X509(obj)
+                if lib.X509_check_ca(cert):
+                    x509_ca += 1
+            elif _type == lib.X509_LU_CRL:
+                crl += 1
+            else:
+                # Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY.
+                # As far as I can tell they are internal states and never
+                # stored in a cert store
+                pass
+        return {'x509': x509, 'x509_ca': x509_ca, 'crl': crl}
+
+
+    def session_stats(self):
+        stats = {}
+        for name, ssl_func in SSL_CTX_STATS:
+            stats[name] = ssl_func(self.ctx)
+        return stats
+
+    def set_default_verify_paths(self):
+        if not lib.SSL_CTX_set_default_verify_paths(self.ctx):
+            raise ssl_error("")
+
+    def load_dh_params(self, filepath):
+        ffi.errno = 0
+        if filepath is None:
+            raise TypeError("filepath must not be None")
+        buf = _fs_converter(filepath)
+        mode = ffi.new("char[]",b"r")
+        ffi.errno = 0
+        bio = lib.BIO_new_file(buf, mode)
+        if bio == ffi.NULL:
+            _errno = ffi.errno
+            lib.ERR_clear_error()
+            raise OSError(_errno, '')
+        try:
+            dh = lib.PEM_read_bio_DHparams(bio, ffi.NULL, ffi.NULL, ffi.NULL)
+        finally:
+            lib.BIO_free(bio)
+        if dh == ffi.NULL:
+            _errno = ffi.errno
+            if _errno != 0:
+                lib.ERR_clear_error()
+                raise OSError(_errno, '')
+            else:
+                raise ssl_error(None)
+        try:
+            if lib.SSL_CTX_set_tmp_dh(self.ctx, dh) == 0:
+                raise ssl_error(None)
+        finally:
+            lib.DH_free(dh)
+
+    def get_ca_certs(self, binary_form=None):
+        binary_mode = bool(binary_form)
+        _list = []
+        store = lib.SSL_CTX_get_cert_store(self.ctx)
+        objs = lib.X509_STORE_get0_objects(store)
+        count = lib.sk_X509_OBJECT_num(objs)
+        for i in range(count):
+            obj = lib.sk_X509_OBJECT_value(objs, i)
+            _type = lib.X509_OBJECT_get_type(obj)
+            if _type != lib.X509_LU_X509:
+                # not a x509 cert
+                continue
+            # CA for any purpose
+            cert = lib.X509_OBJECT_get0_X509(obj)
+            if not lib.X509_check_ca(cert):
+                continue
+            if binary_mode:
+                _list.append(_certificate_to_der(cert))
+            else:
+                _list.append(_decode_certificate(cert))
+        return _list
+
+    def set_ecdh_curve(self, name):
+        # needs to be zero terminated
+        if name is None:
+            raise TypeError()
+        buf = _fs_converter(name)
+        nid = lib.OBJ_sn2nid(buf)
+        if nid == 0:
+            raise ValueError("unknown elliptic curve name '%s'" % name)
+        key = lib.EC_KEY_new_by_curve_name(nid)
+        if not key:
+            raise ssl_error(None)
+        try:
+            lib.SSL_CTX_set_tmp_ecdh(self.ctx, key)
+        finally:
+            lib.EC_KEY_free(key)
+
+    def set_servername_callback(self, callback):
+        # cryptography constraint: OPENSSL_NO_TLSEXT will never be set!
+        if not HAS_SNI:
+            raise NotImplementedError("The TLS extension servername callback, "
+                    "SSL_CTX_set_tlsext_servername_callback, "
+                    "is not in the current OpenSSL library.")
+        if callback is None:
+            lib.SSL_CTX_set_tlsext_servername_callback(self.ctx, ffi.NULL)
+            self._set_hostname_handle = None
+            return
+        if not callable(callback):
+            raise TypeError("not a callable object")
+        scb = ServernameCallback(callback, self)
+        self._set_hostname_handle = ffi.new_handle(scb)
+        lib.SSL_CTX_set_tlsext_servername_callback(self.ctx, _servername_callback)
+        lib.SSL_CTX_set_tlsext_servername_arg(self.ctx, self._set_hostname_handle)
+
+    def _set_alpn_protocols(self, protos):
+        if HAS_ALPN:
+            self.alpn_protocols = protocols = ffi.from_buffer(protos)
+            length = len(protocols)
+
+            if lib.SSL_CTX_set_alpn_protos(self.ctx,ffi.cast("unsigned char*", protocols), length):
+                return MemoryError()
+            self._alpn_protocols_handle = handle = ffi.new_handle(self)
+            lib.SSL_CTX_set_alpn_select_cb(self.ctx, select_alpn_callback, handle)
+        else:
+            raise NotImplementedError("The ALPN extension requires OpenSSL 1.0.2 or later.")
+
+    def _set_npn_protocols(self, protos):
+        if HAS_NPN:
+            self.npn_protocols = ffi.from_buffer(protos)
+            handle = ffi.new_handle(self)
+            self._npn_protocols_handle = handle # track a reference to the handle
+            lib.SSL_CTX_set_next_protos_advertised_cb(self.ctx, advertise_npn_callback, handle)
+            lib.SSL_CTX_set_next_proto_select_cb(self.ctx, select_npn_callback, handle)
+        else:
+            raise NotImplementedError("The NPN extension requires OpenSSL 1.0.1 or later.")
+
+    def _wrap_bio(self, incoming, outgoing, server_side, server_hostname):
+        # server_hostname is either None (or absent), or to be encoded
+        # using the idna encoding.
+        hostname = None
+        if server_hostname is not None:
+            hostname = server_hostname.encode("idna")
+
+        sock = _SSLSocket._new__ssl_socket(self, None, server_side, hostname, incoming, outgoing)
+        return sock
+
+
+
+# cryptography constraint: OPENSSL_NO_TLSEXT will never be set!
+if HAS_SNI:
+    @ffi.callback("int(SSL*,int*,void*)")
+    def _servername_callback(s, al, arg):
+        scb = ffi.from_handle(arg)
+        ssl_ctx = scb.ctx
+        servername = lib.SSL_get_servername(s, lib.TLSEXT_NAMETYPE_host_name)
+        set_hostname = scb.callback
+        #ifdef WITH_THREAD
+            # TODO PyGILState_STATE gstate = PyGILState_Ensure();
+        #endif
+
+        if set_hostname is None:
+            #/* remove race condition in this the call back while if removing the
+            # * callback is in progress */
+            #ifdef WITH_THREAD
+                    # TODO PyGILState_Release(gstate);
+            #endif
+            return lib.SSL_TLSEXT_ERR_OK
+
+        ssl = ffi.from_handle(lib.SSL_get_app_data(s))
+        assert isinstance(ssl, _SSLSocket)
+
+        # The servername callback expects an argument that represents the current
+        # SSL connection and that has a .context attribute that can be changed to
+        # identify the requested hostname. Since the official API is the Python
+        # level API we want to pass the callback a Python level object rather than
+        # a _ssl.SSLSocket instance. If there's an "owner" (typically an
+        # SSLObject) that will be passed. Otherwise if there's a socket then that
+        # will be passed. If both do not exist only then the C-level object is
+        # passed.
+        ssl_socket = ssl.owner
+        if not ssl_socket:
+            ssl_socket = ssl.get_socket_or_None()
+
+        if ssl_socket is None:
+            al[0] = lib.SSL_AD_INTERNAL_ERROR
+            return lib.SSL_TLSEXT_ERR_ALERT_FATAL
+
+        if servername == ffi.NULL:
+            try:
+                result = set_hostname(ssl_socket, None, ssl_ctx)
+            except Exception as e:
+                pyerr_write_unraisable(e, set_hostname)
+                al[0] = lib.SSL_AD_HANDSHAKE_FAILURE
+                return lib.SSL_TLSEXT_ERR_ALERT_FATAL
+        else:
+            servername = ffi.string(servername)
+
+            try:
+                servername_idna = servername.decode("idna")
+            except UnicodeDecodeError as e:
+                pyerr_write_unraisable(e, servername)
+
+            try:
+                result = set_hostname(ssl_socket, servername_idna, ssl_ctx)
+            except Exception as e:
+                pyerr_write_unraisable(e, set_hostname)
+                al[0] = lib.SSL_AD_HANDSHAKE_FAILURE
+                return lib.SSL_TLSEXT_ERR_ALERT_FATAL
+
+        if result is not None:
+            # this is just a poor man's emulation:
+            # in CPython this works a bit different, it calls all the way
+            # down from PyLong_AsLong to _PyLong_FromNbInt which raises
+            # a TypeError if there is no nb_int slot filled.
+            try:
+                if isinstance(result, int):
+                    al[0] = result
+                else:
+                    if result is not None:
+                        if hasattr(result,'__int__'):
+                            al[0] = result.__int__()
+                            return lib.SSL_TLSEXT_ERR_ALERT_FATAL
+                    # needed because sys.exec_info is used in pyerr_write_unraisable
+                    raise TypeError("an integer is required (got type %s)" % result)
+            except TypeError as e:
+                pyerr_write_unraisable(e, result)
+                al[0] = lib.SSL_AD_INTERNAL_ERROR
+            return lib.SSL_TLSEXT_ERR_ALERT_FATAL
+        else:
+            # TODO gil state release?
+            return lib.SSL_TLSEXT_ERR_OK
+
+class ServernameCallback(object):
+    def __init__(self, callback, ctx):
+        self.callback = callback
+        self.ctx = ctx
+
+SERVERNAME_CALLBACKS = weakref.WeakValueDictionary()
+
+def _asn1obj2py(obj):
+    nid = lib.OBJ_obj2nid(obj)
+    if nid == lib.NID_undef:
+        raise ValueError("Unknown object")
+    sn = _str_from_buf(lib.OBJ_nid2sn(nid))
+    ln = _str_from_buf(lib.OBJ_nid2ln(nid))
+    buf = ffi.new("char[]", 255)
+    length = lib.OBJ_obj2txt(buf, len(buf), obj, 1)
+    if length < 0:
+        ssl_error(None)
+    if length > 0:
+        return (nid, sn, ln, _str_with_len(buf, length))
+    else:
+        return (nid, sn, ln, None)
+
+def txt2obj(txt, name):
+    _bytes = _str_to_ffi_buffer(txt)
+    is_name = 0 if name else 1
+    obj = lib.OBJ_txt2obj(_bytes, is_name)
+    if obj == ffi.NULL:
+        raise ValueError("unknown object '%s'" % txt)
+    result = _asn1obj2py(obj)
+    lib.ASN1_OBJECT_free(obj)
+    return result
+
+def nid2obj(nid):
+    if nid < lib.NID_undef:
+        raise ValueError("NID must be positive.")
+    obj = lib.OBJ_nid2obj(nid);
+    if obj == ffi.NULL:
+        raise ValueError("unknown NID %i" % nid)
+    result = _asn1obj2py(obj);
+    lib.ASN1_OBJECT_free(obj);
+    return result;
+
+
+class MemoryBIO(object):
+    def __init__(self):
+        bio = lib.BIO_new(lib.BIO_s_mem());
+        if bio == ffi.NULL:
+            raise ssl_error("failed to allocate BIO")
+
+        # Since our BIO is non-blocking an empty read() does not indicate EOF,
+        # just that no data is currently available. The SSL routines should retry
+        # the read, which we can achieve by calling BIO_set_retry_read().
+        lib.BIO_set_retry_read(bio);
+        lib.BIO_set_mem_eof_return(bio, -1);
+
+        self.bio = ffi.gc(bio, lib.BIO_free)
+        self.eof_written = False
+
+    @property
+    def eof(self):
+        """Whether the memory BIO is at EOF."""
+        return lib.BIO_ctrl_pending(self.bio) == 0 and self.eof_written
+
+    def write(self, strlike):
+        INT_MAX = 2**31-1
+        if isinstance(strlike, memoryview):
+            # FIXME pypy must support get_raw_address for
+            # StringBuffer to remove this case!
+            strlike = strlike.tobytes()
+        buf = ffi.from_buffer(strlike)
+        if len(buf) > INT_MAX:
+            raise OverflowError("string longer than %d bytes", INT_MAX)
+
+        if self.eof_written:
+            raise ssl_error("cannot write() after write_eof()")
+        nbytes = lib.BIO_write(self.bio, buf, len(buf));
+        if nbytes < 0:
+            raise ssl_error(None)
+        return nbytes
+
+    def write_eof(self):
+        self.eof_written = True
+        # After an EOF is written, a zero return from read() should be a real EOF
+        # i.e. it should not be retried. Clear the SHOULD_RETRY flag.
+        lib.BIO_clear_retry_flags(self.bio)
+        lib.BIO_set_mem_eof_return(self.bio, 0)
+
+    def read(self, len=-1):
+        count = len
+        avail = lib.BIO_ctrl_pending(self.bio);
+        if count < 0 or count > avail:
+            count = avail;
+
+        buf = ffi.new("char[]", count)
+
+        nbytes = lib.BIO_read(self.bio, buf, count);
+        #  There should never be any short reads but check anyway.
+        if nbytes < count:
+            return b""
+
+        return _bytes_with_len(buf, nbytes)
+
+    @property
+    def pending(self):
+        return lib.BIO_ctrl_pending(self.bio)
+
+
+def RAND_status():
+    return lib.RAND_status()
+
+def _RAND_bytes(count, pseudo):
+    if count < 0:
+        raise ValueError("num must be positive")
+    buf = ffi.new("unsigned char[]", count)
+    if pseudo:
+        # note by reaperhulk, RAND_pseudo_bytes is deprecated in 3.6 already,
+        # it is totally fine to just call RAND_bytes instead
+        ok = lib.RAND_bytes(buf, count)
+        if ok == 1 or ok == 0:
+            _bytes = _bytes_with_len(buf, count)
+            return (_bytes, ok == 1)
+    else:
+        ok = lib.RAND_bytes(buf, count)
+        if ok == 1 or (pseudo and ok == 0):
+            return _bytes_with_len(buf, count)
+    raise ssl_error(None, errcode=lib.ERR_get_error())
+
+def RAND_pseudo_bytes(count):
+    return _RAND_bytes(count, True)
+
+def RAND_bytes(count):
+    return _RAND_bytes(count, False)
+
+def RAND_add(view, entropy):
+    buf = _str_to_ffi_buffer(view)
+    lib.RAND_add(buf, len(buf), entropy)
+
+def get_default_verify_paths():
+
+    ofile_env = _cstr_decode_fs(lib.X509_get_default_cert_file_env())
+    if ofile_env is None:
+        return None
+    ofile = _cstr_decode_fs(lib.X509_get_default_cert_file())
+    if ofile is None:
+        return None
+    odir_env = _cstr_decode_fs(lib.X509_get_default_cert_dir_env())
+    if odir_env is None:
+        return None
+    odir = _cstr_decode_fs(lib.X509_get_default_cert_dir())
+    if odir is None:
+        return odir
+    return (ofile_env, ofile, odir_env, odir);
+
+ at ffi.callback("int(SSL*,unsigned char **,unsigned char *,const unsigned char *,unsigned int,void *)")
+def select_alpn_callback(ssl, out, outlen, client_protocols, client_protocols_len, args):
+    ctx = ffi.from_handle(args)
+    return do_protocol_selection(1, out, outlen,
+                                 ffi.cast("unsigned char*",ctx.alpn_protocols), len(ctx.alpn_protocols),
+                                 client_protocols, client_protocols_len)
+
+if lib.OPENSSL_NPN_NEGOTIATED:
+    @ffi.callback("int(SSL*,unsigned char **,unsigned char *,const unsigned char *,unsigned int,void *)")
+    def select_npn_callback(ssl, out, outlen, server_protocols, server_protocols_len, args):
+        ctx = ffi.from_handle(args)
+        return do_protocol_selection(0, out, outlen, server_protocols, server_protocols_len,
+                                     ffi.cast("unsigned char*",ctx.npn_protocols), len(ctx.npn_protocols))
+
+
+    @ffi.callback("int(SSL*,const unsigned char**, unsigned int*, void*)")
+    def advertise_npn_callback(ssl, data, length, args):
+        ctx = ffi.from_handle(args)
+
+        if not ctx.npn_protocols:
+            data[0] = ffi.new("unsigned char*", b"")
+            length[0] = 0
+        else:
+            data[0] = ffi.cast("unsigned char*",ctx.npn_protocols)
+            length[0] = len(ctx.npn_protocols)
+
+        return lib.SSL_TLSEXT_ERR_OK
+
+
+    def do_protocol_selection(alpn, out, outlen, server_protocols, server_protocols_len,
+                                                 client_protocols, client_protocols_len):
+        if client_protocols == ffi.NULL:
+            client_protocols = b""
+            client_protocols_len = 0
+        if server_protocols == ffi.NULL:
+            server_protocols = ""
+            server_protocols_len = 0
+
+        ret = lib.SSL_select_next_proto(out, outlen,
+                                        server_protocols, server_protocols_len,
+                                        client_protocols, client_protocols_len);
+        if alpn and ret != lib.OPENSSL_NPN_NEGOTIATED:
+            return lib.SSL_TLSEXT_ERR_NOACK
+
+        return lib.SSL_TLSEXT_ERR_OK
+
+if lib.Cryptography_HAS_EGD:
+    def RAND_egd(path):
+        bytecount = lib.RAND_egd_bytes(ffi.from_buffer(path), len(path))
+        if bytecount == -1:
+            raise SSLError("EGD connection failed or EGD did not return "
+                           "enough data to seed the PRNG");
+        return bytecount
+
diff --git a/lib_pypy/_cffi_ssl/_stdssl/certificate.py b/lib_pypy/_cffi_ssl/_stdssl/certificate.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/_cffi_ssl/_stdssl/certificate.py
@@ -0,0 +1,342 @@
+import warnings
+import base64
+import textwrap
+from _pypy_openssl import ffi
+from _pypy_openssl import lib
+from _cffi_ssl._stdssl.utility import _string_from_asn1, _str_with_len, _bytes_with_len
+from _cffi_ssl._stdssl.error import ssl_error, pyssl_error
+
+X509_NAME_MAXLEN = 256
+
+def _create_tuple_for_attribute(name, value):
+    buf = ffi.new("char[]", X509_NAME_MAXLEN)
+    length = lib.OBJ_obj2txt(buf, X509_NAME_MAXLEN, name, 0)
+    if length < 0:
+        raise ssl_error(None)
+    name = _str_with_len(buf, length)
+
+    buf_ptr = ffi.new("unsigned char**")
+    length = lib.ASN1_STRING_to_UTF8(buf_ptr, value)
+    if length < 0:
+        raise ssl_error(None)
+    try:
+        value = _str_with_len(buf_ptr[0], length)
+    finally:
+        lib.OPENSSL_free(buf_ptr[0])
+    return (name, value)
+
+def _get_aia_uri(certificate, nid):
+    info = lib.X509_get_ext_d2i(certificate, lib.NID_info_access, ffi.NULL, ffi.NULL)
+    if (info == ffi.NULL):
+        return None;
+    if lib.sk_ACCESS_DESCRIPTION_num(info) == 0:
+        lib.sk_ACCESS_DESCRIPTION_free(info)
+        return None
+
+    lst = []
+    count = lib.sk_ACCESS_DESCRIPTION_num(info)
+    for i in range(count):
+        ad = lib.sk_ACCESS_DESCRIPTION_value(info, i)
+
+        if lib.OBJ_obj2nid(ad.method) != nid or \
+           ad.location.type != lib.GEN_URI:
+            continue
+        uri = ad.location.d.uniformResourceIdentifier
+        ostr = _str_with_len(uri.data, uri.length)
+        lst.append(ostr)
+    lib.sk_ACCESS_DESCRIPTION_free(info)
+
+    # convert to tuple or None
+    if len(lst) == 0: return None
+    return tuple(lst)
+
+def _get_peer_alt_names(certificate):
+    # this code follows the procedure outlined in
+    # OpenSSL's crypto/x509v3/v3_prn.c:X509v3_EXT_print()
+    # function to extract the STACK_OF(GENERAL_NAME),
+    # then iterates through the stack to add the
+    # names.
+    peer_alt_names = []
+
+    if certificate == ffi.NULL:
+        return None
+
+    # get a memory buffer
+    biobuf = lib.BIO_new(lib.BIO_s_mem());
+
+    i = -1
+    while True:
+        i = lib.X509_get_ext_by_NID(certificate, lib.NID_subject_alt_name, i)
+        if i < 0:
+            break
+
+
+        # now decode the altName
+        ext = lib.X509_get_ext(certificate, i);
+        method = lib.X509V3_EXT_get(ext)
+        if method is ffi.NULL:
+            raise ssl_error("No method for internalizing subjectAltName!")
+
+        ext_data = lib.X509_EXTENSION_get_data(ext)
+        ext_data_len = ext_data.length
+        ext_data_value = ffi.new("unsigned char**", ffi.NULL)
+        ext_data_value[0] = ext_data.data
+
+        if method.it != ffi.NULL:
+            names = lib.ASN1_item_d2i(ffi.NULL, ext_data_value, ext_data_len, lib.ASN1_ITEM_ptr(method.it))
+        else:
+            names = method.d2i(ffi.NULL, ext_data_value, ext_data_len)
+
+        names = ffi.cast("GENERAL_NAMES*", names)
+        count = lib.sk_GENERAL_NAME_num(names)
+        for j in range(count):
+            # get a rendering of each name in the set of names
+            name = lib.sk_GENERAL_NAME_value(names, j);
+            _type = name.type
+            if _type == lib.GEN_DIRNAME:
+                # we special-case DirName as a tuple of
+                # tuples of attributes
+                v = _create_tuple_for_X509_NAME(name.d.dirn)
+                peer_alt_names.append(("DirName", v))
+            # GENERAL_NAME_print() doesn't handle NULL bytes in ASN1_string
+            # correctly, CVE-2013-4238
+            elif _type == lib.GEN_EMAIL:
+                v = _string_from_asn1(name.d.rfc822Name)
+                peer_alt_names.append(("email", v))
+            elif _type == lib.GEN_DNS:
+                v = _string_from_asn1(name.d.dNSName)
+                peer_alt_names.append(("DNS", v))
+            elif _type == lib.GEN_URI:
+                v = _string_from_asn1(name.d.uniformResourceIdentifier)
+                peer_alt_names.append(("URI", v))
+            elif _type == lib.GEN_RID:
+                v = "Registered ID"
+                buf = ffi.new("char[2048]")
+
+                length = lib.OBJ_obj2txt(buf, 2047, name.d.rid, 0)
+                if length < 0:
+                    # TODO _setSSLError(NULL, 0, __FILE__, __LINE__);
+                    raise NotImplementedError
+                elif length >= 2048:
+                    v = "<INVALID>"
+                else:
+                    v = _str_with_len(buf, length)
+                peer_alt_names.append(("Registered ID", v))
+            else:
+                # for everything else, we use the OpenSSL print form
+                if _type not in (lib.GEN_OTHERNAME, lib.GEN_X400, \
+                                 lib.GEN_EDIPARTY, lib.GEN_IPADD, lib.GEN_RID):
+                    warnings.warn("Unknown general type %d" % _type, RuntimeWarning)
+                    continue
+                lib.BIO_reset(biobuf);
+                lib.GENERAL_NAME_print(biobuf, name);
+                v = _bio_get_str(biobuf)
+                idx = v.find(":")
+                if idx == -1:
+                    raise ValueError("Invalid value %s", v)
+                peer_alt_names.append((v[:idx], v[idx+1:]))
+
+        free_func_addr = ffi.addressof(lib, "GENERAL_NAME_free")
+        lib.sk_GENERAL_NAME_pop_free(names, free_func_addr);
+    lib.BIO_free(biobuf)
+    if peer_alt_names is not None:
+        return tuple(peer_alt_names)
+    return peer_alt_names
+
+def _create_tuple_for_X509_NAME(xname):
+    dn = []
+    rdn = []
+    rdn_level = -1
+    entry_count = lib.X509_NAME_entry_count(xname);
+    for index_counter in range(entry_count):
+        entry = lib.X509_NAME_get_entry(xname, index_counter);
+
+        # check to see if we've gotten to a new RDN
+        _set = lib.Cryptography_X509_NAME_ENTRY_set(entry)
+        if rdn_level >= 0:
+            if rdn_level != _set:
+                dn.append(tuple(rdn))
+                rdn = []
+        rdn_level = _set
+
+        # now add this attribute to the current RDN
+        name = lib.X509_NAME_ENTRY_get_object(entry);
+        value = lib.X509_NAME_ENTRY_get_data(entry);
+        attr = _create_tuple_for_attribute(name, value);
+        if attr == ffi.NULL:
+            raise NotImplementedError
+        rdn.append(attr)
+
+    # now, there's typically a dangling RDN
+    if rdn and len(rdn) > 0:
+        dn.append(tuple(rdn))
+
+    return tuple(dn)
+
+def _bio_get_str(biobuf):
+    bio_buf = ffi.new("char[]", 2048)
+    length = lib.BIO_gets(biobuf, bio_buf, len(bio_buf)-1)
+    if length < 0:
+        if biobuf: lib.BIO_free(biobuf)
+        raise ssl_error(None)
+    return _str_with_len(bio_buf, length)
+
+def _decode_certificate(certificate):
+    retval = {}
+
+    peer = _create_tuple_for_X509_NAME(lib.X509_get_subject_name(certificate));
+    if not peer:
+        return None
+    retval["subject"] = peer
+
+    issuer = _create_tuple_for_X509_NAME(lib.X509_get_issuer_name(certificate));
+    if not issuer:
+        return None
+    retval["issuer"] = issuer
+
+    version = lib.X509_get_version(certificate) + 1
+    if version == 0:
+        return None
+    retval["version"] = version
+
+    try:
+        biobuf = lib.BIO_new(lib.BIO_s_mem());
+
+        lib.BIO_reset(biobuf);
+        serialNumber = lib.X509_get_serialNumber(certificate);
+        # should not exceed 20 octets, 160 bits, so buf is big enough
+        lib.i2a_ASN1_INTEGER(biobuf, serialNumber)
+        buf = ffi.new("char[]", 2048)
+        length = lib.BIO_gets(biobuf, buf, len(buf)-1)
+        if length < 0:
+            raise ssl_error(None)
+        retval["serialNumber"] = _str_with_len(buf, length)
+
+        lib.BIO_reset(biobuf);
+        notBefore = lib.X509_get_notBefore(certificate);
+        lib.ASN1_TIME_print(biobuf, notBefore);
+        length = lib.BIO_gets(biobuf, buf, len(buf)-1);
+        if length < 0:
+            raise ssl_error(None)
+        retval["notBefore"] = _str_with_len(buf, length)
+
+        lib.BIO_reset(biobuf);
+        notAfter = lib.X509_get_notAfter(certificate);
+        lib.ASN1_TIME_print(biobuf, notAfter);
+        length = lib.BIO_gets(biobuf, buf, len(buf)-1);
+        if length < 0:
+            raise ssl_error(None)
+        retval["notAfter"] = _str_with_len(buf, length)
+
+        # Now look for subjectAltName
+        peer_alt_names = _get_peer_alt_names(certificate);
+        if peer_alt_names is None:
+            return None
+        if len(peer_alt_names) > 0:
+            retval["subjectAltName"] = peer_alt_names
+
+        # Authority Information Access: OCSP URIs
+        obj = _get_aia_uri(certificate, lib.NID_ad_OCSP)
+        if obj:
+            retval["OCSP"] = obj
+
+        obj = _get_aia_uri(certificate, lib.NID_ad_ca_issuers)
+        if obj:
+            retval["caIssuers"] = obj
+
+        # CDP (CRL distribution points)
+        obj = _get_crl_dp(certificate)
+        if obj:
+            retval["crlDistributionPoints"] = obj
+    finally:
+        lib.BIO_free(biobuf)
+
+    return retval
+
+
+def _get_crl_dp(certificate):
+    if lib.OPENSSL_VERSION_NUMBER >= 0x10001000:
+        lib.X509_check_ca(certificate)
+    dps = lib.X509_get_ext_d2i(certificate, lib.NID_crl_distribution_points, ffi.NULL, ffi.NULL)
+    if dps is ffi.NULL:
+        return None
+
+    lst = []
+    count = lib.sk_DIST_POINT_num(dps)
+    for i in range(count):
+        dp = lib.sk_DIST_POINT_value(dps, i);
+        gns = dp.distpoint.name.fullname;
+
+        jcount = lib.sk_GENERAL_NAME_num(gns)
+        for j in range(jcount):
+            gn = lib.sk_GENERAL_NAME_value(gns, j)
+            if gn.type != lib.GEN_URI:
+                continue
+
+            uri = gn.d.uniformResourceIdentifier;
+            ouri = _str_with_len(uri.data, uri.length)
+            lst.append(ouri)
+
+    if lib.OPENSSL_VERSION_NUMBER < 0x10001000:
+        lib.sk_DIST_POINT_free(dps);
+
+    if len(lst) == 0: return None
+    return tuple(lst)
+
+def _test_decode_cert(path):
+    cert = lib.BIO_new(lib.BIO_s_file())
+    if cert is ffi.NULL:
+        lib.BIO_free(cert)
+        raise ssl_error("Can't malloc memory to read file")
+
+    epath = path.encode()
+    if lib.BIO_read_filename(cert, epath) <= 0:
+        lib.BIO_free(cert)
+        raise ssl_error("Can't open file")
+
+    x = lib.PEM_read_bio_X509_AUX(cert, ffi.NULL, ffi.NULL, ffi.NULL)
+    if x is ffi.NULL:
+        ssl_error("Error decoding PEM-encoded file")
+
+    retval = _decode_certificate(x)
+    lib.X509_free(x);
+
+    if cert != ffi.NULL:
+        lib.BIO_free(cert)
+    return retval
+
+PEM_HEADER = "-----BEGIN CERTIFICATE-----"
+PEM_FOOTER = "-----END CERTIFICATE-----"
+
+def PEM_cert_to_DER_cert(pem_cert_string):
+    """Takes a certificate in ASCII PEM format and returns the
+    DER-encoded version of it as a byte sequence"""
+
+    if not pem_cert_string.startswith(PEM_HEADER):
+        raise ValueError("Invalid PEM encoding; must start with %s"
+                         % PEM_HEADER)
+    if not pem_cert_string.strip().endswith(PEM_FOOTER):
+        raise ValueError("Invalid PEM encoding; must end with %s"
+                         % PEM_FOOTER)
+    d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)]
+    return base64.decodebytes(d.encode('ASCII', 'strict'))
+
+def DER_cert_to_PEM_cert(der_cert_bytes):
+    """Takes a certificate in binary DER format and returns the
+    PEM version of it as a string."""
+
+    f = str(base64.standard_b64encode(der_cert_bytes), 'ASCII', 'strict')
+    return (PEM_HEADER + '\n' +
+            textwrap.fill(f, 64) + '\n' +
+            PEM_FOOTER + '\n')
+
+def _certificate_to_der(certificate):
+    buf_ptr = ffi.new("unsigned char**")
+    buf_ptr[0] = ffi.NULL
+    length = lib.i2d_X509(certificate, buf_ptr)
+    if length < 0:
+        raise ssl_error(None)
+    try:
+        return _bytes_with_len(ffi.cast("char*",buf_ptr[0]), length)
+    finally:
+        lib.OPENSSL_free(buf_ptr[0])
diff --git a/lib_pypy/_cffi_ssl/_stdssl/error.py b/lib_pypy/_cffi_ssl/_stdssl/error.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/_cffi_ssl/_stdssl/error.py
@@ -0,0 +1,202 @@
+import sys
+import os
+import traceback
+from _pypy_openssl import ffi


More information about the pypy-commit mailing list