Author: Richard Plangger <planri...@gmail.com>
Branch: py3.5-ssl
Changeset: r88000:253ec1245b56
Date: 2016-10-31 16:00 +0100
http://bitbucket.org/pypy/pypy/changeset/253ec1245b56/

Log:    copy over the initial repo containing the api for the ssl stdlib

diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -74,6 +74,7 @@
 ^rpython/doc/_build/.*$
 ^compiled
 ^.git/
+.git/
 ^.hypothesis/
 ^release/
 ^rpython/_cache$
diff --git a/lib_pypy/openssl/.gitignore b/lib_pypy/openssl/.gitignore
new file mode 100644
--- /dev/null
+++ b/lib_pypy/openssl/.gitignore
@@ -0,0 +1,95 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# dotenv
+.env
+
+# virtualenv
+.venv/
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+
+# Rope project settings
+.ropeproject
+
+# Vim
+
+*.swp
+*.swo
diff --git a/lib_pypy/openssl/LICENSE b/lib_pypy/openssl/LICENSE
new file mode 100644
--- /dev/null
+++ b/lib_pypy/openssl/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/openssl/README.md b/lib_pypy/openssl/README.md
new file mode 100644
--- /dev/null
+++ b/lib_pypy/openssl/README.md
@@ -0,0 +1,4 @@
+# PyPy's SSL module
+
+Most of the CFFI code is copied from cryptography
+
diff --git a/lib_pypy/openssl/_cffi_src/openssl/ecdh.py 
b/lib_pypy/openssl/_cffi_src/openssl/ecdh.py
--- a/lib_pypy/openssl/_cffi_src/openssl/ecdh.py
+++ b/lib_pypy/openssl/_cffi_src/openssl/ecdh.py
@@ -12,6 +12,7 @@
 
 TYPES = """
 static const int Cryptography_HAS_ECDH;
+static const int Cryptography_HAS_ECDH_SET_CURVE;
 """
 
 FUNCTIONS = """
@@ -20,6 +21,7 @@
 MACROS = """
 int ECDH_compute_key(void *, size_t, const EC_POINT *, EC_KEY *,
                      void *(*)(const void *, size_t, void *, size_t *));
+int SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int onoff);
 """
 
 CUSTOMIZATIONS = """
@@ -33,4 +35,9 @@
 #else
 static const long Cryptography_HAS_ECDH = 1;
 #endif
+#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 || defined(LIBRESSL_VERSION_NUMBER)
+static const long Cryptography_HAS_ECDH_SET_CURVE = 0;
+#else
+static const long Cryptography_HAS_ECDH_SET_CURVE = 1;
+#endif
 """
diff --git a/lib_pypy/openssl/_cffi_src/openssl/rand.py 
b/lib_pypy/openssl/_cffi_src/openssl/rand.py
--- a/lib_pypy/openssl/_cffi_src/openssl/rand.py
+++ b/lib_pypy/openssl/_cffi_src/openssl/rand.py
@@ -20,6 +20,7 @@
 int RAND_load_file(const char *, long);
 int RAND_write_file(const char *);
 int RAND_bytes(unsigned char *, int);
+int RAND_pseudo_bytes(unsigned char *buf, int num);
 """
 
 MACROS = """
diff --git a/lib_pypy/openssl/_stdssl/__init__.py 
b/lib_pypy/openssl/_stdssl/__init__.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/openssl/_stdssl/__init__.py
@@ -0,0 +1,853 @@
+from _openssl import ffi
+from _openssl import lib
+
+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
+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 = False
+_HAS_TLS_UNIQUE = True
+
+CLIENT = 0
+SERVER = 1
+
+CERT_NONE = 0
+CERT_OPTIONAL = 1
+CERT_REQUIRED = 2
+
+for name in dir(lib):
+    if name.startswith('SSL_OP'):
+        globals()[name[4:]] = getattr(lib, name)
+
+def ssl_error(msg, errno=0, errtype=None, errcode=0):
+    reason_str = None
+    lib_str = None
+    if errcode:
+        err_lib = lib.ERR_GET_LIB(errcode)
+        err_reason = lib.ERR_GET_REASON(errcode)
+        reason_str = ERROR_CODES_TO_NAMES.get((err_lib, err_reason), None)
+        lib_str = LIBRARY_CODES_TO_NAMES.get(err_lib, None)
+        msg = ffi.string(lib.ERR_reason_error_string(errcode)).decode('utf-8')
+    if not msg:
+        msg = "unknown error"
+    if reason_str and lib_str:
+        msg = "[%s: %s] %s" % (lib_str, reason_str, msg)
+    elif lib_str:
+        msg = "[%s] %s" % (lib_str, msg)
+
+    raise Exception(msg)
+    #w_exception_class = w_errtype or get_error(space).w_error
+    #if errno or errcode:
+    #    w_exception = space.call_function(w_exception_class,
+    #                                      space.wrap(errno), space.wrap(msg))
+    #else:
+    #    w_exception = space.call_function(w_exception_class, space.wrap(msg))
+    #space.setattr(w_exception, space.wrap("reason"),
+    #              space.wrap(reason_str) if reason_str else space.w_None)
+    #space.setattr(w_exception, space.wrap("library"),
+    #              space.wrap(lib_str) if lib_str else space.w_None)
+    #return OperationError(w_exception_class, w_exception)
+
+PROTOCOL_SSLv2  = 0
+PROTOCOL_SSLv3  = 1
+PROTOCOL_SSLv23 = 2
+PROTOCOL_TLSv1    = 3
+if lib.Cryptography_HAS_TLSv1_2:
+    PROTOCOL_TLSv1 = 3
+    PROTOCOL_TLSv1_1 = 4
+    PROTOCOL_TLSv1_2 = 5
+
+_PROTOCOL_NAMES = (name for name in dir(lib) if name.startswith('PROTOCOL_'))
+
+from enum import Enum as _Enum, IntEnum as _IntEnum
+_IntEnum._convert('_SSLMethod', __name__,
+        lambda name: name.startswith('PROTOCOL_'))
+
+if _HAS_TLS_UNIQUE:
+    CHANNEL_BINDING_TYPES = ['tls-unique']
+else:
+    CHANNEL_BINDING_TYPES = []
+
+def _ssl_seterror(ss, ret):
+    assert ret <= 0
+
+    errcode = lib.ERR_peek_last_error()
+
+    if ss is None:
+        return ssl_error(None, errcode=errcode)
+    elif ss.ssl:
+        err = lib.SSL_get_error(ss.ssl, ret)
+    else:
+        err = SSL_ERROR_SSL
+    w_errtype = None
+    errstr = ""
+    errval = 0
+
+    if err == SSL_ERROR_ZERO_RETURN:
+        w_errtype = get_error(space).w_ZeroReturnError
+        errstr = "TLS/SSL connection has been closed"
+        errval = PY_SSL_ERROR_ZERO_RETURN
+    elif err == SSL_ERROR_WANT_READ:
+        w_errtype = get_error(space).w_WantReadError
+        errstr = "The operation did not complete (read)"
+        errval = PY_SSL_ERROR_WANT_READ
+    elif err == SSL_ERROR_WANT_WRITE:
+        w_errtype = get_error(space).w_WantWriteError
+        errstr = "The operation did not complete (write)"
+        errval = PY_SSL_ERROR_WANT_WRITE
+    elif err == SSL_ERROR_WANT_X509_LOOKUP:
+        errstr = "The operation did not complete (X509 lookup)"
+        errval = PY_SSL_ERROR_WANT_X509_LOOKUP
+    elif err == SSL_ERROR_WANT_CONNECT:
+        errstr = "The operation did not complete (connect)"
+        errval = PY_SSL_ERROR_WANT_CONNECT
+    elif err == SSL_ERROR_SYSCALL:
+        e = libssl_ERR_get_error()
+        if e == 0:
+            if ret == 0 or ss.w_socket() is None:
+                w_errtype = get_error(space).w_EOFError
+                errstr = "EOF occurred in violation of protocol"
+                errval = PY_SSL_ERROR_EOF
+            elif ret == -1:
+                # the underlying BIO reported an I/0 error
+                error = rsocket.last_error()
+                return interp_socket.converted_error(space, error)
+            else:
+                w_errtype = get_error(space).w_SyscallError
+                errstr = "Some I/O error occurred"
+                errval = PY_SSL_ERROR_SYSCALL
+        else:
+            errstr = rffi.charp2str(libssl_ERR_error_string(e, None))
+            errval = PY_SSL_ERROR_SYSCALL
+    elif err == SSL_ERROR_SSL:
+        errval = PY_SSL_ERROR_SSL
+        if errcode != 0:
+            errstr = rffi.charp2str(libssl_ERR_error_string(errcode, None))
+        else:
+            errstr = "A failure in the SSL library occurred"
+    else:
+        errstr = "Invalid error code"
+        errval = PY_SSL_ERROR_INVALID_ERROR_CODE
+
+    return ssl_error(space, errstr, errval, w_errtype=w_errtype,
+                     errcode=errcode)
+
+class SSLContext(object):
+    ctx = ffi.NULL
+
+    def __init__(self, protocol):
+        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 protocol == PROTOCOL_SSLv2 and lib.Cryptography_HAS_SSL2_METHOD:
+            method = lib.SSLv2_method()
+        elif protocol == PROTOCOL_SSLv23:
+            method = lib.SSLv23_method()
+        else:
+            raise ValueError("invalid protocol version")
+
+        self.ctx = lib.SSL_CTX_new(method)
+        if self.ctx is ffi.NULL:
+            raise ssl_error("failed to allocate SSL context")
+
+        self.check_hostname = False
+        # TODO self.register_finalizer(space)
+
+        # Defaults
+        lib.SSL_CTX_set_verify(self.ctx, lib.SSL_VERIFY_NONE, None)
+        options = lib.SSL_OP_ALL & ~lib.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+        if protocol != PROTOCOL_SSLv2:
+            options |= lib.SSL_OP_NO_SSLv2
+        if protocol != PROTOCOL_SSLv3:
+            options |= lib.SSL_OP_NO_SSLv3
+        lib.SSL_CTX_set_options(self.ctx, options)
+        lib.SSL_CTX_set_session_id_context(self.ctx, b"Python", len(b"Python"))
+
+        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_ECDH_SET_CURVE:
+                lib.SSL_CTX_set_ecdh_auto(self.ctx, 1)
+            else:
+                key = lib.EC_KEY_new_by_curve_name(lib.NID_X9_62_prime256v1)
+                if not key:
+                    # TODO copy from ropenssl?
+                    raise _ssl_seterror(None, 0)
+                try:
+                    lib.SSL_CTX_set_tmp_ecdh(self.ctx, key)
+                finally:
+                    lib.EC_KEY_free(key)
+
+#    def _finalize_(self):
+#        ctx = self.ctx
+#        if ctx:
+#            self.ctx = lltype.nullptr(SSL_CTX.TO)
+#            libssl_SSL_CTX_free(ctx)
+#
+#    @staticmethod
+#    @unwrap_spec(protocol=int)
+#    def descr_new(space, w_subtype, protocol=PY_SSL_VERSION_SSL23):
+#        self = space.allocate_instance(SSLContext, w_subtype)
+#        self.__init__(space, protocol)
+#        return space.wrap(self)
+#
+#    @unwrap_spec(cipherlist=str)
+#    def set_ciphers_w(self, space, cipherlist):
+#        ret = libssl_SSL_CTX_set_cipher_list(self.ctx, cipherlist)
+#        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.
+#            libssl_ERR_clear_error()
+#            raise ssl_error(space, "No cipher can be selected.")
+#
+#    @unwrap_spec(server_side=int)
+#    def wrap_socket_w(self, space, w_sock, server_side,
+#                      w_server_hostname=None):
+#        assert w_sock is not None
+#        # server_hostname is either None (or absent), or to be encoded
+#        # using the idna encoding.
+#        if space.is_none(w_server_hostname):
+#            hostname = None
+#        else:
+#            hostname = space.bytes_w(
+#                space.call_method(w_server_hostname,
+#                                  "encode", space.wrap("idna")))
+#
+#        if hostname and not HAS_SNI:
+#            raise oefmt(space.w_ValueError,
+#                        "server_hostname is not supported by your OpenSSL "
+#                        "library")
+#
+#        return new_sslobject(space, self, w_sock, server_side, hostname)
+#
+#    def session_stats_w(self, space):
+#        w_stats = space.newdict()
+#        for name, ssl_func in SSL_CTX_STATS:
+#            w_value = space.wrap(ssl_func(self.ctx))
+#            space.setitem_str(w_stats, name, w_value)
+#        return w_stats
+#
+#    def descr_set_default_verify_paths(self, space):
+#        if not libssl_SSL_CTX_set_default_verify_paths(self.ctx):
+#            raise ssl_error(space, "")
+#
+#    def descr_get_options(self, space):
+#        return space.newlong(libssl_SSL_CTX_get_options(self.ctx))
+#
+#    def descr_set_options(self, space, w_new_opts):
+#        new_opts = space.int_w(w_new_opts)
+#        opts = libssl_SSL_CTX_get_options(self.ctx)
+#        clear = opts & ~new_opts
+#        set = ~opts & new_opts
+#        if clear:
+#            if HAVE_SSL_CTX_CLEAR_OPTIONS:
+#                libssl_SSL_CTX_clear_options(self.ctx, clear)
+#            else:
+#                raise oefmt(space.w_ValueError,
+#                            "can't clear options before OpenSSL 0.9.8m")
+#        if set:
+#            libssl_SSL_CTX_set_options(self.ctx, set)
+#
+#    def descr_get_verify_mode(self, space):
+#        mode = libssl_SSL_CTX_get_verify_mode(self.ctx)
+#        if mode == SSL_VERIFY_NONE:
+#            return space.newlong(PY_SSL_CERT_NONE)
+#        elif mode == SSL_VERIFY_PEER:
+#            return space.newlong(PY_SSL_CERT_OPTIONAL)
+#        elif mode == SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT:
+#            return space.newlong(PY_SSL_CERT_REQUIRED)
+#        raise ssl_error(space, "invalid return value from 
SSL_CTX_get_verify_mode")
+#
+#    def descr_set_verify_mode(self, space, w_mode):
+#        n = space.int_w(w_mode)
+#        if n == PY_SSL_CERT_NONE:
+#            mode = SSL_VERIFY_NONE
+#        elif n == PY_SSL_CERT_OPTIONAL:
+#            mode = SSL_VERIFY_PEER
+#        elif n == PY_SSL_CERT_REQUIRED:
+#            mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT
+#        else:
+#            raise oefmt(space.w_ValueError,
+#                        "invalid value for verify_mode")
+#        if mode == SSL_VERIFY_NONE and self.check_hostname:
+#            raise oefmt(space.w_ValueError,
+#                        "Cannot set verify_mode to CERT_NONE when "
+#                        "check_hostname is enabled.")
+#        libssl_SSL_CTX_set_verify(self.ctx, mode, None)
+#
+#    def descr_get_verify_flags(self, space):
+#        store = libssl_SSL_CTX_get_cert_store(self.ctx)
+#        flags = libssl_X509_VERIFY_PARAM_get_flags(store[0].c_param)
+#        return space.wrap(flags)
+#
+#    def descr_set_verify_flags(self, space, w_obj):
+#        new_flags = space.int_w(w_obj)
+#        store = libssl_SSL_CTX_get_cert_store(self.ctx)
+#        flags = libssl_X509_VERIFY_PARAM_get_flags(store[0].c_param)
+#        flags_clear = flags & ~new_flags
+#        flags_set = ~flags & new_flags
+#        if flags_clear and not libssl_X509_VERIFY_PARAM_clear_flags(
+#                store[0].c_param, flags_clear):
+#            raise _ssl_seterror(space, None, 0)
+#        if flags_set and not libssl_X509_VERIFY_PARAM_set_flags(
+#                store[0].c_param, flags_set):
+#            raise _ssl_seterror(space, None, 0)
+#
+#    def descr_get_check_hostname(self, space):
+#        return space.newbool(self.check_hostname)
+#
+#    def descr_set_check_hostname(self, space, w_obj):
+#        check_hostname = space.is_true(w_obj)
+#        if check_hostname and libssl_SSL_CTX_get_verify_mode(self.ctx) == 
SSL_VERIFY_NONE:
+#            raise oefmt(space.w_ValueError,
+#                        "check_hostname needs a SSL context with either "
+#                        "CERT_OPTIONAL or CERT_REQUIRED")
+#        self.check_hostname = check_hostname
+#
+#    def load_cert_chain_w(self, space, w_certfile, w_keyfile=None,
+#                          w_password=None):
+#        if space.is_none(w_certfile):
+#            certfile = None
+#        else:
+#            certfile = space.str_w(w_certfile)
+#        if space.is_none(w_keyfile):
+#            keyfile = certfile
+#        else:
+#            keyfile = space.str_w(w_keyfile)
+#        pw_info = PasswordInfo()
+#        pw_info.space = space
+#        index = -1
+#        if not space.is_none(w_password):
+#            index = rthread.get_ident()
+#            PWINFO_STORAGE[index] = pw_info
+#
+#            if space.is_true(space.callable(w_password)):
+#                pw_info.w_callable = w_password
+#            else:
+#                if space.isinstance_w(w_password, space.w_unicode):
+#                    pw_info.password = space.str_w(w_password)
+#                else:
+#                    try:
+#                        pw_info.password = space.bufferstr_w(w_password)
+#                    except OperationError as e:
+#                        if not e.match(space, space.w_TypeError):
+#                            raise
+#                        raise oefmt(space.w_TypeError,
+#                                    "password should be a string or callable")
+#
+#            libssl_SSL_CTX_set_default_passwd_cb(
+#                self.ctx, _password_callback)
+#            libssl_SSL_CTX_set_default_passwd_cb_userdata(
+#                self.ctx, rffi.cast(rffi.VOIDP, index))
+#
+#        try:
+#            ret = libssl_SSL_CTX_use_certificate_chain_file(self.ctx, 
certfile)
+#            if ret != 1:
+#                if pw_info.operationerror:
+#                    libssl_ERR_clear_error()
+#                    raise pw_info.operationerror
+#                errno = get_saved_errno()
+#                if errno:
+#                    libssl_ERR_clear_error()
+#                    raise wrap_oserror(space, OSError(errno, ''),
+#                                       exception_name = 'w_IOError')
+#                else:
+#                    raise _ssl_seterror(space, None, -1)
+#
+#            ret = libssl_SSL_CTX_use_PrivateKey_file(self.ctx, keyfile,
+#                                                     SSL_FILETYPE_PEM)
+#            if ret != 1:
+#                if pw_info.operationerror:
+#                    libssl_ERR_clear_error()
+#                    raise pw_info.operationerror
+#                errno = get_saved_errno()
+#                if errno:
+#                    libssl_ERR_clear_error()
+#                    raise wrap_oserror(space, OSError(errno, ''),
+#                                       exception_name = 'w_IOError')
+#                else:
+#                    raise _ssl_seterror(space, None, -1)
+#
+#            ret = libssl_SSL_CTX_check_private_key(self.ctx)
+#            if ret != 1:
+#                raise _ssl_seterror(space, None, -1)
+#        finally:
+#            if index >= 0:
+#                del PWINFO_STORAGE[index]
+#            libssl_SSL_CTX_set_default_passwd_cb(
+#                self.ctx, lltype.nullptr(pem_password_cb.TO))
+#            libssl_SSL_CTX_set_default_passwd_cb_userdata(
+#                self.ctx, None)
+#
+#    @unwrap_spec(filepath=str)
+#    def load_dh_params_w(self, space, filepath):
+#        bio = libssl_BIO_new_file(filepath, "r")
+#        if not bio:
+#            errno = get_saved_errno()
+#            libssl_ERR_clear_error()
+#            raise wrap_oserror(space, OSError(errno, ''),
+#                               exception_name = 'w_IOError')
+#        try:
+#            dh = libssl_PEM_read_bio_DHparams(bio, None, None, None)
+#        finally:
+#            libssl_BIO_free(bio)
+#        if not dh:
+#            errno = get_saved_errno()
+#            if errno != 0:
+#                libssl_ERR_clear_error()
+#                raise wrap_oserror(space, OSError(errno, ''))
+#            else:
+#                raise _ssl_seterror(space, None, 0)
+#        try:
+#            if libssl_SSL_CTX_set_tmp_dh(self.ctx, dh) == 0:
+#                raise _ssl_seterror(space, None, 0)
+#        finally:
+#            libssl_DH_free(dh)
+#
+#    def load_verify_locations_w(self, space, w_cafile=None, w_capath=None,
+#                                w_cadata=None):
+#        if space.is_none(w_cafile):
+#            cafile = None
+#        else:
+#            cafile = space.str_w(w_cafile)
+#        if space.is_none(w_capath):
+#            capath = None
+#        else:
+#            capath = space.str_w(w_capath)
+#        if space.is_none(w_cadata):
+#            cadata = None
+#            ca_file_type = -1
+#        else:
+#            if not space.isinstance_w(w_cadata, space.w_unicode):
+#                ca_file_type = SSL_FILETYPE_ASN1
+#                cadata = space.bufferstr_w(w_cadata)
+#            else:
+#                ca_file_type = SSL_FILETYPE_PEM
+#                try:
+#                    cadata = space.unicode_w(w_cadata).encode('ascii')
+#                except UnicodeEncodeError:
+#                    raise oefmt(space.w_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 oefmt(space.w_TypeError,
+#                        "cafile and capath cannot be both omitted")
+#        # load from cadata
+#        if cadata is not None:
+#            with rffi.scoped_nonmovingbuffer(cadata) as buf:
+#                self._add_ca_certs(space, buf, len(cadata), ca_file_type)
+#
+#        # load cafile or capath
+#        if cafile is not None or capath is not None:
+#            ret = libssl_SSL_CTX_load_verify_locations(
+#                self.ctx, cafile, capath)
+#            if ret != 1:
+#                errno = get_saved_errno()
+#                if errno:
+#                    libssl_ERR_clear_error()
+#                    raise wrap_oserror(space, OSError(errno, ''),
+#                                       exception_name = 'w_IOError')
+#                else:
+#                    raise _ssl_seterror(space, None, -1)
+#
+#    def _add_ca_certs(self, space, data, size, ca_file_type):
+#        biobuf = libssl_BIO_new_mem_buf(data, size)
+#        if not biobuf:
+#            raise ssl_error(space, "Can't allocate buffer")
+#        try:
+#            store = libssl_SSL_CTX_get_cert_store(self.ctx)
+#            loaded = 0
+#            while True:
+#                if ca_file_type == SSL_FILETYPE_ASN1:
+#                    cert = libssl_d2i_X509_bio(
+#                        biobuf, None)
+#                else:
+#                    cert = libssl_PEM_read_bio_X509(
+#                        biobuf, None, None, None)
+#                if not cert:
+#                    break
+#                try:
+#                    r = libssl_X509_STORE_add_cert(store, cert)
+#                finally:
+#                    libssl_X509_free(cert)
+#                if not r:
+#                    err = libssl_ERR_peek_last_error()
+#                    if (libssl_ERR_GET_LIB(err) == ERR_LIB_X509 and
+#                        libssl_ERR_GET_REASON(err) ==
+#                        X509_R_CERT_ALREADY_IN_HASH_TABLE):
+#                        # cert already in hash table, not an error
+#                        libssl_ERR_clear_error()
+#                    else:
+#                        break
+#                loaded += 1
+#
+#            err = libssl_ERR_peek_last_error()
+#            if (ca_file_type == SSL_FILETYPE_ASN1 and
+#                loaded > 0 and
+#                libssl_ERR_GET_LIB(err) == ERR_LIB_ASN1 and
+#                libssl_ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG):
+#                # EOF ASN1 file, not an error
+#                libssl_ERR_clear_error()
+#            elif (ca_file_type == SSL_FILETYPE_PEM and
+#                  loaded > 0 and
+#                  libssl_ERR_GET_LIB(err) == ERR_LIB_PEM and
+#                  libssl_ERR_GET_REASON(err) == PEM_R_NO_START_LINE):
+#                # EOF PEM file, not an error
+#                libssl_ERR_clear_error()
+#            else:
+#                raise _ssl_seterror(space, None, 0)
+#        finally:
+#            libssl_BIO_free(biobuf)
+#
+#    def cert_store_stats_w(self, space):
+#        store = libssl_SSL_CTX_get_cert_store(self.ctx)
+#        x509 = 0
+#        x509_ca = 0
+#        crl = 0
+#        for i in range(libssl_sk_X509_OBJECT_num(store[0].c_objs)):
+#            obj = libssl_sk_X509_OBJECT_value(store[0].c_objs, i)
+#            if intmask(obj.c_type) == X509_LU_X509:
+#                x509 += 1
+#                if libssl_X509_check_ca(
+#                        libssl_pypy_X509_OBJECT_data_x509(obj)):
+#                    x509_ca += 1
+#            elif intmask(obj.c_type) == 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
+#        w_result = space.newdict()
+#        space.setitem(w_result,
+#                      space.wrap('x509'), space.wrap(x509))
+#        space.setitem(w_result,
+#                      space.wrap('x509_ca'), space.wrap(x509_ca))
+#        space.setitem(w_result,
+#                      space.wrap('crl'), space.wrap(crl))
+#        return w_result
+#
+#    @unwrap_spec(protos='bufferstr')
+#    def set_npn_protocols_w(self, space, protos):
+#        if not HAS_NPN:
+#            raise oefmt(space.w_NotImplementedError,
+#                        "The NPN extension requires OpenSSL 1.0.1 or later.")
+#
+#        self.npn_protocols = SSLNpnProtocols(self.ctx, protos)
+#
+#    @unwrap_spec(protos='bufferstr')
+#    def set_alpn_protocols_w(self, space, protos):
+#        if not HAS_ALPN:
+#            raise oefmt(space.w_NotImplementedError,
+#                        "The ALPN extension requires OpenSSL 1.0.2 or later.")
+#
+#        self.alpn_protocols = SSLAlpnProtocols(self.ctx, protos)
+#
+#    def get_ca_certs_w(self, space, w_binary_form=None):
+#        if w_binary_form and space.is_true(w_binary_form):
+#            binary_mode = True
+#        else:
+#            binary_mode = False
+#        rlist = []
+#        store = libssl_SSL_CTX_get_cert_store(self.ctx)
+#        for i in range(libssl_sk_X509_OBJECT_num(store[0].c_objs)):
+#            obj = libssl_sk_X509_OBJECT_value(store[0].c_objs, i)
+#            if intmask(obj.c_type) != X509_LU_X509:
+#                # not a x509 cert
+#                continue
+#            # CA for any purpose
+#            cert = libssl_pypy_X509_OBJECT_data_x509(obj)
+#            if not libssl_X509_check_ca(cert):
+#                continue
+#            if binary_mode:
+#                rlist.append(_certificate_to_der(space, cert))
+#            else:
+#                rlist.append(_decode_certificate(space, cert))
+#        return space.newlist(rlist)
+#
+#    @unwrap_spec(name=str)
+#    def set_ecdh_curve_w(self, space, name):
+#        nid = libssl_OBJ_sn2nid(name)
+#        if nid == 0:
+#            raise oefmt(space.w_ValueError,
+#                        "unknown elliptic curve name '%s'", name)
+#        key = libssl_EC_KEY_new_by_curve_name(nid)
+#        if not key:
+#            raise _ssl_seterror(space, None, 0)
+#        try:
+#            libssl_SSL_CTX_set_tmp_ecdh(self.ctx, key)
+#        finally:
+#            libssl_EC_KEY_free(key)
+#
+#    def set_servername_callback_w(self, space, w_callback):
+#        if space.is_none(w_callback):
+#            libssl_SSL_CTX_set_tlsext_servername_callback(
+#                self.ctx, lltype.nullptr(servername_cb.TO))
+#            self.servername_callback = None
+#            return
+#        if not space.is_true(space.callable(w_callback)):
+#            raise oefmt(space.w_TypeError, "not a callable object")
+#        callback_struct = ServernameCallback()
+#        callback_struct.space = space
+#        callback_struct.w_ctx = self
+#        callback_struct.w_set_hostname = w_callback
+#        self.servername_callback = callback_struct
+#        index = compute_unique_id(self)
+#        SERVERNAME_CALLBACKS.set(index, callback_struct)
+#        libssl_SSL_CTX_set_tlsext_servername_callback(
+#            self.ctx, _servername_callback)
+#        libssl_SSL_CTX_set_tlsext_servername_arg(self.ctx,
+#                                                 rffi.cast(rffi.VOIDP, index))
+#
+#
+
+RAND_status = lib.RAND_status
+RAND_add = lib.RAND_add
+
+def _RAND_bytes(count, pseudo):
+    if count < 0:
+        raise ValueError("num must be positive")
+    buf = ffi.new("unsigned char[]", b"\x00"*count)
+    if pseudo:
+        ok = lib.RAND_pseudo_bytes(buf, count)
+        if ok == 1 or ok == 0:
+            return (ffi.string(buf), ok == 1)
+    else:
+        ok = lib.RAND_bytes(buf, count)
+        if ok == 1:
+            return ffi.string(buf)
+    raise ssl_error("", 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):
+    # REVIEW unsure how to solve this. might be easy:
+    # str does not support buffer protocol.
+    # I think a user should really encode the string before it is 
+    # passed here!
+    if isinstance(view, str):
+        buf = ffi.from_buffer(view.encode())
+    else:
+        buf = ffi.from_buffer(view)
+    lib.RAND_add(buf, len(buf), entropy)
+
+def wrap_socket(s):
+    pass
+
+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_seterror(None, 0)
+    name = ffi.string(buf, length).decode('utf-8')
+
+    buf_ptr = ffi.new("unsigned char**")
+    length = lib.ASN1_STRING_to_UTF8(buf_ptr, value)
+    if length < 0:
+        raise _ssl_seterror(None, 0)
+    try:
+        value = ffi.string(buf_ptr[0]).decode('utf-8')
+    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.AUTHORITY_INFO_ACCESS_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 != GEN_URI:
+            continue
+        uri = ad.location.d.uniformResourceIdentifier
+        ostr = ffi.string(uri.data, uri.length)
+        lst.append(ostr)
+    lib.AUTHORITY_INFO_ACCESS_free(info)
+
+    # convert to tuple or None
+    if len(lst) == 0: return None
+    return tuple(lst)
+
+
+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
+        if rdn_level >= 0:
+            if rdn_level != entry.set:
+                dn.append(tuple(rdn))
+                rdn = []
+        rdn_level = entry.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:
+            pass # TODO error
+            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 _decode_certificate(certificate):
+    #PyObject *retval = NULL;
+    #BIO *biobuf = NULL;
+    #PyObject *peer;
+    #PyObject *peer_alt_names = NULL;
+    #PyObject *issuer;
+    #PyObject *version;
+    #PyObject *sn_obj;
+    #PyObject *obj;
+    #ASN1_INTEGER *serialNumber;
+    #char buf[2048];
+    #int len, result;
+    #ASN1_TIME *notBefore, *notAfter;
+    #PyObject *pnotBefore, *pnotAfter;
+
+    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
+
+    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.buf("char[2048]")
+    len = bio.BIO_gets(biobuf, buf, len(buf)-1)
+    if len < 0:
+        if biobuf: lib.BIO_free(biobuf)
+        raise _ssl_error(None) # TODO _setSSLError
+    retval["serialNumber"] = ffi.string(buf, len).decode('utf-8')
+
+    lib.BIO_reset(biobuf);
+    notBefore = lib.X509_get_notBefore(certificate);
+    lib.ASN1_TIME_print(biobuf, notBefore);
+    len = lib.BIO_gets(biobuf, buf, len(buf)-1);
+    if len < 0:
+        if biobuf: lib.BIO_free(biobuf)
+        raise _ssl_error(None) # TODO _setSSLError
+    retval["notBefore"] = ffi.string(buf, len).decode('utf-8')
+
+    lib.BIO_reset(biobuf);
+    notAfter = lib.X509_get_notAfter(certificate);
+    lib.ASN1_TIME_print(biobuf, notAfter);
+    len = lib.BIO_gets(biobuf, buf, len(buf)-1);
+    if len < 0:
+        raise _ssl_error(None) # TODO _setSSLError
+    retval["notAfter"] = ffi.string(buf, len);
+
+    # Now look for subjectAltName
+
+    peer_alt_names = _get_peer_alt_names(certificate);
+    if not peer_alt_names:
+        if biobuf: lib.BIO_free(biobuf)
+        return None
+    retval["subjectAltName"] = peer_alt_names
+
+    # Authority Information Access: OCSP URIs
+    obj = _get_aia_uri(certificate, lib.NID_ad_OCSP)
+    if not obj:
+        if biobuf: lib.BIO_free(biobuf)
+        return None
+    retval["OCSP"] = obj
+
+    obj = _get_aia_uri(certificate, lib.NID_ad_ca_issuers)
+    if not obj:
+        if biobuf: lib.BIO_free(biobuf)
+        return None
+    retval["caIssuers"] = obj
+
+    # CDP (CRL distribution points)
+    obj = _ssl._get_crl_dp(certificate)
+    if not obj:
+        if biobuf: lib.BIO_free(biobuf)
+        return None
+    retval["crlDistributionPoints"] = obj
+
+    lib.BIO_free(biobuf)
+    return retval
+
+
+class _ssl(object):
+    # for testing only
+    @staticmethod
+    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")
+
+        # REVIEW how to encode this properly?
+        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
diff --git a/lib_pypy/openssl/_stdssl/_tests.py 
b/lib_pypy/openssl/_stdssl/_tests.py
new file mode 100644
diff --git a/lib_pypy/ssl.py b/lib_pypy/ssl.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/ssl.py
@@ -0,0 +1,5 @@
+# This file exposes the Standard Library API for the ssl module
+
+from openssl._stdssl import _PROTOCOL_NAMES
+from openssl._stdssl import _ssl
+from openssl._stdssl import *
diff --git a/pypy/tool/build_cffi_imports.py b/pypy/tool/build_cffi_imports.py
--- a/pypy/tool/build_cffi_imports.py
+++ b/pypy/tool/build_cffi_imports.py
@@ -17,7 +17,7 @@
     "resource": "_resource_build.py" if sys.platform != "win32" else None,
     "lzma": "_lzma_build.py",
     "_decimal": "_decimal_build.py",
-    "ssl": "ssl_build.py",
+    "ssl": "_ssl_build.py",
     "xx": None,    # for testing: 'None' should be completely ignored
     }
 
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to