Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r88053:30e64025212d Date: 2016-11-01 22:58 +0100 http://bitbucket.org/pypy/pypy/changeset/30e64025212d/
Log: hg merge openssl-1.1 Support for OpenSSL version 1.1 (in addition to version 1.0). Thanks tumbleweed for doing this! Tested on Linux (1.1, 1.0) and on Windows and Mac (1.0 only). Tumbleweed says LibreSSL still segfaults _ssl tests and fails _hashlib tests, just like previously. diff --git a/lib-python/2.7/test/test_ssl.py b/lib-python/2.7/test/test_ssl.py --- a/lib-python/2.7/test/test_ssl.py +++ b/lib-python/2.7/test/test_ssl.py @@ -26,6 +26,8 @@ PROTOCOLS = sorted(ssl._PROTOCOL_NAMES) HOST = support.HOST +IS_LIBRESSL = ssl.OPENSSL_VERSION.startswith('LibreSSL') +IS_OPENSSL_1_1 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0) def data_file(*name): return os.path.join(os.path.dirname(__file__), *name) @@ -742,15 +744,15 @@ def test_options(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) # OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value - self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3, - ctx.options) + default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3) + if not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0): + default |= ssl.OP_NO_COMPRESSION + self.assertEqual(default, ctx.options) ctx.options |= ssl.OP_NO_TLSv1 - self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1, - ctx.options) + self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options) if can_clear_options(): - ctx.options = (ctx.options & ~ssl.OP_NO_SSLv2) | ssl.OP_NO_TLSv1 - self.assertEqual(ssl.OP_ALL | ssl.OP_NO_TLSv1 | ssl.OP_NO_SSLv3, - ctx.options) + ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1) + self.assertEqual(default, ctx.options) ctx.options = 0 self.assertEqual(0, ctx.options) else: @@ -2918,18 +2920,27 @@ client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) client_context.load_cert_chain(CERTFILE) client_context.set_alpn_protocols(client_protocols) - stats = server_params_test(client_context, server_context, - chatty=True, connectionchatty=True) - - msg = "failed trying %s (s) and %s (c).\n" \ - "was expecting %s, but got %%s from the %%s" \ - % (str(server_protocols), str(client_protocols), - str(expected)) - client_result = stats['client_alpn_protocol'] - self.assertEqual(client_result, expected, msg % (client_result, "client")) - server_result = stats['server_alpn_protocols'][-1] \ - if len(stats['server_alpn_protocols']) else 'nothing' - self.assertEqual(server_result, expected, msg % (server_result, "server")) + try: + stats = server_params_test(client_context, server_context, + chatty=True, connectionchatty=True) + except ssl.SSLError as e: + stats = e + + if expected is None and IS_OPENSSL_1_1: + # OpenSSL 1.1.0 raises handshake error + self.assertIsInstance(stats, ssl.SSLError) + else: + msg = "failed trying %s (s) and %s (c).\n" \ + "was expecting %s, but got %%s from the %%s" \ + % (str(server_protocols), str(client_protocols), + str(expected)) + client_result = stats['client_alpn_protocol'] + self.assertEqual(client_result, expected, + msg % (client_result, "client")) + server_result = stats['server_alpn_protocols'][-1] \ + if len(stats['server_alpn_protocols']) else 'nothing' + self.assertEqual(server_result, expected, + msg % (server_result, "server")) def test_selected_npn_protocol(self): # selected_npn_protocol() is None unless NPN is used diff --git a/pypy/module/_hashlib/__init__.py b/pypy/module/_hashlib/__init__.py --- a/pypy/module/_hashlib/__init__.py +++ b/pypy/module/_hashlib/__init__.py @@ -1,5 +1,6 @@ from pypy.interpreter.mixedmodule import MixedModule -from pypy.module._hashlib.interp_hashlib import algorithms, fetch_names +from pypy.module._hashlib.interp_hashlib import ( + algorithms, fetch_names, HAS_FAST_PKCS5_PBKDF2_HMAC) class Module(MixedModule): @@ -13,6 +14,9 @@ for name in algorithms: interpleveldefs['openssl_' + name] = 'interp_hashlib.new_' + name + if HAS_FAST_PKCS5_PBKDF2_HMAC: + interpleveldefs['pbkdf2_hmac'] = 'interp_hashlib.pbkdf2_hmac' + def startup(self, space): w_meth_names = fetch_names(space) space.setattr(self, space.wrap('openssl_md_meth_names'), w_meth_names) diff --git a/pypy/module/_hashlib/interp_hashlib.py b/pypy/module/_hashlib/interp_hashlib.py --- a/pypy/module/_hashlib/interp_hashlib.py +++ b/pypy/module/_hashlib/interp_hashlib.py @@ -8,7 +8,7 @@ from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt -from pypy.interpreter.gateway import unwrap_spec, interp2app +from pypy.interpreter.gateway import unwrap_spec, interp2app, WrappedDefault from pypy.interpreter.typedef import TypeDef, GetSetProperty from pypy.module.thread.os_lock import Lock @@ -58,23 +58,26 @@ def __init__(self, space, name, copy_from=NULL_CTX): self.name = name digest_type = self.digest_type_by_name(space) - self.digest_size = rffi.getintfield(digest_type, 'c_md_size') + self.digest_size = ropenssl.EVP_MD_size(digest_type) # Allocate a lock for each HASH object. # An optimization would be to not release the GIL on small requests, # and use a custom lock only when needed. self.lock = Lock(space) - ctx = lltype.malloc(ropenssl.EVP_MD_CTX.TO, flavor='raw') + ctx = ropenssl.EVP_MD_CTX_new() + if ctx is None: + raise MemoryError rgc.add_memory_pressure(ropenssl.HASH_MALLOC_SIZE + self.digest_size) try: if copy_from: - ropenssl.EVP_MD_CTX_copy(ctx, copy_from) + if not ropenssl.EVP_MD_CTX_copy(ctx, copy_from): + raise ValueError else: ropenssl.EVP_DigestInit(ctx, digest_type) self.ctx = ctx except: - lltype.free(ctx, flavor='raw') + ropenssl.EVP_MD_CTX_free(ctx) raise self.register_finalizer(space) @@ -82,8 +85,7 @@ ctx = self.ctx if ctx: self.ctx = lltype.nullptr(ropenssl.EVP_MD_CTX.TO) - ropenssl.EVP_MD_CTX_cleanup(ctx) - lltype.free(ctx, flavor='raw') + ropenssl.EVP_MD_CTX_free(ctx) def digest_type_by_name(self, space): digest_type = ropenssl.EVP_get_digestbyname(self.name) @@ -128,21 +130,26 @@ def get_block_size(self, space): digest_type = self.digest_type_by_name(space) - block_size = rffi.getintfield(digest_type, 'c_block_size') + block_size = ropenssl.EVP_MD_block_size(digest_type) return space.wrap(block_size) def get_name(self, space): return space.wrap(self.name) def _digest(self, space): - with lltype.scoped_alloc(ropenssl.EVP_MD_CTX.TO) as ctx: + ctx = ropenssl.EVP_MD_CTX_new() + if ctx is None: + raise MemoryError + try: with self.lock: - ropenssl.EVP_MD_CTX_copy(ctx, self.ctx) + if not ropenssl.EVP_MD_CTX_copy(ctx, self.ctx): + raise ValueError digest_size = self.digest_size with rffi.scoped_alloc_buffer(digest_size) as buf: ropenssl.EVP_DigestFinal(ctx, buf.raw, None) - ropenssl.EVP_MD_CTX_cleanup(ctx) return buf.str(digest_size) + finally: + ropenssl.EVP_MD_CTX_free(ctx) W_Hash.typedef = TypeDef( @@ -177,3 +184,27 @@ for _name in algorithms: _newname = 'new_%s' % (_name,) globals()[_newname] = make_new_hash(_name, _newname) + + +HAS_FAST_PKCS5_PBKDF2_HMAC = ropenssl.PKCS5_PBKDF2_HMAC is not None +if HAS_FAST_PKCS5_PBKDF2_HMAC: + @unwrap_spec(name=str, password=str, salt=str, rounds=int, + w_dklen=WrappedDefault(None)) + def pbkdf2_hmac(space, name, password, salt, rounds, w_dklen): + digest = ropenssl.EVP_get_digestbyname(name) + if not digest: + raise oefmt(space.w_ValueError, "unknown hash function") + if space.is_w(w_dklen, space.w_None): + dklen = ropenssl.EVP_MD_size(digest) + else: + dklen = space.int_w(w_dklen) + if dklen < 1: + raise oefmt(space.w_ValueError, + "key length must be greater than 0.") + with rffi.scoped_alloc_buffer(dklen) as buf: + r = ropenssl.PKCS5_PBKDF2_HMAC( + password, len(password), salt, len(salt), rounds, digest, + dklen, buf.raw) + if not r: + raise ValueError + return space.wrap(buf.str(dklen)) diff --git a/pypy/module/_hashlib/test/test_hashlib.py b/pypy/module/_hashlib/test/test_hashlib.py --- a/pypy/module/_hashlib/test/test_hashlib.py +++ b/pypy/module/_hashlib/test/test_hashlib.py @@ -15,17 +15,19 @@ def test_attributes(self): import hashlib - for name, expected_size in {'md5': 16, - 'sha1': 20, - 'sha224': 28, - 'sha256': 32, - 'sha384': 48, - 'sha512': 64, - }.items(): + for name, (expected_size, expected_block_size) in { + 'md5': (16, 64), + 'sha1': (20, 64), + 'sha224': (28, 64), + 'sha256': (32, 64), + 'sha384': (48, 128), + 'sha512': (64, 128), + }.items(): h = hashlib.new(name) assert h.name == name assert h.digest_size == expected_size assert h.digestsize == expected_size + assert h.block_size == expected_block_size # h.update('abc') h2 = h.copy() @@ -46,6 +48,7 @@ h = py_new(name)('') assert h.digest_size == expected_size assert h.digestsize == expected_size + assert h.block_size == expected_block_size # h.update('abc') h2 = h.copy() @@ -108,3 +111,13 @@ got.decode('hex') if expected is not None: assert got == expected + + def test_pbkdf2(self): + try: + from _hashlib import pbkdf2_hmac + except ImportError: + skip("Requires OpenSSL >= 1.1") + out = pbkdf2_hmac('sha1', 'password', 'salt', 1) + assert out == '0c60c80f961f0e71f3a9b524af6012062fe037a6'.decode('hex') + out = pbkdf2_hmac('sha1', 'password', 'salt', 2, None) + assert out == 'ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957'.decode('hex') diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py --- a/pypy/module/_ssl/interp_ssl.py +++ b/pypy/module/_ssl/interp_ssl.py @@ -33,9 +33,10 @@ PY_SSL_CLIENT, PY_SSL_SERVER = 0, 1 (PY_SSL_VERSION_SSL2, PY_SSL_VERSION_SSL3, - PY_SSL_VERSION_SSL23, PY_SSL_VERSION_TLS1, PY_SSL_VERSION_TLS1_1, + PY_SSL_VERSION_TLS, PY_SSL_VERSION_TLS1, PY_SSL_VERSION_TLS1_1, PY_SSL_VERSION_TLS1_2) = range(6) + SOCKET_IS_NONBLOCKING, SOCKET_IS_BLOCKING = 0, 1 SOCKET_HAS_TIMED_OUT, SOCKET_HAS_BEEN_CLOSED = 2, 3 SOCKET_TOO_LARGE_FOR_SELECT, SOCKET_OPERATION_OK = 4, 5 @@ -68,11 +69,12 @@ constants["HAS_NPN"] = HAS_NPN constants["HAS_ALPN"] = HAS_ALPN +constants["PROTOCOL_TLS"] = PY_SSL_VERSION_TLS +constants["PROTOCOL_SSLv23"] = PY_SSL_VERSION_TLS # Legacy name if not OPENSSL_NO_SSL2: constants["PROTOCOL_SSLv2"] = PY_SSL_VERSION_SSL2 if not OPENSSL_NO_SSL3: constants["PROTOCOL_SSLv3"] = PY_SSL_VERSION_SSL3 -constants["PROTOCOL_SSLv23"] = PY_SSL_VERSION_SSL23 constants["PROTOCOL_TLSv1"] = PY_SSL_VERSION_TLS1 if HAVE_TLSv1_2: constants["PROTOCOL_TLSv1_1"] = PY_SSL_VERSION_TLS1_1 @@ -111,7 +113,10 @@ err_reason = libssl_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 = rffi.charp2str(libssl_ERR_reason_error_string(errcode)) + raw_msg = libssl_ERR_reason_error_string(errcode) + msg = None + if raw_msg: + msg = rffi.charp2str(raw_msg) if not msg: msg = "unknown error" if reason_str and lib_str: @@ -637,9 +642,12 @@ if not self.ssl: return space.w_None comp_method = libssl_SSL_get_current_compression(self.ssl) - if not comp_method or intmask(comp_method[0].c_type) == NID_undef: + if not comp_method: return space.w_None - short_name = libssl_OBJ_nid2sn(comp_method[0].c_type) + method_type = intmask(libssl_COMP_get_type(comp_method)) + if method_type == NID_undef: + return space.w_None + short_name = libssl_COMP_get_name(comp_method) if not short_name: return space.w_None return space.wrap(rffi.charp2str(short_name)) @@ -795,7 +803,7 @@ for index in range(entry_count): entry = libssl_X509_NAME_get_entry(xname, index) # check to see if we've gotten to a new RDN - entry_level = intmask(entry[0].c_set) + entry_level = intmask(libssl_X509_NAME_ENTRY_set(entry)) if rdn_level >= 0: if rdn_level != entry_level: # yes, new RDN @@ -846,8 +854,9 @@ "No method for internalizing subjectAltName!'") with lltype.scoped_alloc(rffi.CCHARPP.TO, 1) as p_ptr: - p_ptr[0] = ext[0].c_value.c_data - length = intmask(ext[0].c_value.c_length) + ext_value = libssl_X509_EXTENSION_get_data(ext) + p_ptr[0] = ext_value.c_data + length = intmask(ext_value.c_length) null = lltype.nullptr(rffi.VOIDP.TO) if method[0].c_it: names = rffi.cast(GENERAL_NAMES, libssl_ASN1_item_d2i( @@ -967,10 +976,8 @@ if OPENSSL_VERSION_NUMBER >= 0x10001000: # Calls x509v3_cache_extensions and sets up crldp libssl_X509_check_ca(certificate) - dps = certificate[0].c_crldp - else: - dps = rffi.cast(stack_st_DIST_POINT, libssl_X509_get_ext_d2i( - certificate, NID_crl_distribution_points, None, None)) + dps = rffi.cast(stack_st_DIST_POINT, libssl_X509_get_ext_d2i( + certificate, NID_crl_distribution_points, None, None)) if not dps: return None @@ -1268,14 +1275,14 @@ @staticmethod @unwrap_spec(protocol=int) def descr_new(space, w_subtype, protocol): - if protocol == PY_SSL_VERSION_TLS1: + if protocol == PY_SSL_VERSION_TLS: + method = libssl_TLS_method() + elif protocol == PY_SSL_VERSION_TLS1: method = libssl_TLSv1_method() elif protocol == PY_SSL_VERSION_SSL3 and not OPENSSL_NO_SSL3: method = libssl_SSLv3_method() elif protocol == PY_SSL_VERSION_SSL2 and not OPENSSL_NO_SSL2: method = libssl_SSLv2_method() - elif protocol == PY_SSL_VERSION_SSL23: - method = libssl_SSLv23_method() elif protocol == PY_SSL_VERSION_TLS1_1 and HAVE_TLSv1_2: method = libssl_TLSv1_1_method() elif protocol == PY_SSL_VERSION_TLS1_2 and HAVE_TLSv1_2: @@ -1303,6 +1310,7 @@ # OpenSSL 1.0.2+), or use prime256v1 by default. # This is Apache mod_ssl's initialization # policy, so we should be safe. + # OpenSSL 1.1 has it enabled by default. if libssl_SSL_CTX_set_ecdh_auto: libssl_SSL_CTX_set_ecdh_auto(self.ctx, 1) else: @@ -1390,20 +1398,22 @@ 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) + param = libssl_X509_STORE_get0_param(store) + flags = libssl_X509_VERIFY_PARAM_get_flags(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) + param = libssl_X509_STORE_get0_param(store) + flags = libssl_X509_VERIFY_PARAM_get_flags(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): + 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): + param, flags_set): raise _ssl_seterror(space, None, 0) def descr_get_check_hostname(self, space): @@ -1614,14 +1624,16 @@ 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: + objs = libssl_X509_STORE_get0_objects(store) + for i in range(libssl_sk_X509_OBJECT_num(objs)): + obj = libssl_sk_X509_OBJECT_value(objs, i) + obj_type = intmask(libssl_X509_OBJECT_get_type(obj)) + if obj_type == X509_LU_X509: x509 += 1 if libssl_X509_check_ca( - libssl_pypy_X509_OBJECT_data_x509(obj)): + libssl_X509_OBJECT_get0_X509(obj)): x509_ca += 1 - elif intmask(obj.c_type) == X509_LU_CRL: + elif obj_type == X509_LU_CRL: crl += 1 else: # Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY. @@ -1660,13 +1672,14 @@ 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: + objs = libssl_X509_STORE_get0_objects(store) + for i in range(libssl_sk_X509_OBJECT_num(objs)): + obj = libssl_sk_X509_OBJECT_value(objs, i) + if intmask(libssl_X509_OBJECT_get_type(obj)) != X509_LU_X509: # not a x509 cert continue # CA for any purpose - cert = libssl_pypy_X509_OBJECT_data_x509(obj) + cert = libssl_X509_OBJECT_get0_X509(obj) if not libssl_X509_check_ca(cert): continue if binary_mode: diff --git a/pypy/module/_ssl/test/test_ssl.py b/pypy/module/_ssl/test/test_ssl.py --- a/pypy/module/_ssl/test/test_ssl.py +++ b/pypy/module/_ssl/test/test_ssl.py @@ -77,7 +77,7 @@ if sys.version_info < (2, 7, 9): ss = _ssl.sslwrap(s, 0) else: - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) ss = ctx._wrap_socket(s, 0) assert ss.context is ctx exc = raises(_socket.error, ss.do_handshake) @@ -95,7 +95,7 @@ if sys.version_info < (2, 7, 9): ss = _ssl.sslwrap(s, 0) else: - ss = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1)._wrap_socket(s, 0) + ss = _ssl._SSLContext(_ssl.PROTOCOL_TLS)._wrap_socket(s, 0) s.close() exc = raises(_ssl.SSLError, ss.write, "data") assert exc.value.message == 'Underlying socket has been closed.' @@ -123,13 +123,13 @@ def test_context(self): import _ssl - s = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + s = _ssl._SSLContext(_ssl.PROTOCOL_TLS) raises(ValueError, _ssl._SSLContext, -1) assert type(s.options) is long - assert s.options & _ssl.OP_NO_SSLv2 - s.options &= ~_ssl.OP_NO_SSLv2 - assert not s.options & _ssl.OP_NO_SSLv2 + assert s.options & _ssl.OP_NO_SSLv3 + s.options &= ~_ssl.OP_NO_SSLv3 + assert not s.options & _ssl.OP_NO_SSLv3 raises(TypeError, "s.options = 2.5") assert not s.check_hostname @@ -159,7 +159,7 @@ def test_set_default_verify_paths(self): import _ssl - s = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + s = _ssl._SSLContext(_ssl.PROTOCOL_TLS) s.set_default_verify_paths() @@ -253,13 +253,44 @@ if not _ssl.HAS_NPN: skip("NPN requires OpenSSL 1.0.1 or greater") - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) ctx._set_npn_protocols(b'\x08http/1.1\x06spdy/2') ss = ctx._wrap_socket(self.s._sock, True, server_hostname="svn.python.org") self.s.close() del ss; gc.collect() + def test_peer_certificate(self): + import _ssl, gc + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) + ss = ctx._wrap_socket(self.s._sock, False) + ss.do_handshake() + assert isinstance(ss.peer_certificate(der=True), bytes) + assert isinstance(ss.peer_certificate(), dict) + self.s.close() + del ss; gc.collect() + + def test_peer_certificate_verify(self): + import _ssl, ssl, gc + paths = ssl.get_default_verify_paths() + if not paths.capath and not paths.cafile: + skip("ssl.get_default_verify_paths() failed to return any path") + + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) + ctx.verify_mode = _ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=paths.capath, cafile=paths.cafile) + + ss = ctx._wrap_socket(self.s._sock, False) + try: + ss.do_handshake() + except _ssl.SSLError as e: + if e.reason == 'CERTIFICATE_VERIFY_FAILED': + skip("Certificate verification failed. " + "Most likely we just don't have any CA certificates.") + assert ss.peer_certificate() + self.s.close() + del ss; gc.collect() + def test_tls_unique_cb(self): import ssl, sys, gc ss = ssl.wrap_socket(self.s) @@ -325,7 +356,7 @@ def test_load_cert_chain(self): import _ssl, errno - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) ctx.load_cert_chain(self.keycert) ctx.load_cert_chain(self.cert, self.key) exc = raises(IOError, ctx.load_cert_chain, "inexistent.pem") @@ -344,11 +375,11 @@ def test_load_verify_locations(self): import _ssl - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) ctx.load_verify_locations(self.keycert) ctx.load_verify_locations(cafile=self.keycert, capath=None) - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) with open(self.keycert) as f: cacert_pem = f.read().decode('ascii') ctx.load_verify_locations(cadata=cacert_pem) @@ -356,7 +387,7 @@ def test_get_ca_certs(self): import _ssl - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) ctx.load_verify_locations(self.keycert) assert ctx.get_ca_certs() == [] ctx.load_verify_locations(self.python_org_cert) @@ -370,7 +401,7 @@ def test_cert_store_stats(self): import _ssl - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) assert ctx.cert_store_stats() == {'x509_ca': 0, 'crl': 0, 'x509': 0} ctx.load_cert_chain(self.keycert) assert ctx.cert_store_stats() == {'x509_ca': 0, 'crl': 0, 'x509': 0} @@ -379,7 +410,7 @@ def test_load_dh_params(self): import _ssl, errno - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) ctx.load_dh_params(self.dh512) raises(TypeError, ctx.load_dh_params) raises(TypeError, ctx.load_dh_params, None) @@ -389,7 +420,7 @@ def test_set_ecdh_curve(self): import _ssl - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) ctx.set_ecdh_curve("prime256v1") raises(ValueError, ctx.set_ecdh_curve, "foo") @@ -434,7 +465,7 @@ def test_lib_reason(self): # Test the library and reason attributes import _ssl - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) exc = raises(_ssl.SSLError, ctx.load_dh_params, self.keycert) assert exc.value.library == 'PEM' assert exc.value.reason == 'NO_START_LINE' @@ -445,7 +476,7 @@ # Check that the appropriate SSLError subclass is raised # (this only tests one of them) import _ssl, _socket - ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLS) s = _socket.socket() try: s.bind(("127.0.0.1", 0)) diff --git a/rpython/rlib/ropenssl.py b/rpython/rlib/ropenssl.py --- a/rpython/rlib/ropenssl.py +++ b/rpython/rlib/ropenssl.py @@ -57,8 +57,19 @@ '#define pypy_GENERAL_NAME_dirn(name) (name->d.dirn)', '#define pypy_GENERAL_NAME_uri(name) (name->d.uniformResourceIdentifier)', '#define pypy_GENERAL_NAME_pop_free(names) (sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free))', - '#define pypy_X509_OBJECT_data_x509(obj) (obj->data.x509)', '#define pypy_DIST_POINT_fullname(obj) (obj->distpoint->name.fullname)', + # Backwards compatibility for functions introduced in 1.1 + '#if (OPENSSL_VERSION_NUMBER < 0x10100000) || defined(LIBRESSL_VERSION_NUMBER)\n' + '# define COMP_get_name(meth) (meth->name)\n' + '# define COMP_get_type(meth) (meth->type)\n' + '# define X509_NAME_ENTRY_set(ne) (ne->set)\n' + '# define X509_OBJECT_get0_X509(obj) (obj->data.x509)\n' + '# define X509_OBJECT_get_type(obj) (obj->type)\n' + '# define X509_STORE_get0_objects(store) (store->objs)\n' + '# define X509_STORE_get0_param(store) (store->param)\n' + '#else\n' + '# define OPENSSL_NO_SSL2\n' + '#endif', ], ) @@ -88,6 +99,7 @@ "OPENSSL_EXPORT_VAR_AS_FUNCTION") OPENSSL_VERSION_NUMBER = rffi_platform.ConstantInteger( "OPENSSL_VERSION_NUMBER") + LIBRESSL = rffi_platform.Defined("LIBRESSL_VERSION_NUMBER") cconfig = rffi_platform.configure(CConfigBootstrap) if cconfig["OPENSSL_EXPORT_VAR_AS_FUNCTION"]: @@ -95,23 +107,10 @@ else: ASN1_ITEM_EXP = ASN1_ITEM OPENSSL_VERSION_NUMBER = cconfig["OPENSSL_VERSION_NUMBER"] +LIBRESSL = cconfig["LIBRESSL"] +OPENSSL_1_1 = OPENSSL_VERSION_NUMBER >= 0x10100000 and not LIBRESSL HAVE_TLSv1_2 = OPENSSL_VERSION_NUMBER >= 0x10001000 -if (OPENSSL_VERSION_NUMBER >= 0x10100000 and - OPENSSL_VERSION_NUMBER < 0x20000000): # <= libressl :-( - eci.pre_include_bits = () - eci.post_include_bits = () - raise Exception("""OpenSSL version >= 1.1 not supported yet. - - This program requires OpenSSL version 1.0.x, and may also - work with LibreSSL or OpenSSL 0.9.x. OpenSSL 1.1 is quite - some work to update to; contributions are welcome. Sorry, - you need to install an older version of OpenSSL for now. - Make sure this older version is the one picked up by this - program when it runs the compiler. - - This is the configuration used: %r""" % (eci,)) - class CConfig: _compilation_info_ = eci @@ -210,28 +209,10 @@ OBJ_NAME_TYPE_MD_METH = rffi_platform.ConstantInteger( "OBJ_NAME_TYPE_MD_METH") - if OPENSSL_VERSION_NUMBER >= 0x10001000: - X509_st = rffi_platform.Struct( - 'struct x509_st', - [('crldp', stack_st_DIST_POINT)]) - # Some structures, with only the fields used in the _ssl module - X509_name_entry_st = rffi_platform.Struct('struct X509_name_entry_st', - [('set', rffi.INT)]) asn1_string_st = rffi_platform.Struct('struct asn1_string_st', [('length', rffi.INT), ('data', rffi.CCHARP)]) - X509_extension_st = rffi_platform.Struct( - 'struct X509_extension_st', - [('value', ASN1_STRING)]) - x509_store_st = rffi_platform.Struct( - 'struct x509_store_st', - [('objs', stack_st_X509_OBJECT), - ('param', X509_VERIFY_PARAM)]) - x509_object_st = rffi_platform.Struct( - 'struct x509_object_st', - [('type', rffi.INT)]) - X509_LU_X509 = rffi_platform.ConstantInteger("X509_LU_X509") X509_LU_CRL = rffi_platform.ConstantInteger("X509_LU_CRL") @@ -244,12 +225,6 @@ GENERAL_NAME_st = rffi_platform.Struct( 'struct GENERAL_NAME_st', [('type', rffi.INT)]) - EVP_MD_st = rffi_platform.Struct( - 'EVP_MD', - [('md_size', rffi.INT), - ('block_size', rffi.INT)]) - EVP_MD_SIZE = rffi_platform.SizeOf('EVP_MD') - EVP_MD_CTX_SIZE = rffi_platform.SizeOf('EVP_MD_CTX') OBJ_NAME_st = rffi_platform.Struct( 'OBJ_NAME', @@ -257,10 +232,6 @@ ('name', rffi.CCHARP), ]) - COMP_METHOD_st = rffi_platform.Struct( - 'struct comp_method_st', - [('type', rffi.INT),]) - ACCESS_DESCRIPTION_st = rffi_platform.Struct( 'struct ACCESS_DESCRIPTION_st', [('method', ASN1_OBJECT), @@ -275,14 +246,12 @@ SSL_CIPHER = rffi.COpaquePtr('SSL_CIPHER') SSL = rffi.COpaquePtr('SSL') BIO = rffi.COpaquePtr('BIO') -if OPENSSL_VERSION_NUMBER >= 0x10001000: - X509 = rffi.CArrayPtr(X509_st) -else: - X509 = rffi.COpaquePtr('X509') -X509_NAME_ENTRY = rffi.CArrayPtr(X509_name_entry_st) -X509_EXTENSION = rffi.CArrayPtr(X509_extension_st) -X509_STORE = rffi.CArrayPtr(x509_store_st) -X509_OBJECT = lltype.Ptr(x509_object_st) +X509 = rffi.COpaquePtr('X509') +X509_OBJECT = rffi.COpaquePtr('X509_OBJECT') +COMP_METHOD = rffi.COpaquePtr('COMP_METHOD') +X509_NAME_ENTRY = rffi.COpaquePtr('X509_NAME_ENTRY') +X509_EXTENSION = rffi.COpaquePtr('X509_EXTENSION') +X509_STORE = rffi.COpaquePtr('X509_STORE') X509V3_EXT_METHOD = rffi.CArrayPtr(v3_ext_method) ASN1_STRING.TO.become(asn1_string_st) ASN1_TIME = rffi.COpaquePtr('ASN1_TIME') @@ -290,9 +259,9 @@ GENERAL_NAMES = rffi.COpaquePtr('GENERAL_NAMES') GENERAL_NAME.TO.become(GENERAL_NAME_st) OBJ_NAME = rffi.CArrayPtr(OBJ_NAME_st) -COMP_METHOD = rffi.CArrayPtr(COMP_METHOD_st) ACCESS_DESCRIPTION = rffi.CArrayPtr(ACCESS_DESCRIPTION_st) + HAVE_OPENSSL_RAND = OPENSSL_VERSION_NUMBER >= 0x0090500f HAVE_OPENSSL_FINISHED = OPENSSL_VERSION_NUMBER >= 0x0090500f HAVE_SSL_CTX_CLEAR_OPTIONS = OPENSSL_VERSION_NUMBER >= 0x009080df and \ @@ -314,8 +283,10 @@ globals()['libssl_' + name] = external( name, argtypes, restype, **kw) -ssl_external('SSL_load_error_strings', [], lltype.Void) -ssl_external('SSL_library_init', [], rffi.INT) +ssl_external('SSL_load_error_strings', [], lltype.Void, + macro=OPENSSL_1_1 or None) +ssl_external('SSL_library_init', [], rffi.INT, + macro=OPENSSL_1_1 or None) ssl_external('CRYPTO_num_locks', [], rffi.INT) ssl_external('CRYPTO_set_locking_callback', [lltype.Ptr(lltype.FuncType( @@ -341,7 +312,11 @@ ssl_external('TLSv1_2_method', [], SSL_METHOD) ssl_external('SSLv2_method', [], SSL_METHOD) ssl_external('SSLv3_method', [], SSL_METHOD) -ssl_external('SSLv23_method', [], SSL_METHOD) +# Windows note: fails in untranslated tests if the following function is +# made 'macro=True'. Not sure I want to dig into the reason for that mess. +libssl_TLS_method = external( + 'TLS_method' if OPENSSL_1_1 else 'SSLv23_method', + [], SSL_METHOD) ssl_external('SSL_CTX_use_PrivateKey_file', [SSL_CTX, rffi.CCHARP, rffi.INT], rffi.INT, save_err=rffi.RFFI_FULL_ERRNO_ZERO) ssl_external('SSL_CTX_use_certificate_chain_file', [SSL_CTX, rffi.CCHARP], rffi.INT, @@ -370,7 +345,7 @@ lltype.Void, macro=True) ssl_external('SSL_CTX_set_tlsext_servername_arg', [SSL_CTX, rffi.VOIDP], lltype.Void, macro=True) ssl_external('SSL_CTX_set_tmp_ecdh', [SSL_CTX, EC_KEY], lltype.Void, macro=True) -if OPENSSL_VERSION_NUMBER >= 0x10002000: +if OPENSSL_VERSION_NUMBER >= 0x10002000 and not OPENSSL_1_1: ssl_external('SSL_CTX_set_ecdh_auto', [SSL_CTX, rffi.INT], lltype.Void, macro=True) else: @@ -416,6 +391,8 @@ ssl_external('X509_NAME_get_entry', [X509_NAME, rffi.INT], X509_NAME_ENTRY) ssl_external('X509_NAME_ENTRY_get_object', [X509_NAME_ENTRY], ASN1_OBJECT) ssl_external('X509_NAME_ENTRY_get_data', [X509_NAME_ENTRY], ASN1_STRING) +ssl_external('X509_NAME_ENTRY_set', [X509_NAME_ENTRY], rffi.INT, + macro=(not OPENSSL_1_1) or None) ssl_external('i2d_X509', [X509, rffi.CCHARPP], rffi.INT, save_err=SAVE_ERR) ssl_external('X509_free', [X509], lltype.Void, releasegil=False) ssl_external('X509_check_ca', [X509], rffi.INT) @@ -427,11 +404,16 @@ ssl_external('X509_get_ext', [X509, rffi.INT], X509_EXTENSION) ssl_external('X509_get_ext_d2i', [X509, rffi.INT, rffi.VOIDP, rffi.VOIDP], rffi.VOIDP) ssl_external('X509V3_EXT_get', [X509_EXTENSION], X509V3_EXT_METHOD) +ssl_external('X509_EXTENSION_get_data', [X509_EXTENSION], ASN1_STRING) ssl_external('X509_VERIFY_PARAM_get_flags', [X509_VERIFY_PARAM], rffi.ULONG) ssl_external('X509_VERIFY_PARAM_set_flags', [X509_VERIFY_PARAM, rffi.ULONG], rffi.INT) ssl_external('X509_VERIFY_PARAM_clear_flags', [X509_VERIFY_PARAM, rffi.ULONG], rffi.INT) ssl_external('X509_STORE_add_cert', [X509_STORE, X509], rffi.INT) +ssl_external('X509_STORE_get0_objects', [X509_STORE], stack_st_X509_OBJECT, + macro=bool(not OPENSSL_1_1) or None) +ssl_external('X509_STORE_get0_param', [X509_STORE], X509_VERIFY_PARAM, + macro=bool(not OPENSSL_1_1) or None) ssl_external('X509_get_default_cert_file_env', [], rffi.CCHARP) ssl_external('X509_get_default_cert_file', [], rffi.CCHARP) @@ -468,8 +450,14 @@ macro=True) ssl_external('sk_X509_OBJECT_value', [stack_st_X509_OBJECT, rffi.INT], X509_OBJECT, macro=True) -ssl_external('pypy_X509_OBJECT_data_x509', [X509_OBJECT], X509, - macro=True) +ssl_external('X509_OBJECT_get0_X509', [X509_OBJECT], X509, + macro=bool(not OPENSSL_1_1) or None) +ssl_external('X509_OBJECT_get_type', [X509_OBJECT], rffi.INT, + macro=bool(not OPENSSL_1_1) or None) +ssl_external('COMP_get_name', [COMP_METHOD], rffi.CCHARP, + macro=bool(not OPENSSL_1_1) or None) +ssl_external('COMP_get_type', [COMP_METHOD], rffi.INT, + macro=bool(not OPENSSL_1_1) or None) ssl_external('sk_DIST_POINT_num', [stack_st_DIST_POINT], rffi.INT, macro=True) ssl_external('sk_DIST_POINT_value', [stack_st_DIST_POINT, rffi.INT], DIST_POINT, @@ -511,8 +499,12 @@ # with the GIL held, and so is allowed to run in a RPython __del__ method. ssl_external('SSL_free', [SSL], lltype.Void, releasegil=False) ssl_external('SSL_CTX_free', [SSL_CTX], lltype.Void, releasegil=False) -ssl_external('CRYPTO_free', [rffi.VOIDP], lltype.Void) -libssl_OPENSSL_free = libssl_CRYPTO_free +if OPENSSL_1_1: + ssl_external('OPENSSL_free', [rffi.VOIDP], lltype.Void, macro=True) +else: + ssl_external('CRYPTO_free', [rffi.VOIDP], lltype.Void) + libssl_OPENSSL_free = libssl_CRYPTO_free + del libssl_CRYPTO_free ssl_external('SSL_write', [SSL, rffi.CCHARP, rffi.INT], rffi.INT, save_err=SAVE_ERR) @@ -576,10 +568,11 @@ SSL, rffi.CCHARPP, rffi.UINTP], lltype.Void) EVP_MD_CTX = rffi.COpaquePtr('EVP_MD_CTX', compilation_info=eci) -EVP_MD = lltype.Ptr(EVP_MD_st) +EVP_MD = rffi.COpaquePtr('EVP_MD') OpenSSL_add_all_digests = external( - 'OpenSSL_add_all_digests', [], lltype.Void) + 'OpenSSL_add_all_digests', [], lltype.Void, + macro=OPENSSL_1_1 or None) EVP_get_digestbyname = external( 'EVP_get_digestbyname', [rffi.CCHARP], EVP_MD) @@ -592,10 +585,25 @@ EVP_DigestFinal = external( 'EVP_DigestFinal', [EVP_MD_CTX, rffi.CCHARP, rffi.VOIDP], rffi.INT) +EVP_MD_size = external( + 'EVP_MD_size', [EVP_MD], rffi.INT) +EVP_MD_block_size = external( + 'EVP_MD_block_size', [EVP_MD], rffi.INT) EVP_MD_CTX_copy = external( 'EVP_MD_CTX_copy', [EVP_MD_CTX, EVP_MD_CTX], rffi.INT) -EVP_MD_CTX_cleanup = external( - 'EVP_MD_CTX_cleanup', [EVP_MD_CTX], rffi.INT, releasegil=False) +EVP_MD_CTX_new = external( + 'EVP_MD_CTX_new' if OPENSSL_1_1 else 'EVP_MD_CTX_create', + [], EVP_MD_CTX) +EVP_MD_CTX_free = external( + 'EVP_MD_CTX_free' if OPENSSL_1_1 else 'EVP_MD_CTX_destroy', + [EVP_MD_CTX], lltype.Void, releasegil=False) + +if OPENSSL_1_1: + PKCS5_PBKDF2_HMAC = external('PKCS5_PBKDF2_HMAC', [ + rffi.CCHARP, rffi.INT, rffi.CCHARP, rffi.INT, rffi.INT, EVP_MD, + rffi.INT, rffi.CCHARP], rffi.INT) +else: + PKCS5_PBKDF2_HMAC = None OBJ_NAME_CALLBACK = lltype.Ptr(lltype.FuncType( [OBJ_NAME, rffi.VOIDP], lltype.Void)) @@ -606,7 +614,7 @@ # Used for adding memory pressure. Last number is an (under?)estimate of # EVP_PKEY_CTX's size. # XXX: Make a better estimate here -HASH_MALLOC_SIZE = EVP_MD_SIZE + EVP_MD_CTX_SIZE \ +HASH_MALLOC_SIZE = 120 + 48 \ + rffi.sizeof(EVP_MD) * 2 + 208 def init_ssl(): diff --git a/rpython/translator/c/test/test_newgc.py b/rpython/translator/c/test/test_newgc.py --- a/rpython/translator/c/test/test_newgc.py +++ b/rpython/translator/c/test/test_newgc.py @@ -1575,15 +1575,13 @@ class A: def __init__(self): - self.ctx = lltype.malloc(ropenssl.EVP_MD_CTX.TO, - flavor='raw') digest = ropenssl.EVP_get_digestbyname('sha1') + self.ctx = ropenssl.EVP_MD_CTX_new() ropenssl.EVP_DigestInit(self.ctx, digest) rgc.add_memory_pressure(ropenssl.HASH_MALLOC_SIZE + 64) def __del__(self): - ropenssl.EVP_MD_CTX_cleanup(self.ctx) - lltype.free(self.ctx, flavor='raw') + ropenssl.EVP_MD_CTX_free(self.ctx) #A() --- can't call it here?? get glibc crashes on tannit64 def f(): am1 = am2 = am3 = None _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit