diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 69bea1ed4a0..031c3618bef 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -193,11 +193,11 @@ instead. .. table:: ======================== ============ ============ ============= ========= =========== =========== - *client* / **server** **SSLv2** **SSLv3** **TLS** **TLSv1** **TLSv1.1** **TLSv1.2** + *client* / **server** **SSLv2** **SSLv3** **TLS** [3]_ **TLSv1** **TLSv1.1** **TLSv1.2** ------------------------ ------------ ------------ ------------- --------- ----------- ----------- *SSLv2* yes no no [1]_ no no no *SSLv3* no yes no [2]_ no no no - *TLS* (*SSLv23*) no [1]_ no [2]_ yes yes yes yes + *TLS* (*SSLv23*) [3]_ no [1]_ no [2]_ 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 @@ -206,6 +206,9 @@ instead. .. rubric:: Footnotes .. [1] :class:`SSLContext` disables SSLv2 with :data:`OP_NO_SSLv2` by default. .. [2] :class:`SSLContext` disables SSLv3 with :data:`OP_NO_SSLv3` by default. + .. [3] TLS 1.3 protocol will be available with :data:`PROTOCOL_TLS` in + OpenSSL >= 1.1.1. There is no dedicated PROTOCOL constant for just + TLS 1.3. .. note:: @@ -294,6 +297,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 ^^^^^^^^^^^^^^^^^ @@ -764,6 +772,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. @@ -838,6 +856,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 7a574dcb2b1..1f3a31a9b79 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -115,7 +115,7 @@ except ImportError: pass -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 @@ -178,6 +178,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 @@ -189,6 +190,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' @@ -196,6 +199,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 @@ -206,6 +210,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 747661bc6d0..fe9f6939d33 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -170,6 +170,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 @@ -3098,12 +3105,33 @@ class ThreadedTests(unittest.TestCase): 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 @@ -3532,6 +3560,10 @@ class ThreadedTests(unittest.TestCase): context2.load_verify_locations(CERTFILE) context2.load_cert_chain(CERTFILE) + # TODO: session reuse does not work with TLS 1.3 + context.options |= ssl.OP_NO_TLSv1_3 + context2.options |= ssl.OP_NO_TLSv1_3 + server = ThreadedEchoServer(context=context, chatty=False) with server: with context.wrap_socket(socket.socket()) as s: 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 00000000000..e76997ef836 --- /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 5ea354a60aa..f7379257956 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -5349,6 +5349,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); @@ -5398,6 +5403,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();