Skip to content

Commit f97eb88

Browse files
miss-islingtonned-deily
authored andcommitted
[3.7] bpo-37440: Enable TLS 1.3 post-handshake auth in http.client (GH-14448) (GH-14496)
Post-handshake authentication is required for conditional client cert authentication with TLS 1.3. https://bugs.python.org/issue37440 (cherry picked from commit d1bd6e7) Co-authored-by: Christian Heimes <christian@python.org> https://bugs.python.org/issue37440
1 parent 5b45fb0 commit f97eb88

File tree

4 files changed

+32
-0
lines changed

4 files changed

+32
-0
lines changed

Doc/library/http.client.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ The module provides the following classes:
9494
:func:`ssl._create_unverified_context` can be passed to the *context*
9595
parameter.
9696

97+
.. versionchanged:: 3.7.4
98+
This class now enables TLS 1.3
99+
:attr:`ssl.SSLContext.post_handshake_auth` for the default *context* or
100+
when *cert_file* is passed with a custom *context*.
101+
97102
.. deprecated:: 3.6
98103

99104
*key_file* and *cert_file* are deprecated in favor of *context*.

Lib/http/client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,6 +1381,9 @@ def __init__(self, host, port=None, key_file=None, cert_file=None,
13811381
self.cert_file = cert_file
13821382
if context is None:
13831383
context = ssl._create_default_https_context()
1384+
# enable PHA for TLS 1.3 connections if available
1385+
if context.post_handshake_auth is not None:
1386+
context.post_handshake_auth = True
13841387
will_verify = context.verify_mode != ssl.CERT_NONE
13851388
if check_hostname is None:
13861389
check_hostname = context.check_hostname
@@ -1389,6 +1392,10 @@ def __init__(self, host, port=None, key_file=None, cert_file=None,
13891392
"either CERT_OPTIONAL or CERT_REQUIRED")
13901393
if key_file or cert_file:
13911394
context.load_cert_chain(cert_file, key_file)
1395+
# cert and key file means the user wants to authenticate.
1396+
# enable TLS 1.3 PHA implicitly even for custom contexts.
1397+
if context.post_handshake_auth is not None:
1398+
context.post_handshake_auth = True
13921399
self._context = context
13931400
if check_hostname is not None:
13941401
self._context.check_hostname = check_hostname

Lib/test/test_httplib.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,6 +1748,24 @@ def test_host_port(self):
17481748
self.assertEqual(h, c.host)
17491749
self.assertEqual(p, c.port)
17501750

1751+
def test_tls13_pha(self):
1752+
import ssl
1753+
if not ssl.HAS_TLSv1_3:
1754+
self.skipTest('TLS 1.3 support required')
1755+
# just check status of PHA flag
1756+
h = client.HTTPSConnection('localhost', 443)
1757+
self.assertTrue(h._context.post_handshake_auth)
1758+
1759+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
1760+
self.assertFalse(context.post_handshake_auth)
1761+
h = client.HTTPSConnection('localhost', 443, context=context)
1762+
self.assertIs(h._context, context)
1763+
self.assertFalse(h._context.post_handshake_auth)
1764+
1765+
h = client.HTTPSConnection('localhost', 443, context=context,
1766+
cert_file=CERT_localhost)
1767+
self.assertTrue(h._context.post_handshake_auth)
1768+
17511769

17521770
class RequestBodyTest(TestCase):
17531771
"""Test cases where a request includes a message body."""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
http.client now enables TLS 1.3 post-handshake authentication for default
2+
context or if a cert_file is passed to HTTPSConnection.

0 commit comments

Comments
 (0)