diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 3564855594c32d..6876a16555980c 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1262,7 +1262,7 @@ SSL sockets also have the following additional methods and attributes: .. method:: SSLSocket.shared_ciphers() - Return the list of ciphers available in both the client and server. Each + Return the list of ciphers shared by the client during the handshake. Each entry of the returned list is a three-value tuple containing the name of the cipher, the version of the SSL protocol that defines its use, and the number of secret bits the cipher uses. :meth:`~SSLSocket.shared_ciphers` returns diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 6e63a8872d9c6e..f3e28e5283df2f 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -2068,6 +2068,9 @@ def test_tls13_pha(self): import ssl if not ssl.HAS_TLSv1_3: self.skipTest('TLS 1.3 support required') + if 'BoringSSL' in ssl.OPENSSL_VERSION: + self.skipTest( + 'Post Handshake Authentication is not supported by BoringSSL.') # just check status of PHA flag h = client.HTTPSConnection('localhost', 443) self.assertTrue(h._context.post_handshake_auth) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index bd831ac22419af..2e7e703fa72eec 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -44,6 +44,7 @@ PROTOCOLS = sorted(ssl._PROTOCOL_NAMES) HOST = socket_helper.HOST +IS_BORINGSSL = "BoringSSL" in ssl.OPENSSL_VERSION IS_OPENSSL_3_0_0 = ssl.OPENSSL_VERSION_INFO >= (3, 0, 0) PY_SSL_DEFAULT_CIPHERS = sysconfig.get_config_var('PY_SSL_DEFAULT_CIPHERS') @@ -551,7 +552,7 @@ def test_openssl_version(self): else: openssl_ver = f"OpenSSL {major:d}.{minor:d}.{fix:d}" self.assertTrue( - s.startswith((openssl_ver, libressl_ver, "AWS-LC")), + s.startswith((openssl_ver, libressl_ver, "AWS-LC", "BoringSSL")), (s, t, hex(n)) ) @@ -906,6 +907,12 @@ def test_connect_ex_error(self): self.assertIn(rc, errors) def test_read_write_zero(self): + if IS_BORINGSSL: + # We had to revert the change which made this work for BoringSSL. + # This is because the change used APIs in OpenSSL which were + # mis-designed. When that mess is untangled, we can restore this + # test and the fix. See go/bio-read-ex-eof. + return # empty reads and writes now work, bpo-42854, bpo-31711 client_context, server_context, hostname = testing_context() server = ThreadedEchoServer(context=server_context) @@ -1078,6 +1085,26 @@ def test_min_max_version(self): ctx.maximum_version, ssl.TLSVersion.TLSv1_2 ) + if IS_BORINGSSL: + # BoringSSL's version APIs differ from OpenSSL's slightly. Skip the + # remainder of the tests for now, until the conflicts can be + # resolved. It is unlikely these differences will affect calling + # code. + # + # - BoringSSL treats zero as the default version, not the minimum or + # maximum supported one. This works better when we are deprecating + # a version or implementing a new experimental version. + # + # - After SSL_CTX_set_min_proto_version(0), BoringSSL's + # SSL_CTX_get_min_proto_version returns the version 0 was + # interpreted as, while OpenSSL remembers it was set as 0. + # + # - BoringSSL's legacy version-locked SSL_METHODs don't reject calls + # to set the version. This is a little simpler to implement. As + # everyone, Python and OpenSSL included, have deprecated these + # APIs, this doesn't seem worth putting much effort into. + return + ctx.minimum_version = ssl.TLSVersion.MINIMUM_SUPPORTED ctx.maximum_version = ssl.TLSVersion.TLSv1 self.assertEqual( @@ -1190,7 +1217,7 @@ def test_load_cert_chain(self): regex = re.compile(r"""( key values mismatch # OpenSSL | - KEY_VALUES_MISMATCH # AWS-LC + KEY_VALUES_MISMATCH # AWS-LC, BoringSSL )""", re.X) with self.assertRaisesRegex(ssl.SSLError, regex): ctx.load_cert_chain(CAFILE_CACERT, ONLYKEY) @@ -1322,12 +1349,14 @@ def test_load_verify_cadata(self): with self.assertRaisesRegex( ssl.SSLError, - "no start line: cadata does not contain a certificate" + "no start line: cadata does not contain a certificate" + + "|NO_START_LINE" ): ctx.load_verify_locations(cadata="broken") with self.assertRaisesRegex( ssl.SSLError, - "not enough data: cadata does not contain a certificate" + "not enough data: cadata does not contain a certificate" + + "|NOT_ENOUGH_DATA" ): ctx.load_verify_locations(cadata=b"broken") with self.assertRaises(ssl.SSLError): @@ -1671,7 +1700,7 @@ def test_lib_reason(self): regex = "(NO_START_LINE|UNSUPPORTED_PUBLIC_KEY_TYPE)" self.assertRegex(cm.exception.reason, regex) s = str(cm.exception) - self.assertTrue("NO_START_LINE" in s, s) + self.assertIn("NO_START_LINE", s) def test_subclass(self): # Check that the appropriate SSLError subclass is raised @@ -1855,7 +1884,7 @@ def test_connect_fail(self): regex = re.compile(r"""( certificate verify failed # OpenSSL | - CERTIFICATE_VERIFY_FAILED # AWS-LC + CERTIFICATE_VERIFY_FAILED # AWS-LC, BoringSSL )""", re.X) self.assertRaisesRegex(ssl.SSLError, regex, s.connect, self.server_addr) @@ -1929,7 +1958,7 @@ def test_connect_with_context_fail(self): regex = re.compile(r"""( certificate verify failed # OpenSSL | - CERTIFICATE_VERIFY_FAILED # AWS-LC + CERTIFICATE_VERIFY_FAILED # AWS-LC, BoringSSL )""", re.X) self.assertRaisesRegex(ssl.SSLError, regex, s.connect, self.server_addr) @@ -2138,6 +2167,8 @@ def test_bio_handshake(self): incoming = ssl.MemoryBIO() outgoing = ssl.MemoryBIO() ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + # This test requests tls-unique, which is not defined in TLS 1.3. + ctx.maximum_version = ssl.TLSVersion.TLSv1_2 self.assertTrue(ctx.check_hostname) self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) ctx.load_verify_locations(SIGNING_CA) @@ -2146,7 +2177,7 @@ def test_bio_handshake(self): self.assertIs(sslobj._sslobj.owner, sslobj) self.assertIsNone(sslobj.cipher()) self.assertIsNone(sslobj.version()) - self.assertIsNone(sslobj.shared_ciphers()) + self.assertIsNotNone(sslobj.shared_ciphers()) self.assertRaises(ValueError, sslobj.getpeercert) # tls-unique is not defined for TLSv1.3 # https://datatracker.ietf.org/doc/html/rfc8446#appendix-C.5 @@ -2154,7 +2185,7 @@ def test_bio_handshake(self): self.assertIsNone(sslobj.get_channel_binding('tls-unique')) self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake) self.assertTrue(sslobj.cipher()) - self.assertIsNone(sslobj.shared_ciphers()) + self.assertIsNotNone(sslobj.shared_ciphers()) self.assertIsNotNone(sslobj.version()) self.assertTrue(sslobj.getpeercert()) if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES and sslobj.version() != "TLSv1.3": @@ -2888,7 +2919,7 @@ def test_crl_check(self): regex = re.compile(r"""( certificate verify failed # OpenSSL | - CERTIFICATE_VERIFY_FAILED # AWS-LC + CERTIFICATE_VERIFY_FAILED # AWS-LC, BoringSSL )""", re.X) with server: with client_context.wrap_socket(socket.socket(), @@ -2928,7 +2959,7 @@ def test_check_hostname(self): regex = re.compile(r"""( certificate verify failed # OpenSSL | - CERTIFICATE_VERIFY_FAILED # AWS-LC + CERTIFICATE_VERIFY_FAILED # AWS-LC, BoringSSL )""", re.X) with server: with client_context.wrap_socket(socket.socket(), @@ -3021,6 +3052,10 @@ def test_verify_strict(self): cert = s.getpeercert() self.assertTrue(cert, "Can't get peer certificate.") + # TODO(gpshead): Is this accurate? If run, the test gets + # HANDSHAKE_FAILURE_ON_CLIENT_HELLO on connect(). + @unittest.skipIf(ssl.OPENSSL_VERSION == "BoringSSL", + "BoringSSL does not support dual RSA ECC?") def test_dual_rsa_ecc(self): client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) client_context.load_verify_locations(SIGNING_CA) @@ -3245,7 +3280,7 @@ def test_ssl_cert_verify_error(self): regex = re.compile(r"""( certificate verify failed # OpenSSL | - CERTIFICATE_VERIFY_FAILED # AWS-LC + CERTIFICATE_VERIFY_FAILED # AWS-LC, BoringSSL )""", re.X) self.assertRegex(repr(e), regex) @@ -3908,6 +3943,8 @@ def test_tls_unique_channel_binding(self): sys.stdout.write("\n") client_context, server_context, hostname = testing_context() + # tls-unique is not defined in TLS 1.3. + client_context.maximum_version = ssl.TLSVersion.TLSv1_2 # tls-unique is not defined for TLSv1.3 # https://datatracker.ietf.org/doc/html/rfc8446#appendix-C.5 @@ -3999,6 +4036,7 @@ def test_no_legacy_server_connect(self): sni_name=hostname) @unittest.skipIf(Py_DEBUG_WIN32, "Avoid mixing debug/release CRT on Windows") + @unittest.skipIf(IS_BORINGSSL, "BoringSSL does not support DHE ciphers") def test_dh_params(self): # Check we can get a connection with ephemeral Diffie-Hellman client_context, server_context, hostname = testing_context() @@ -4181,8 +4219,14 @@ def cb_raising(ssl_sock, server_name, initial_context): sni_name='supermessage') # Allow for flexible libssl error messages. - regex = "(SSLV3_ALERT_HANDSHAKE_FAILURE|NO_PRIVATE_VALUE)" - self.assertRegex(regex, cm.exception.reason) + regex = re.compile(r"""( + SSLV3_ALERT_HANDSHAKE_FAILURE # OpenSSL + | + NO_PRIVATE_VALUE # AWS-LC + | + HANDSHAKE_FAILURE_ON_CLIENT_HELLO # BoringSSL? TODO(gpshead): confirm + )""", re.X) + self.assertRegex(cm.exception.reason, regex) self.assertEqual(catch.unraisable.exc_type, ZeroDivisionError) def test_sni_callback_wrong_return_type(self): @@ -4207,7 +4251,7 @@ def cb_wrong_return_type(ssl_sock, server_name, initial_context): def test_shared_ciphers(self): client_context, server_context, hostname = testing_context() client_context.set_ciphers("AES128:AES256") - server_context.set_ciphers("AES256:eNULL") + server_context.set_ciphers("AES256") expected_algs = [ "AES256", "AES-256", # TLS 1.3 ciphers are always enabled @@ -4251,6 +4295,12 @@ def test_sendfile(self): self.assertEqual(s.recv(1024), TEST_DATA) def test_session(self): + # Assertions on session_stats are skipped in BoringSSL. BoringSSL + # does not maintain those statistics for thread safety. + def maybeAssertEqual(a, b): + if not IS_BORINGSSL: + self.assertEqual(a, b) + client_context, server_context, hostname = testing_context() # TODO: sessions aren't compatible with TLSv1.3 yet client_context.maximum_version = ssl.TLSVersion.TLSv1_2 @@ -4266,15 +4316,15 @@ def test_session(self): self.assertGreater(session.ticket_lifetime_hint, 0) self.assertFalse(stats['session_reused']) sess_stat = server_context.session_stats() - self.assertEqual(sess_stat['accept'], 1) - self.assertEqual(sess_stat['hits'], 0) + maybeAssertEqual(sess_stat['accept'], 1) + maybeAssertEqual(sess_stat['hits'], 0) # reuse session stats = server_params_test(client_context, server_context, session=session, sni_name=hostname) sess_stat = server_context.session_stats() - self.assertEqual(sess_stat['accept'], 2) - self.assertEqual(sess_stat['hits'], 1) + maybeAssertEqual(sess_stat['accept'], 2) + maybeAssertEqual(sess_stat['hits'], 1) self.assertTrue(stats['session_reused']) session2 = stats['session'] self.assertEqual(session2.id, session.id) @@ -4291,8 +4341,8 @@ def test_session(self): self.assertNotEqual(session3.id, session.id) self.assertNotEqual(session3, session) sess_stat = server_context.session_stats() - self.assertEqual(sess_stat['accept'], 3) - self.assertEqual(sess_stat['hits'], 1) + maybeAssertEqual(sess_stat['accept'], 3) + maybeAssertEqual(sess_stat['hits'], 1) # reuse session again stats = server_params_test(client_context, server_context, @@ -4304,8 +4354,8 @@ def test_session(self): self.assertGreaterEqual(session4.time, session.time) self.assertGreaterEqual(session4.timeout, session.timeout) sess_stat = server_context.session_stats() - self.assertEqual(sess_stat['accept'], 4) - self.assertEqual(sess_stat['hits'], 2) + maybeAssertEqual(sess_stat['accept'], 4) + maybeAssertEqual(sess_stat['hits'], 2) def test_session_handling(self): client_context, server_context, hostname = testing_context() @@ -4460,6 +4510,7 @@ def server_callback(identity): @unittest.skipUnless(has_tls_version('TLSv1_3'), "Test needs TLS 1.3") +@unittest.skipIf(IS_BORINGSSL, "PHA not supported b/117801554") class TestPostHandshakeAuth(unittest.TestCase): def test_pha_setter(self): protocols = [ diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 0e230f332ff6cb..a0b42a15d43ad7 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -118,6 +118,7 @@ static const py_hashentry_t py_hashes[] = { PY_HASH_ENTRY(Py_hash_sha256, "SHA256", SN_sha256, NID_sha256), PY_HASH_ENTRY(Py_hash_sha384, "SHA384", SN_sha384, NID_sha384), PY_HASH_ENTRY(Py_hash_sha512, "SHA512", SN_sha512, NID_sha512), +#ifndef OPENSSL_IS_BORINGSSL /* truncated sha2 */ PY_HASH_ENTRY(Py_hash_sha512_224, "SHA512_224", SN_sha512_224, NID_sha512_224), PY_HASH_ENTRY(Py_hash_sha512_256, "SHA512_256", SN_sha512_256, NID_sha512_256), @@ -132,6 +133,7 @@ static const py_hashentry_t py_hashes[] = { /* blake2 digest */ PY_HASH_ENTRY(Py_hash_blake2s, "blake2s256", SN_blake2s256, NID_blake2s256), PY_HASH_ENTRY(Py_hash_blake2b, "blake2b512", SN_blake2b512, NID_blake2b512), +#endif PY_HASH_ENTRY(NULL, NULL, NULL, 0), }; @@ -1826,6 +1828,7 @@ typedef struct _internal_name_mapper_state { } _InternalNameMapperState; +#ifndef OPENSSL_IS_BORINGSSL /* A callback function to pass to OpenSSL's OBJ_NAME_do_all(...) */ static void #if OPENSSL_VERSION_NUMBER >= 0x30000000L @@ -1854,6 +1857,7 @@ _openssl_hash_name_mapper(const EVP_MD *md, const char *from, Py_DECREF(py_name); } } +#endif // !OPENSSL_IS_BORINGSSL /* Ask OpenSSL for a list of supported ciphers, filling in a Python set. */ @@ -1862,12 +1866,42 @@ hashlib_md_meth_names(PyObject *module) { _InternalNameMapperState state = { .set = PyFrozenSet_New(NULL), +#ifndef OPENSSL_IS_BORINGSSL .error = 0 +#endif // !OPENSSL_IS_BORINGSSL }; if (state.set == NULL) { return -1; } +#if defined(OPENSSL_IS_BORINGSSL) + // This avoids a need to link with -ldecrepit for EVP_MD_do_all(). + // TODO(gpshead): Using CPython predefined constant internal C APIs + // would be better. + const char *boringssl_hash_names[] = { + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + NULL, + }; + for (int i=0; boringssl_hash_names[i] != NULL; ++i) { + PyObject *py_name = PyUnicode_FromString(boringssl_hash_names[i]); + if (py_name == NULL) { + Py_DECREF(state.set); + return -1; + } else { + if (PySet_Add(state.set, py_name) != 0) { + Py_DECREF(py_name); + Py_DECREF(state.set); + return -1; + }; + Py_DECREF(py_name); + } + } +#else #if OPENSSL_VERSION_NUMBER >= 0x30000000L // get algorithms from all activated providers in default context EVP_MD_do_all_provided(NULL, &_openssl_hash_name_mapper, &state); @@ -1879,6 +1913,7 @@ hashlib_md_meth_names(PyObject *module) Py_DECREF(state.set); return -1; } +#endif // !OPENSSL_IS_BORINGSSL return PyModule_Add(module, "openssl_md_meth_names", state.set); } diff --git a/Modules/_ssl.c b/Modules/_ssl.c index fbf914c4321922..fd4ad90a831644 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -75,6 +75,12 @@ # error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL" #endif +#ifdef OPENSSL_IS_BORINGSSL +# ifndef OPENSSL_NO_PSK +/* PSK APIs not supported by BoringSSL. */ +# define OPENSSL_NO_PSK 1 +# endif +#endif struct py_ssl_error_code { @@ -121,6 +127,7 @@ static void _PySSLFixErrno(void) { #define _PySSL_FIX_ERRNO _PySSLFixErrno() #endif +#ifndef OPENSSL_IS_BORINGSSL /* Include generated data (error codes) */ #if (OPENSSL_VERSION_NUMBER >= 0x30100000L) #include "_ssl_data_31.h" @@ -131,6 +138,7 @@ static void _PySSLFixErrno(void) { #else #error Unsupported OpenSSL version #endif +#endif // !OPENSSL_IS_BORINGSSL /* OpenSSL API 1.1.0+ does not include version methods */ #ifndef OPENSSL_NO_SSL3_METHOD @@ -471,10 +479,30 @@ fill_and_set_sslerror(_sslmodulestate *state, { PyObject *err_value = NULL, *reason_obj = NULL, *lib_obj = NULL; PyObject *verify_obj = NULL, *verify_code_obj = NULL; - PyObject *init_value, *msg, *key; + PyObject *init_value, *msg; if (errcode != 0) { +#if defined(OPENSSL_IS_BORINGSSL) + const char *lib_str, *reason_str; + + lib_str = ERR_lib_symbol_name(errcode); + if (lib_str != NULL) { + lib_obj = PyUnicode_FromString(lib_str); + if (lib_obj == NULL) { + goto fail; + } + } + + reason_str = ERR_reason_symbol_name(errcode); + if (reason_str != NULL) { + reason_obj = PyUnicode_FromString(reason_str); + if (reason_obj == NULL) { + goto fail; + } + } +#else int lib, reason; + PyObject *key; lib = ERR_GET_LIB(errcode); reason = ERR_GET_REASON(errcode); @@ -494,6 +522,7 @@ fill_and_set_sslerror(_sslmodulestate *state, if (lib_obj == NULL && PyErr_Occurred()) { goto fail; } +#endif /* OPENSSL_IS_BORINGSSL */ if (errstr == NULL) errstr = ERR_reason_error_string(errcode); } @@ -874,7 +903,8 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, SSL_set_mode(self->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_AUTO_RETRY); -#ifdef TLS1_3_VERSION +/* https://crbug.com/boringssl/78: BoringSSL will never support PHA. */ +#if defined(TLS1_3_VERSION) && !defined(OPENSSL_IS_BORINGSSL) if (sslctx->post_handshake_auth == 1) { if (socket_type == PY_SSL_SERVER) { /* bpo-37428: OpenSSL does not ignore SSL_VERIFY_POST_HANDSHAKE. @@ -1870,12 +1900,16 @@ static PyObject * _ssl__SSLSocket_get_verified_chain_impl(PySSLSocket *self) /*[clinic end generated code: output=802421163cdc3110 input=5fb0714f77e2bd51]*/ { +#ifdef OPENSSL_IS_BORINGSSL + Py_RETURN_NONE; +#else /* borrowed reference */ STACK_OF(X509) *chain = SSL_get0_verified_chain(self->ssl); if (chain == NULL) { Py_RETURN_NONE; } return _PySSL_CertificateFromX509Stack(self->ctx->state, chain, 1); +#endif } /*[clinic input] @@ -2022,44 +2056,25 @@ static PyObject * _ssl__SSLSocket_shared_ciphers_impl(PySSLSocket *self) /*[clinic end generated code: output=3d174ead2e42c4fd input=0bfe149da8fe6306]*/ { - STACK_OF(SSL_CIPHER) *server_ciphers; - STACK_OF(SSL_CIPHER) *client_ciphers; - int i, len; + STACK_OF(SSL_CIPHER) *ciphers; + int i; PyObject *res; - const SSL_CIPHER* cipher; - - /* Rather than use SSL_get_shared_ciphers, we use an equivalent algorithm because: - 1) It returns a colon separated list of strings, in an undefined - order, that we would have to post process back into tuples. - 2) It will return a truncated string with no indication that it has - done so, if the buffer is too small. - */ - - server_ciphers = SSL_get_ciphers(self->ssl); - if (!server_ciphers) - Py_RETURN_NONE; - client_ciphers = SSL_get_client_ciphers(self->ssl); - if (!client_ciphers) + ciphers = SSL_get_ciphers(self->ssl); + if (!ciphers) Py_RETURN_NONE; - res = PyList_New(sk_SSL_CIPHER_num(server_ciphers)); + res = PyList_New(sk_SSL_CIPHER_num(ciphers)); if (!res) return NULL; - len = 0; - for (i = 0; i < sk_SSL_CIPHER_num(server_ciphers); i++) { - cipher = sk_SSL_CIPHER_value(server_ciphers, i); - if (sk_SSL_CIPHER_find(client_ciphers, cipher) < 0) - continue; - - PyObject *tup = cipher_to_tuple(cipher); + for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) { + PyObject *tup = cipher_to_tuple(sk_SSL_CIPHER_value(ciphers, i)); if (!tup) { Py_DECREF(res); return NULL; } - PyList_SET_ITEM(res, len++, tup); + PyList_SET_ITEM(res, i, tup); } - Py_SET_SIZE(res, len); return res; } @@ -2389,8 +2404,14 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) do { PySSL_BEGIN_ALLOW_THREADS +#if defined(OPENSSL_IS_BORINGSSL) + retval = SSL_write(self->ssl, b->buf, b->len); + count = retval > 0 ? (size_t)retval : 0; + err = _PySSL_errno(retval <= 0, self->ssl, retval); +#else retval = SSL_write_ex(self->ssl, b->buf, (size_t)b->len, &count); err = _PySSL_errno(retval == 0, self->ssl, retval); +#endif PySSL_END_ALLOW_THREADS self->err = err; @@ -2424,7 +2445,11 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) err.ssl == SSL_ERROR_WANT_WRITE); Py_XDECREF(sock); +#if defined(OPENSSL_IS_BORINGSSL) + if (retval <= 0) +#else if (retval == 0) +#endif return PySSL_SetError(self, __FILE__, __LINE__); if (PySSL_ChainExceptions(self) < 0) return NULL; @@ -2542,8 +2567,14 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, do { PySSL_BEGIN_ALLOW_THREADS +#if defined(OPENSSL_IS_BORINGSSL) + retval = SSL_read(self->ssl, mem, len); + count = retval > 0 ? (size_t)retval : 0; + err = _PySSL_errno(retval <= 0, self->ssl, retval); +#else retval = SSL_read_ex(self->ssl, mem, (size_t)len, &count); err = _PySSL_errno(retval == 0, self->ssl, retval); +#endif PySSL_END_ALLOW_THREADS self->err = err; @@ -2577,7 +2608,11 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, } while (err.ssl == SSL_ERROR_WANT_READ || err.ssl == SSL_ERROR_WANT_WRITE); +#if defined(OPENSSL_IS_BORINGSSL) + if (retval <= 0) { +#else if (retval == 0) { +#endif PySSL_SetError(self, __FILE__, __LINE__); goto error; } @@ -2776,7 +2811,8 @@ static PyObject * _ssl__SSLSocket_verify_client_post_handshake_impl(PySSLSocket *self) /*[clinic end generated code: output=532147f3b1341425 input=6bfa874810a3d889]*/ { -#ifdef TLS1_3_VERSION +/* https://crbug.com/boringssl/78: BoringSSL will never support PHA. */ +#if defined(TLS1_3_VERSION) && !defined(OPENSSL_IS_BORINGSSL) int err = SSL_verify_client_post_handshake(self->ssl); if (err == 0) return _setSSLError(get_state_sock(self), NULL, 0, __FILE__, __LINE__); @@ -3199,7 +3235,8 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) X509_VERIFY_PARAM_set_flags(params, X509_V_FLAG_TRUSTED_FIRST); X509_VERIFY_PARAM_set_hostflags(params, self->hostflags); -#ifdef TLS1_3_VERSION +/* https://crbug.com/boringssl/78: BoringSSL will never support PHA. */ +#if defined(TLS1_3_VERSION) && !defined(OPENSSL_IS_BORINGSSL) self->post_handshake_auth = 0; SSL_CTX_set_post_handshake_auth(self->ctx, self->post_handshake_auth); #endif @@ -3613,7 +3650,11 @@ PyDoc_STRVAR(PySSLContext_num_tickets_doc, static PyObject * get_security_level(PySSLContext *self, void *c) { +#ifdef OPENSSL_IS_BORINGSSL + return PyLong_FromLong(0); +#else return PyLong_FromLong(SSL_CTX_get_security_level(self->ctx)); +#endif } PyDoc_STRVAR(PySSLContext_security_level_doc, "The current security level"); @@ -3711,14 +3752,15 @@ set_check_hostname(PySSLContext *self, PyObject *arg, void *c) static PyObject * get_post_handshake_auth(PySSLContext *self, void *c) { -#if TLS1_3_VERSION +#if defined(TLS1_3_VERSION) && !defined(OPENSSL_IS_BORINGSSL) return PyBool_FromLong(self->post_handshake_auth); #else Py_RETURN_NONE; #endif } -#if TLS1_3_VERSION +/* https://crbug.com/boringssl/78: BoringSSL will never support PHA. */ +#if defined(TLS1_3_VERSION) && !defined(OPENSSL_IS_BORINGSSL) static int set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) { if (arg == NULL) { @@ -4541,50 +4583,6 @@ set_sni_callback(PySSLContext *self, PyObject *arg, void *c) return 0; } -#if OPENSSL_VERSION_NUMBER < 0x30300000L -static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj) -{ - int ok; - X509_OBJECT *ret = X509_OBJECT_new(); - if (ret == NULL) { - return NULL; - } - switch (X509_OBJECT_get_type(obj)) { - case X509_LU_X509: - ok = X509_OBJECT_set1_X509(ret, X509_OBJECT_get0_X509(obj)); - break; - case X509_LU_CRL: - /* X509_OBJECT_get0_X509_CRL was not const-correct prior to 3.0.*/ - ok = X509_OBJECT_set1_X509_CRL( - ret, X509_OBJECT_get0_X509_CRL((X509_OBJECT *)obj)); - break; - default: - /* We cannot duplicate unrecognized types in a polyfill, but it is - * safe to leave an empty object. The caller will ignore it. */ - ok = 1; - break; - } - if (!ok) { - X509_OBJECT_free(ret); - return NULL; - } - return ret; -} - -static STACK_OF(X509_OBJECT) * -X509_STORE_get1_objects(X509_STORE *store) -{ - STACK_OF(X509_OBJECT) *ret; - if (!X509_STORE_lock(store)) { - return NULL; - } - ret = sk_X509_OBJECT_deep_copy(X509_STORE_get0_objects(store), - x509_object_dup, X509_OBJECT_free); - X509_STORE_unlock(store); - return ret; -} -#endif - PyDoc_STRVAR(PySSLContext_sni_callback_doc, "Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n\ \n\ @@ -4614,12 +4612,7 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self) int x509 = 0, crl = 0, ca = 0, i; store = SSL_CTX_get_cert_store(self->ctx); - objs = X509_STORE_get1_objects(store); - if (objs == NULL) { - PyErr_SetString(PyExc_MemoryError, "failed to query cert store"); - return NULL; - } - + objs = X509_STORE_get0_objects(store); for (i = 0; i < sk_X509_OBJECT_num(objs); i++) { obj = sk_X509_OBJECT_value(objs, i); switch (X509_OBJECT_get_type(obj)) { @@ -4633,11 +4626,12 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self) crl++; break; default: - /* Ignore unrecognized types. */ + /* 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 */ break; } } - sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free); return Py_BuildValue("{sisisi}", "x509", x509, "crl", crl, "x509_ca", ca); } @@ -4669,12 +4663,7 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form) } store = SSL_CTX_get_cert_store(self->ctx); - objs = X509_STORE_get1_objects(store); - if (objs == NULL) { - PyErr_SetString(PyExc_MemoryError, "failed to query cert store"); - goto error; - } - + objs = X509_STORE_get0_objects(store); for (i = 0; i < sk_X509_OBJECT_num(objs); i++) { X509_OBJECT *obj; X509 *cert; @@ -4702,11 +4691,9 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form) } Py_CLEAR(ci); } - sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free); return rlist; error: - sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free); Py_XDECREF(ci); Py_XDECREF(rlist); return NULL; @@ -4967,7 +4954,7 @@ static PyGetSetDef context_getsetlist[] = { {"options", (getter) get_options, (setter) set_options, NULL}, {"post_handshake_auth", (getter) get_post_handshake_auth, -#ifdef TLS1_3_VERSION +#if defined(TLS1_3_VERSION) && !defined(OPENSSL_IS_BORINGSSL) (setter) set_post_handshake_auth, #else NULL, @@ -6318,6 +6305,11 @@ sslmodule_init_constants(PyObject *m) static int sslmodule_init_errorcodes(PyObject *module) { +#if defined(OPENSSL_IS_BORINGSSL) + /* BoringSSL does not use error tables and instead provides the necessary + API directly. */ + return 0; +#else _sslmodulestate *state = get_ssl_state(module); struct py_ssl_error_code *errcode; @@ -6366,6 +6358,7 @@ sslmodule_init_errorcodes(PyObject *module) } return 0; +#endif /* OPENSSL_IS_BORINGSSL */ } static void diff --git a/configure b/configure index 542783e723d934..c2591f2a30b4ac 100755 --- a/configure +++ b/configure @@ -28185,8 +28185,8 @@ main (void) OBJ_nid2sn(NID_md5); OBJ_nid2sn(NID_sha1); - OBJ_nid2sn(NID_sha3_512); - OBJ_nid2sn(NID_blake2b512); + OBJ_nid2sn(NID_sha256); + OBJ_nid2sn(NID_sha512); EVP_PBE_scrypt(NULL, 0, NULL, 0, 2, 8, 1, 0, NULL, 0); ; diff --git a/configure.ac b/configure.ac index fc62bfe5a1d4c4..c096dec7413373 100644 --- a/configure.ac +++ b/configure.ac @@ -7166,8 +7166,8 @@ WITH_SAVE_ENV([ ], [ OBJ_nid2sn(NID_md5); OBJ_nid2sn(NID_sha1); - OBJ_nid2sn(NID_sha3_512); - OBJ_nid2sn(NID_blake2b512); + OBJ_nid2sn(NID_sha256); + OBJ_nid2sn(NID_sha512); EVP_PBE_scrypt(NULL, 0, NULL, 0, 2, 8, 1, 0, NULL, 0); ])], [ac_cv_working_openssl_hashlib=yes], [ac_cv_working_openssl_hashlib=no]) ])