Author: Richard Plangger <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit