diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 36beb1777cf036..288b2914a25c1f 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -192,12 +192,17 @@ instead. ------------------------ --------- --------- ---------- --------- ----------- ----------- *SSLv2* yes no yes no no no *SSLv3* no yes yes no no no - *SSLv23* no yes yes yes yes yes + *SSLv23* [1]_ no yes yes yes yes yes *TLSv1* no no yes yes no no *TLSv1.1* no no yes no yes no *TLSv1.2* no no yes no no yes ======================== ========= ========= ========== ========= =========== =========== + .. rubric:: Footnotes + .. [1] TLS 1.3 protocol will be available with :data:`PROTOCOL_SSLv23` in + OpenSSL >= 1.1.1. There is no dedicated PROTOCOL constant for just + TLS 1.3. + .. note:: Which connections succeed will vary depending on the version of @@ -286,6 +291,11 @@ purposes. 3DES was dropped from the default cipher string. + .. versionchanged:: 2.7.15 + + 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. + .. function:: _https_verify_certificates(enable=True) Specifies whether or not server certificates are verified when creating @@ -701,6 +711,16 @@ Constants .. versionadded:: 2.7.9 +.. 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:: 2.7.15 + .. data:: OP_CIPHER_SERVER_PREFERENCE Use the server's cipher ordering preference, rather than the client's. @@ -765,6 +785,12 @@ Constants .. versionadded:: 2.7.9 +.. data:: HAS_TLSv1_3 + + Whether the OpenSSL library has built-in support for the TLS 1.3 protocol. + + .. versionadded:: 2.7.15 + .. 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 f28c863811fd98..22d478b56871b4 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -123,7 +123,7 @@ def _import_symbols(prefix): _import_symbols('PROTOCOL_') _import_symbols('VERIFY_') -from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN +from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3 from _ssl import _OPENSSL_API_VERSION @@ -157,6 +157,7 @@ def _import_symbols(prefix): # (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 @@ -168,6 +169,8 @@ def _import_symbols(prefix): # * 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' @@ -175,6 +178,7 @@ def _import_symbols(prefix): # 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 @@ -185,6 +189,8 @@ def _import_symbols(prefix): # * 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 d9ef232580c123..cfc03e343c7d6b 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -168,6 +168,13 @@ def test_constants(self): 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_random(self): v = ssl.RAND_status() @@ -2784,6 +2791,24 @@ def test_version_basic(self): 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 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 00000000000000..e76997ef836128 --- /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 5b4cec203addf6..f70af266731ad0 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4281,6 +4281,11 @@ init_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); @@ -4333,6 +4338,14 @@ init_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();