Skip to content

Commit 9f2b3d4

Browse files
authored
[3.6] 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 cb5b68a)
1 parent f032e92 commit 9f2b3d4

File tree

5 files changed

+79
-3
lines changed

5 files changed

+79
-3
lines changed

Doc/library/ssl.rst

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,11 @@ instead.
193193
.. table::
194194

195195
======================== ============ ============ ============= ========= =========== ===========
196-
*client* / **server** **SSLv2** **SSLv3** **TLS** **TLSv1** **TLSv1.1** **TLSv1.2**
196+
*client* / **server** **SSLv2** **SSLv3** **TLS** [3]_ **TLSv1** **TLSv1.1** **TLSv1.2**
197197
------------------------ ------------ ------------ ------------- --------- ----------- -----------
198198
*SSLv2* yes no no [1]_ no no no
199199
*SSLv3* no yes no [2]_ no no no
200-
*TLS* (*SSLv23*) no [1]_ no [2]_ yes yes yes yes
200+
*TLS* (*SSLv23*) [3]_ no [1]_ no [2]_ yes yes yes yes
201201
*TLSv1* no no yes yes no no
202202
*TLSv1.1* no no yes no yes no
203203
*TLSv1.2* no no yes no no yes
@@ -206,6 +206,9 @@ instead.
206206
.. rubric:: Footnotes
207207
.. [1] :class:`SSLContext` disables SSLv2 with :data:`OP_NO_SSLv2` by default.
208208
.. [2] :class:`SSLContext` disables SSLv3 with :data:`OP_NO_SSLv3` by default.
209+
.. [3] TLS 1.3 protocol will be available with :data:`PROTOCOL_TLS` in
210+
OpenSSL >= 1.1.1. There is no dedicated PROTOCOL constant for just
211+
TLS 1.3.
209212
210213
.. note::
211214

@@ -294,6 +297,11 @@ purposes.
294297

295298
3DES was dropped from the default cipher string.
296299

300+
.. versionchanged:: 3.7
301+
302+
TLS 1.3 cipher suites TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384,
303+
and TLS_CHACHA20_POLY1305_SHA256 were added to the default cipher string.
304+
297305

298306
Random generation
299307
^^^^^^^^^^^^^^^^^
@@ -760,6 +768,16 @@ Constants
760768

761769
.. versionadded:: 3.4
762770

771+
.. data:: OP_NO_TLSv1_3
772+
773+
Prevents a TLSv1.3 connection. This option is only applicable in conjunction
774+
with :const:`PROTOCOL_TLS`. It prevents the peers from choosing TLSv1.3 as
775+
the protocol version. TLS 1.3 is available with OpenSSL 1.1.1 or later.
776+
When Python has been compiled against an older version of OpenSSL, the
777+
flag defaults to *0*.
778+
779+
.. versionadded:: 3.7
780+
763781
.. data:: OP_CIPHER_SERVER_PREFERENCE
764782

765783
Use the server's cipher ordering preference, rather than the client's.
@@ -834,6 +852,12 @@ Constants
834852

835853
.. versionadded:: 3.3
836854

855+
.. data:: HAS_TLSv1_3
856+
857+
Whether the OpenSSL library has built-in support for the TLS 1.3 protocol.
858+
859+
.. versionadded:: 3.7
860+
837861
.. data:: CHANNEL_BINDING_TYPES
838862

839863
List of supported TLS channel binding types. Strings in this list

Lib/ssl.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
pass
116116

117117

118-
from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN
118+
from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3
119119
from _ssl import _OPENSSL_API_VERSION
120120

121121

@@ -178,6 +178,7 @@
178178
# (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL')
179179
# Enable a better set of ciphers by default
180180
# This list has been explicitly chosen to:
181+
# * TLS 1.3 ChaCha20 and AES-GCM cipher suites
181182
# * Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE)
182183
# * Prefer ECDHE over DHE for better performance
183184
# * Prefer AEAD over CBC for better performance and security
@@ -189,13 +190,16 @@
189190
# * Disable NULL authentication, NULL encryption, 3DES and MD5 MACs
190191
# for security reasons
191192
_DEFAULT_CIPHERS = (
193+
'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:'
194+
'TLS13-AES-128-GCM-SHA256:'
192195
'ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:'
193196
'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:'
194197
'!aNULL:!eNULL:!MD5:!3DES'
195198
)
196199

197200
# Restricted and more secure ciphers for the server side
198201
# This list has been explicitly chosen to:
202+
# * TLS 1.3 ChaCha20 and AES-GCM cipher suites
199203
# * Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE)
200204
# * Prefer ECDHE over DHE for better performance
201205
# * Prefer AEAD over CBC for better performance and security
@@ -206,6 +210,8 @@
206210
# * Disable NULL authentication, NULL encryption, MD5 MACs, DSS, RC4, and
207211
# 3DES for security reasons
208212
_RESTRICTED_SERVER_CIPHERS = (
213+
'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:'
214+
'TLS13-AES-128-GCM-SHA256:'
209215
'ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:'
210216
'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:'
211217
'!aNULL:!eNULL:!MD5:!DSS:!RC4:!3DES'

Lib/test/test_ssl.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ def test_constants(self):
176176
ssl.OP_NO_COMPRESSION
177177
self.assertIn(ssl.HAS_SNI, {True, False})
178178
self.assertIn(ssl.HAS_ECDH, {True, False})
179+
ssl.OP_NO_SSLv2
180+
ssl.OP_NO_SSLv3
181+
ssl.OP_NO_TLSv1
182+
ssl.OP_NO_TLSv1_3
183+
if ssl.OPENSSL_VERSION_INFO >= (1, 0, 1):
184+
ssl.OP_NO_TLSv1_1
185+
ssl.OP_NO_TLSv1_2
179186

180187
def test_str_for_enums(self):
181188
# Make sure that the PROTOCOL_* constants have enum-like string
@@ -3091,12 +3098,33 @@ def test_version_basic(self):
30913098
self.assertEqual(s.version(), 'TLSv1')
30923099
self.assertIs(s.version(), None)
30933100

3101+
@unittest.skipUnless(ssl.HAS_TLSv1_3,
3102+
"test requires TLSv1.3 enabled OpenSSL")
3103+
def test_tls1_3(self):
3104+
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
3105+
context.load_cert_chain(CERTFILE)
3106+
# disable all but TLS 1.3
3107+
context.options |= (
3108+
ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1_2
3109+
)
3110+
with ThreadedEchoServer(context=context) as server:
3111+
with context.wrap_socket(socket.socket()) as s:
3112+
s.connect((HOST, server.port))
3113+
self.assertIn(s.cipher()[0], [
3114+
'TLS13-AES-256-GCM-SHA384',
3115+
'TLS13-CHACHA20-POLY1305-SHA256',
3116+
'TLS13-AES-128-GCM-SHA256',
3117+
])
3118+
30943119
@unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL")
30953120
def test_default_ecdh_curve(self):
30963121
# Issue #21015: elliptic curve-based Diffie Hellman key exchange
30973122
# should be enabled by default on SSL contexts.
30983123
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
30993124
context.load_cert_chain(CERTFILE)
3125+
# TLSv1.3 defaults to PFS key agreement and no longer has KEA in
3126+
# cipher name.
3127+
context.options |= ssl.OP_NO_TLSv1_3
31003128
# Prior to OpenSSL 1.0.0, ECDH ciphers have to be enabled
31013129
# explicitly using the 'ECCdraft' cipher alias. Otherwise,
31023130
# our default cipher list should prefer ECDH-based ciphers
@@ -3525,6 +3553,10 @@ def test_session_handling(self):
35253553
context2.load_verify_locations(CERTFILE)
35263554
context2.load_cert_chain(CERTFILE)
35273555

3556+
# TODO: session reuse does not work with TLS 1.3
3557+
context.options |= ssl.OP_NO_TLSv1_3
3558+
context2.options |= ssl.OP_NO_TLSv1_3
3559+
35283560
server = ThreadedEchoServer(context=context, chatty=False)
35293561
with server:
35303562
with context.wrap_socket(socket.socket()) as s:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add TLS 1.3 cipher suites and OP_NO_TLSv1_3.

Modules/_ssl.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5362,6 +5362,11 @@ PyInit__ssl(void)
53625362
#if HAVE_TLSv1_2
53635363
PyModule_AddIntConstant(m, "OP_NO_TLSv1_1", SSL_OP_NO_TLSv1_1);
53645364
PyModule_AddIntConstant(m, "OP_NO_TLSv1_2", SSL_OP_NO_TLSv1_2);
5365+
#endif
5366+
#ifdef SSL_OP_NO_TLSv1_3
5367+
PyModule_AddIntConstant(m, "OP_NO_TLSv1_3", SSL_OP_NO_TLSv1_3);
5368+
#else
5369+
PyModule_AddIntConstant(m, "OP_NO_TLSv1_3", 0);
53655370
#endif
53665371
PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE",
53675372
SSL_OP_CIPHER_SERVER_PREFERENCE);
@@ -5411,6 +5416,14 @@ PyInit__ssl(void)
54115416
Py_INCREF(r);
54125417
PyModule_AddObject(m, "HAS_ALPN", r);
54135418

5419+
#if defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3)
5420+
r = Py_True;
5421+
#else
5422+
r = Py_False;
5423+
#endif
5424+
Py_INCREF(r);
5425+
PyModule_AddObject(m, "HAS_TLSv1_3", r);
5426+
54145427
/* Mappings for error codes */
54155428
err_codes_to_names = PyDict_New();
54165429
err_names_to_codes = PyDict_New();

0 commit comments

Comments
 (0)