Skip to content

Commit 72ef4fc

Browse files
miss-islingtontiran
authored andcommitted
[3.7] bpo-33618: Enable TLS 1.3 in tests (GH-7079) (GH-7082)
TLS 1.3 behaves slightly different than TLS 1.2. Session tickets and TLS client cert auth are now handled after the initialy handshake. Tests now either send/recv data to trigger session and client certs. Or tests ignore ConnectionResetError / BrokenPipeError on the server side to handle clients that force-close the socket fd. To test TLS 1.3, OpenSSL 1.1.1-pre7-dev (git master + OpenSSL PR openssl/openssl#6340) is required. Signed-off-by: Christian Heimes <[email protected]> (cherry picked from commit 529525f)
1 parent 508d769 commit 72ef4fc

File tree

9 files changed

+142
-46
lines changed

9 files changed

+142
-46
lines changed

Doc/library/ssl.rst

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2587,7 +2587,33 @@ successful call of :func:`~ssl.RAND_add`, :func:`~ssl.RAND_bytes` or
25872587
:func:`~ssl.RAND_pseudo_bytes` is sufficient.
25882588

25892589

2590-
.. ssl-libressl:
2590+
.. _ssl-tlsv1_3:
2591+
2592+
TLS 1.3
2593+
-------
2594+
2595+
.. versionadded:: 3.7
2596+
2597+
Python has provisional and experimental support for TLS 1.3 with OpenSSL
2598+
1.1.1. The new protocol behaves slightly differently than previous version
2599+
of TLS/SSL. Some new TLS 1.3 features are not yet available.
2600+
2601+
- TLS 1.3 uses a disjunct set of cipher suites. All AES-GCM and
2602+
ChaCha20 cipher suites are enabled by default. The method
2603+
:meth:`SSLContext.set_ciphers` cannot enable or disable any TLS 1.3
2604+
ciphers yet, but :meth:`SSLContext.get_cipers` returns them.
2605+
- Session tickets are no longer sent as part of the initial handshake and
2606+
are handled differently. :attr:`SSLSocket.session` and :class:`SSLSession`
2607+
are not compatible with TLS 1.3.
2608+
- Client-side certificates are also no longer verified during the initial
2609+
handshake. A server can request a certificate at any time. Clients
2610+
process certificate requests while they send or receive application data
2611+
from the server.
2612+
- TLS 1.3 features like early data, deferred TLS client cert request,
2613+
signature algorithm configuration, and rekeying are not supported yet.
2614+
2615+
2616+
.. _ssl-libressl:
25912617

25922618
LibreSSL support
25932619
----------------

Doc/whatsnew/3.7.rst

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,8 +1244,8 @@ Host name validation can be customized with
12441244
.. note::
12451245
The improved host name check requires a *libssl* implementation compatible
12461246
with OpenSSL 1.0.2 or 1.1. Consequently, OpenSSL 0.9.8 and 1.0.1 are no
1247-
longer supported and LibreSSL is temporarily not supported until it gains
1248-
the necessary OpenSSL 1.0.2 APIs.
1247+
longer supported. The ssl module is mostly compatible with LibreSSL 2.7.2
1248+
and newer.
12491249

12501250
The ``ssl`` module no longer sends IP addresses in SNI TLS extension.
12511251
(Contributed by Christian Heimes in :issue:`32185`.)
@@ -1270,8 +1270,12 @@ rather than the U-label form (``"pythön.org"``). (Contributed by
12701270
Nathaniel J. Smith and Christian Heimes in :issue:`28414`.)
12711271

12721272
The ``ssl`` module has preliminary and experimental support for TLS 1.3 and
1273-
OpenSSL 1.1.1. (Contributed by Christian Heimes in :issue:`32947`,
1274-
:issue:`20995`, :issue:`29136`, and :issue:`30622`)
1273+
OpenSSL 1.1.1. At the time of Python 3.7.0 release, OpenSSL 1.1.1 is still
1274+
under development and TLS 1.3 hasn't been finalized yet. The TLS 1.3
1275+
handshake and protocol behaves slightly differently than TLS 1.2 and earlier,
1276+
see :ref:`ssl-tlsv1_3`.
1277+
(Contributed by Christian Heimes in :issue:`32947`, :issue:`20995`,
1278+
:issue:`29136`, :issue:`30622` and :issue:`33618`)
12751279

12761280
:class:`~ssl.SSLSocket` and :class:`~ssl.SSLObject` no longer have a public
12771281
constructor. Direct instantiation was never a documented and supported

Lib/test/test_asyncio/test_sslproto.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ def test_start_tls_server_1(self):
251251

252252
server_context = test_utils.simple_server_sslcontext()
253253
client_context = test_utils.simple_client_sslcontext()
254+
# TODO: fix TLSv1.3 support
255+
client_context.options |= ssl.OP_NO_TLSv1_3
254256

255257
def client(sock, addr):
256258
time.sleep(0.5)

Lib/test/test_asyncio/utils.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ def simple_server_sslcontext():
7474
server_context.load_cert_chain(ONLYCERT, ONLYKEY)
7575
server_context.check_hostname = False
7676
server_context.verify_mode = ssl.CERT_NONE
77-
# TODO: fix TLSv1.3 support
78-
server_context.options |= ssl.OP_NO_TLSv1_3
7977
return server_context
8078

8179

Lib/test/test_ftplib.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ class DummyFTPServer(asyncore.dispatcher, threading.Thread):
257257
def __init__(self, address, af=socket.AF_INET):
258258
threading.Thread.__init__(self)
259259
asyncore.dispatcher.__init__(self)
260+
self.daemon = True
260261
self.create_socket(af, socket.SOCK_STREAM)
261262
self.bind(address)
262263
self.listen(5)
@@ -312,8 +313,6 @@ class SSLConnection(asyncore.dispatcher):
312313

313314
def secure_connection(self):
314315
context = ssl.SSLContext()
315-
# TODO: fix TLSv1.3 support
316-
context.options |= ssl.OP_NO_TLSv1_3
317316
context.load_cert_chain(CERTFILE)
318317
socket = context.wrap_socket(self.socket,
319318
suppress_ragged_eofs=False,
@@ -405,7 +404,7 @@ def handle_error(self):
405404

406405
def close(self):
407406
if (isinstance(self.socket, ssl.SSLSocket) and
408-
self.socket._sslobj is not None):
407+
self.socket._sslobj is not None):
409408
self._do_ssl_shutdown()
410409
else:
411410
super(SSLConnection, self).close()
@@ -910,8 +909,6 @@ def test_auth_issued_twice(self):
910909
def test_context(self):
911910
self.client.quit()
912911
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
913-
# TODO: fix TLSv1.3 support
914-
ctx.options |= ssl.OP_NO_TLSv1_3
915912
ctx.check_hostname = False
916913
ctx.verify_mode = ssl.CERT_NONE
917914
self.assertRaises(ValueError, ftplib.FTP_TLS, keyfile=CERTFILE,
@@ -944,8 +941,6 @@ def test_ccc(self):
944941
def test_check_hostname(self):
945942
self.client.quit()
946943
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
947-
# TODO: fix TLSv1.3 support
948-
ctx.options |= ssl.OP_NO_TLSv1_3
949944
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
950945
self.assertEqual(ctx.check_hostname, True)
951946
ctx.load_verify_locations(CAFILE)
@@ -982,6 +977,7 @@ def setUp(self):
982977
self.sock.settimeout(20)
983978
self.port = support.bind_port(self.sock)
984979
self.server_thread = threading.Thread(target=self.server)
980+
self.server_thread.daemon = True
985981
self.server_thread.start()
986982
# Wait for the server to be ready.
987983
self.evt.wait()

Lib/test/test_poplib.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,6 @@ def cmd_stls(self, arg):
153153
if self.tls_active is False:
154154
self.push('+OK Begin TLS negotiation')
155155
context = ssl.SSLContext()
156-
# TODO: fix TLSv1.3 support
157-
context.options |= ssl.OP_NO_TLSv1_3
158156
context.load_cert_chain(CERTFILE)
159157
tls_sock = context.wrap_socket(self.socket,
160158
server_side=True,
@@ -206,6 +204,7 @@ class DummyPOP3Server(asyncore.dispatcher, threading.Thread):
206204
def __init__(self, address, af=socket.AF_INET):
207205
threading.Thread.__init__(self)
208206
asyncore.dispatcher.__init__(self)
207+
self.daemon = True
209208
self.create_socket(af, socket.SOCK_STREAM)
210209
self.bind(address)
211210
self.listen(5)
@@ -370,8 +369,6 @@ def test_stls(self):
370369
def test_stls_context(self):
371370
expected = b'+OK Begin TLS negotiation'
372371
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
373-
# TODO: fix TLSv1.3 support
374-
ctx.options |= ssl.OP_NO_TLSv1_3
375372
ctx.load_verify_locations(CAFILE)
376373
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
377374
self.assertEqual(ctx.check_hostname, True)
@@ -412,8 +409,6 @@ def test__all__(self):
412409

413410
def test_context(self):
414411
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
415-
# TODO: fix TLSv1.3 support
416-
ctx.options |= ssl.OP_NO_TLSv1_3
417412
ctx.check_hostname = False
418413
ctx.verify_mode = ssl.CERT_NONE
419414
self.assertRaises(ValueError, poplib.POP3_SSL, self.server.host,
@@ -482,7 +477,7 @@ def setUp(self):
482477
self.sock.settimeout(60) # Safety net. Look issue 11812
483478
self.port = test_support.bind_port(self.sock)
484479
self.thread = threading.Thread(target=self.server, args=(self.evt,self.sock))
485-
self.thread.setDaemon(True)
480+
self.thread.daemon = True
486481
self.thread.start()
487482
self.evt.wait()
488483

Lib/test/test_ssl.py

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,6 +1846,7 @@ def test_connect_capath(self):
18461846
s.connect(self.server_addr)
18471847
cert = s.getpeercert()
18481848
self.assertTrue(cert)
1849+
18491850
# Same with a bytes `capath` argument
18501851
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
18511852
ctx.verify_mode = ssl.CERT_REQUIRED
@@ -1861,8 +1862,6 @@ def test_connect_cadata(self):
18611862
der = ssl.PEM_cert_to_DER_cert(pem)
18621863
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
18631864
ctx.verify_mode = ssl.CERT_REQUIRED
1864-
# TODO: fix TLSv1.3 support
1865-
ctx.options |= ssl.OP_NO_TLSv1_3
18661865
ctx.load_verify_locations(cadata=pem)
18671866
with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s:
18681867
s.connect(self.server_addr)
@@ -1872,8 +1871,6 @@ def test_connect_cadata(self):
18721871
# same with DER
18731872
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
18741873
ctx.verify_mode = ssl.CERT_REQUIRED
1875-
# TODO: fix TLSv1.3 support
1876-
ctx.options |= ssl.OP_NO_TLSv1_3
18771874
ctx.load_verify_locations(cadata=der)
18781875
with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s:
18791876
s.connect(self.server_addr)
@@ -2129,11 +2126,21 @@ def wrap_conn(self):
21292126
self.sock, server_side=True)
21302127
self.server.selected_npn_protocols.append(self.sslconn.selected_npn_protocol())
21312128
self.server.selected_alpn_protocols.append(self.sslconn.selected_alpn_protocol())
2132-
except (ssl.SSLError, ConnectionResetError, OSError) as e:
2129+
except (ConnectionResetError, BrokenPipeError) as e:
21332130
# We treat ConnectionResetError as though it were an
21342131
# SSLError - OpenSSL on Ubuntu abruptly closes the
21352132
# connection when asked to use an unsupported protocol.
21362133
#
2134+
# BrokenPipeError is raised in TLS 1.3 mode, when OpenSSL
2135+
# tries to send session tickets after handshake.
2136+
# https://github.com/openssl/openssl/issues/6342
2137+
self.server.conn_errors.append(str(e))
2138+
if self.server.chatty:
2139+
handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n")
2140+
self.running = False
2141+
self.close()
2142+
return False
2143+
except (ssl.SSLError, OSError) as e:
21372144
# OSError may occur with wrong protocols, e.g. both
21382145
# sides use PROTOCOL_TLS_SERVER.
21392146
#
@@ -2240,11 +2247,22 @@ def run(self):
22402247
sys.stdout.write(" server: read %r (%s), sending back %r (%s)...\n"
22412248
% (msg, ctype, msg.lower(), ctype))
22422249
self.write(msg.lower())
2250+
except ConnectionResetError:
2251+
# XXX: OpenSSL 1.1.1 sometimes raises ConnectionResetError
2252+
# when connection is not shut down gracefully.
2253+
if self.server.chatty and support.verbose:
2254+
sys.stdout.write(
2255+
" Connection reset by peer: {}\n".format(
2256+
self.addr)
2257+
)
2258+
self.close()
2259+
self.running = False
22432260
except OSError:
22442261
if self.server.chatty:
22452262
handle_error("Test server failure:\n")
22462263
self.close()
22472264
self.running = False
2265+
22482266
# normally, we'd just stop here, but for the test
22492267
# harness, we want to stop the server
22502268
self.server.stop()
@@ -2319,6 +2337,11 @@ def run(self):
23192337
pass
23202338
except KeyboardInterrupt:
23212339
self.stop()
2340+
except BaseException as e:
2341+
if support.verbose and self.chatty:
2342+
sys.stdout.write(
2343+
' connection handling failed: ' + repr(e) + '\n')
2344+
23222345
self.sock.close()
23232346

23242347
def stop(self):
@@ -2766,8 +2789,6 @@ def test_check_hostname_idn(self):
27662789

27672790
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
27682791
server_context.load_cert_chain(IDNSANSFILE)
2769-
# TODO: fix TLSv1.3 support
2770-
server_context.options |= ssl.OP_NO_TLSv1_3
27712792

27722793
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
27732794
context.verify_mode = ssl.CERT_REQUIRED
@@ -2818,7 +2839,7 @@ def test_check_hostname_idn(self):
28182839
with self.assertRaises(ssl.CertificateError):
28192840
s.connect((HOST, server.port))
28202841

2821-
def test_wrong_cert(self):
2842+
def test_wrong_cert_tls12(self):
28222843
"""Connecting when the server rejects the client's certificate
28232844
28242845
Launch a server with CERT_REQUIRED, and check that trying to
@@ -2829,9 +2850,8 @@ def test_wrong_cert(self):
28292850
client_context.load_cert_chain(WRONG_CERT)
28302851
# require TLS client authentication
28312852
server_context.verify_mode = ssl.CERT_REQUIRED
2832-
# TODO: fix TLSv1.3 support
2833-
# With TLS 1.3, test fails with exception in server thread
2834-
server_context.options |= ssl.OP_NO_TLSv1_3
2853+
# TLS 1.3 has different handshake
2854+
client_context.maximum_version = ssl.TLSVersion.TLSv1_2
28352855

28362856
server = ThreadedEchoServer(
28372857
context=server_context, chatty=True, connectionchatty=True,
@@ -2856,6 +2876,36 @@ def test_wrong_cert(self):
28562876
else:
28572877
self.fail("Use of invalid cert should have failed!")
28582878

2879+
@unittest.skipUnless(ssl.HAS_TLSv1_3, "Test needs TLS 1.3")
2880+
def test_wrong_cert_tls13(self):
2881+
client_context, server_context, hostname = testing_context()
2882+
client_context.load_cert_chain(WRONG_CERT)
2883+
server_context.verify_mode = ssl.CERT_REQUIRED
2884+
server_context.minimum_version = ssl.TLSVersion.TLSv1_3
2885+
client_context.minimum_version = ssl.TLSVersion.TLSv1_3
2886+
2887+
server = ThreadedEchoServer(
2888+
context=server_context, chatty=True, connectionchatty=True,
2889+
)
2890+
with server, \
2891+
client_context.wrap_socket(socket.socket(),
2892+
server_hostname=hostname) as s:
2893+
# TLS 1.3 perform client cert exchange after handshake
2894+
s.connect((HOST, server.port))
2895+
try:
2896+
s.write(b'data')
2897+
s.read(4)
2898+
except ssl.SSLError as e:
2899+
if support.verbose:
2900+
sys.stdout.write("\nSSLError is %r\n" % e)
2901+
except OSError as e:
2902+
if e.errno != errno.ECONNRESET:
2903+
raise
2904+
if support.verbose:
2905+
sys.stdout.write("\nsocket.error is %r\n" % e)
2906+
else:
2907+
self.fail("Use of invalid cert should have failed!")
2908+
28592909
def test_rude_shutdown(self):
28602910
"""A brutal shutdown of an SSL server should raise an OSError
28612911
in the client when attempting handshake.
@@ -3432,14 +3482,16 @@ def serve():
34323482
# Block on the accept and wait on the connection to close.
34333483
evt.set()
34343484
remote, peer = server.accept()
3435-
remote.recv(1)
3485+
remote.send(remote.recv(4))
34363486

34373487
t = threading.Thread(target=serve)
34383488
t.start()
34393489
# Client wait until server setup and perform a connect.
34403490
evt.wait()
34413491
client = context.wrap_socket(socket.socket())
34423492
client.connect((host, port))
3493+
client.send(b'data')
3494+
client.recv()
34433495
client_addr = client.getsockname()
34443496
client.close()
34453497
t.join()
@@ -3492,7 +3544,7 @@ def test_version_basic(self):
34923544
self.assertIs(s.version(), None)
34933545
self.assertIs(s._sslobj, None)
34943546
s.connect((HOST, server.port))
3495-
if ssl.OPENSSL_VERSION_INFO >= (1, 1, 1):
3547+
if IS_OPENSSL_1_1_1 and ssl.HAS_TLSv1_3:
34963548
self.assertEqual(s.version(), 'TLSv1.3')
34973549
elif ssl.OPENSSL_VERSION_INFO >= (1, 0, 2):
34983550
self.assertEqual(s.version(), 'TLSv1.2')
@@ -3601,8 +3653,6 @@ def test_tls_unique_channel_binding(self):
36013653
sys.stdout.write("\n")
36023654

36033655
client_context, server_context, hostname = testing_context()
3604-
# TODO: fix TLSv1.3 support
3605-
client_context.options |= ssl.OP_NO_TLSv1_3
36063656

36073657
server = ThreadedEchoServer(context=server_context,
36083658
chatty=True,
@@ -3621,7 +3671,10 @@ def test_tls_unique_channel_binding(self):
36213671

36223672
# check if it is sane
36233673
self.assertIsNotNone(cb_data)
3624-
self.assertEqual(len(cb_data), 12) # True for TLSv1
3674+
if s.version() == 'TLSv1.3':
3675+
self.assertEqual(len(cb_data), 48)
3676+
else:
3677+
self.assertEqual(len(cb_data), 12) # True for TLSv1
36253678

36263679
# and compare with the peers version
36273680
s.write(b"CB tls-unique\n")
@@ -3643,7 +3696,10 @@ def test_tls_unique_channel_binding(self):
36433696
# is it really unique
36443697
self.assertNotEqual(cb_data, new_cb_data)
36453698
self.assertIsNotNone(cb_data)
3646-
self.assertEqual(len(cb_data), 12) # True for TLSv1
3699+
if s.version() == 'TLSv1.3':
3700+
self.assertEqual(len(cb_data), 48)
3701+
else:
3702+
self.assertEqual(len(cb_data), 12) # True for TLSv1
36473703
s.write(b"CB tls-unique\n")
36483704
peer_data_repr = s.read().strip()
36493705
self.assertEqual(peer_data_repr,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Finalize and document preliminary and experimental TLS 1.3 support with
2+
OpenSSL 1.1.1

0 commit comments

Comments
 (0)