| From e950ea68dab006944af194c9910b8f2341d1437d Mon Sep 17 00:00:00 2001 |
| From: Christian Heimes <christian@python.org> |
| Date: Thu, 7 Sep 2017 20:23:52 -0700 |
| Subject: [PATCH] bpo-29136: Add TLS 1.3 cipher suites and OP_NO_TLSv1_3 |
| (GH-1363) (#3444) |
| |
| * bpo-29136: Add TLS 1.3 support |
| |
| TLS 1.3 introduces a new, distinct set of cipher suites. The TLS 1.3 |
| cipher suites don't overlap with cipher suites from TLS 1.2 and earlier. |
| Since Python sets its own set of permitted ciphers, TLS 1.3 handshake |
| will fail as soon as OpenSSL 1.1.1 is released. Let's enable the common |
| AES-GCM and ChaCha20 suites. |
| |
| Additionally the flag OP_NO_TLSv1_3 is added. It defaults to 0 (no op) with |
| OpenSSL prior to 1.1.1. This allows applications to opt-out from TLS 1.3 |
| now. |
| |
| Signed-off-by: Christian Heimes <christian@python.org>. |
| (cherry picked from commit cb5b68abdeb1b1d56c581d5b4d647018703d61e3) |
| |
| Upstream-Status: Backport |
| [https://github.com/python/cpython/commit/cb5b68abdeb1b1d56c581d5b4d647018703d61e3] |
| |
| Signed-off-by: Anuj Mittal <anuj.mittal@intel.com> |
| --- |
| Doc/library/ssl.rst | 21 ++++++++++++++ |
| Lib/ssl.py | 7 +++++ |
| Lib/test/test_ssl.py | 29 ++++++++++++++++++- |
| .../2017-09-04-16-39-49.bpo-29136.vSn1oR.rst | 1 + |
| Modules/_ssl.c | 13 +++++++++ |
| 5 files changed, 70 insertions(+), 1 deletion(-) |
| create mode 100644 Misc/NEWS.d/next/Library/2017-09-04-16-39-49.bpo-29136.vSn1oR.rst |
| |
| diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst |
| index 14f2d68217..29c5e94cf6 100644 |
| --- a/Doc/library/ssl.rst |
| +++ b/Doc/library/ssl.rst |
| @@ -285,6 +285,11 @@ purposes. |
| |
| 3DES was dropped from the default cipher string. |
| |
| + .. versionchanged:: 3.7 |
| + |
| + TLS 1.3 cipher suites TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, |
| + and TLS_CHACHA20_POLY1305_SHA256 were added to the default cipher string. |
| + |
| |
| Random generation |
| ^^^^^^^^^^^^^^^^^ |
| @@ -719,6 +724,16 @@ Constants |
| |
| .. versionadded:: 3.4 |
| |
| +.. data:: OP_NO_TLSv1_3 |
| + |
| + Prevents a TLSv1.3 connection. This option is only applicable in conjunction |
| + with :const:`PROTOCOL_TLS`. It prevents the peers from choosing TLSv1.3 as |
| + the protocol version. TLS 1.3 is available with OpenSSL 1.1.1 or later. |
| + When Python has been compiled against an older version of OpenSSL, the |
| + flag defaults to *0*. |
| + |
| + .. versionadded:: 3.7 |
| + |
| .. data:: OP_CIPHER_SERVER_PREFERENCE |
| |
| Use the server's cipher ordering preference, rather than the client's. |
| @@ -783,6 +798,12 @@ Constants |
| |
| .. versionadded:: 3.3 |
| |
| +.. data:: HAS_TLSv1_3 |
| + |
| + Whether the OpenSSL library has built-in support for the TLS 1.3 protocol. |
| + |
| + .. versionadded:: 3.7 |
| + |
| .. data:: CHANNEL_BINDING_TYPES |
| |
| List of supported TLS channel binding types. Strings in this list |
| diff --git a/Lib/ssl.py b/Lib/ssl.py |
| index 4d302a78fa..f233e72e1f 100644 |
| --- a/Lib/ssl.py |
| +++ b/Lib/ssl.py |
| @@ -122,6 +122,7 @@ _import_symbols('OP_') |
| _import_symbols('ALERT_DESCRIPTION_') |
| _import_symbols('SSL_ERROR_') |
| _import_symbols('VERIFY_') |
| +from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3 |
| |
| from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN |
| |
| @@ -162,6 +163,7 @@ else: |
| # (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL') |
| # Enable a better set of ciphers by default |
| # This list has been explicitly chosen to: |
| +# * TLS 1.3 ChaCha20 and AES-GCM cipher suites |
| # * Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE) |
| # * Prefer ECDHE over DHE for better performance |
| # * Prefer AEAD over CBC for better performance and security |
| @@ -173,6 +175,8 @@ else: |
| # * Disable NULL authentication, NULL encryption, 3DES and MD5 MACs |
| # for security reasons |
| _DEFAULT_CIPHERS = ( |
| + 'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:' |
| + 'TLS13-AES-128-GCM-SHA256:' |
| 'ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:' |
| 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:' |
| '!aNULL:!eNULL:!MD5:!3DES' |
| @@ -180,6 +184,7 @@ _DEFAULT_CIPHERS = ( |
| |
| # Restricted and more secure ciphers for the server side |
| # This list has been explicitly chosen to: |
| +# * TLS 1.3 ChaCha20 and AES-GCM cipher suites |
| # * Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE) |
| # * Prefer ECDHE over DHE for better performance |
| # * Prefer AEAD over CBC for better performance and security |
| @@ -190,6 +195,8 @@ _DEFAULT_CIPHERS = ( |
| # * Disable NULL authentication, NULL encryption, MD5 MACs, DSS, RC4, and |
| # 3DES for security reasons |
| _RESTRICTED_SERVER_CIPHERS = ( |
| + 'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:' |
| + 'TLS13-AES-128-GCM-SHA256:' |
| 'ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:' |
| 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:' |
| '!aNULL:!eNULL:!MD5:!DSS:!RC4:!3DES' |
| diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py |
| index f91af7bd05..1acc12ec2d 100644 |
| --- a/Lib/test/test_ssl.py |
| +++ b/Lib/test/test_ssl.py |
| @@ -150,6 +150,13 @@ class BasicSocketTests(unittest.TestCase): |
| ssl.OP_NO_COMPRESSION |
| self.assertIn(ssl.HAS_SNI, {True, False}) |
| self.assertIn(ssl.HAS_ECDH, {True, False}) |
| + ssl.OP_NO_SSLv2 |
| + ssl.OP_NO_SSLv3 |
| + ssl.OP_NO_TLSv1 |
| + ssl.OP_NO_TLSv1_3 |
| + if ssl.OPENSSL_VERSION_INFO >= (1, 0, 1): |
| + ssl.OP_NO_TLSv1_1 |
| + ssl.OP_NO_TLSv1_2 |
| |
| def test_str_for_enums(self): |
| # Make sure that the PROTOCOL_* constants have enum-like string |
| @@ -3028,12 +3035,33 @@ else: |
| self.assertEqual(s.version(), 'TLSv1') |
| self.assertIs(s.version(), None) |
| |
| + @unittest.skipUnless(ssl.HAS_TLSv1_3, |
| + "test requires TLSv1.3 enabled OpenSSL") |
| + def test_tls1_3(self): |
| + context = ssl.SSLContext(ssl.PROTOCOL_TLS) |
| + context.load_cert_chain(CERTFILE) |
| + # disable all but TLS 1.3 |
| + context.options |= ( |
| + ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1_2 |
| + ) |
| + with ThreadedEchoServer(context=context) as server: |
| + with context.wrap_socket(socket.socket()) as s: |
| + s.connect((HOST, server.port)) |
| + self.assertIn(s.cipher()[0], [ |
| + 'TLS13-AES-256-GCM-SHA384', |
| + 'TLS13-CHACHA20-POLY1305-SHA256', |
| + 'TLS13-AES-128-GCM-SHA256', |
| + ]) |
| + |
| @unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL") |
| def test_default_ecdh_curve(self): |
| # Issue #21015: elliptic curve-based Diffie Hellman key exchange |
| # should be enabled by default on SSL contexts. |
| context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) |
| context.load_cert_chain(CERTFILE) |
| + # TLSv1.3 defaults to PFS key agreement and no longer has KEA in |
| + # cipher name. |
| + context.options |= ssl.OP_NO_TLSv1_3 |
| # Prior to OpenSSL 1.0.0, ECDH ciphers have to be enabled |
| # explicitly using the 'ECCdraft' cipher alias. Otherwise, |
| # our default cipher list should prefer ECDH-based ciphers |
| @@ -3394,7 +3422,6 @@ else: |
| s.sendfile(file) |
| self.assertEqual(s.recv(1024), TEST_DATA) |
| |
| - |
| def test_main(verbose=False): |
| if support.verbose: |
| import warnings |
| diff --git a/Misc/NEWS.d/next/Library/2017-09-04-16-39-49.bpo-29136.vSn1oR.rst b/Misc/NEWS.d/next/Library/2017-09-04-16-39-49.bpo-29136.vSn1oR.rst |
| new file mode 100644 |
| index 0000000000..e76997ef83 |
| --- /dev/null |
| +++ b/Misc/NEWS.d/next/Library/2017-09-04-16-39-49.bpo-29136.vSn1oR.rst |
| @@ -0,0 +1 @@ |
| +Add TLS 1.3 cipher suites and OP_NO_TLSv1_3. |
| diff --git a/Modules/_ssl.c b/Modules/_ssl.c |
| index 0d5c121d2c..c71d89607c 100644 |
| --- a/Modules/_ssl.c |
| +++ b/Modules/_ssl.c |
| @@ -4842,6 +4842,11 @@ PyInit__ssl(void) |
| #if HAVE_TLSv1_2 |
| PyModule_AddIntConstant(m, "OP_NO_TLSv1_1", SSL_OP_NO_TLSv1_1); |
| PyModule_AddIntConstant(m, "OP_NO_TLSv1_2", SSL_OP_NO_TLSv1_2); |
| +#endif |
| +#ifdef SSL_OP_NO_TLSv1_3 |
| + PyModule_AddIntConstant(m, "OP_NO_TLSv1_3", SSL_OP_NO_TLSv1_3); |
| +#else |
| + PyModule_AddIntConstant(m, "OP_NO_TLSv1_3", 0); |
| #endif |
| PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE", |
| SSL_OP_CIPHER_SERVER_PREFERENCE); |
| @@ -4890,6 +4895,14 @@ PyInit__ssl(void) |
| Py_INCREF(r); |
| PyModule_AddObject(m, "HAS_ALPN", r); |
| |
| +#if defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3) |
| + r = Py_True; |
| +#else |
| + r = Py_False; |
| +#endif |
| + Py_INCREF(r); |
| + PyModule_AddObject(m, "HAS_TLSv1_3", r); |
| + |
| /* Mappings for error codes */ |
| err_codes_to_names = PyDict_New(); |
| err_names_to_codes = PyDict_New(); |
| -- |
| 2.17.1 |
| |