mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
bpo-43669: PEP 644: Require OpenSSL 1.1.1 or newer (GH-23014)
- Remove HAVE_X509_VERIFY_PARAM_SET1_HOST check - Update hashopenssl to require OpenSSL 1.1.1 - multissltests only OpenSSL > 1.1.0 - ALPN is always supported - SNI is always supported - Remove deprecated NPN code. Python wrappers are no-op. - ECDH is always supported - Remove OPENSSL_VERSION_1_1 macro - Remove locking callbacks - Drop PY_OPENSSL_1_1_API macro - Drop HAVE_SSL_CTX_CLEAR_OPTIONS macro - SSL_CTRL_GET_MAX_PROTO_VERSION is always defined now - security level is always available now - get_num_tickets is available with TLS 1.3 - X509_V_ERR MISMATCH is always available now - Always set SSL_MODE_RELEASE_BUFFERS - X509_V_FLAG_TRUSTED_FIRST is always available - get_ciphers is always supported - SSL_CTX_set_keylog_callback is always available - Update Modules/Setup with static link example - Mention PEP in whatsnew - Drop 1.0.2 and 1.1.0 from GHA tests
This commit is contained in:
parent
b467d9a240
commit
39258d3595
17 changed files with 5144 additions and 8274 deletions
|
@ -40,7 +40,6 @@ Py_DEBUG_WIN32 = Py_DEBUG and sys.platform == 'win32'
|
|||
PROTOCOLS = sorted(ssl._PROTOCOL_NAMES)
|
||||
HOST = socket_helper.HOST
|
||||
IS_LIBRESSL = ssl.OPENSSL_VERSION.startswith('LibreSSL')
|
||||
IS_OPENSSL_1_1_0 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0)
|
||||
IS_OPENSSL_1_1_1 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 1)
|
||||
IS_OPENSSL_3_0_0 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (3, 0, 0)
|
||||
PY_SSL_DEFAULT_CIPHERS = sysconfig.get_config_var('PY_SSL_DEFAULT_CIPHERS')
|
||||
|
@ -270,18 +269,6 @@ def handle_error(prefix):
|
|||
if support.verbose:
|
||||
sys.stdout.write(prefix + exc_format)
|
||||
|
||||
def can_clear_options():
|
||||
# 0.9.8m or higher
|
||||
return ssl._OPENSSL_API_VERSION >= (0, 9, 8, 13, 15)
|
||||
|
||||
def no_sslv2_implies_sslv3_hello():
|
||||
# 0.9.7h or higher
|
||||
return ssl.OPENSSL_VERSION_INFO >= (0, 9, 7, 8, 15)
|
||||
|
||||
def have_verify_flags():
|
||||
# 0.9.8 or higher
|
||||
return ssl.OPENSSL_VERSION_INFO >= (0, 9, 8, 0, 15)
|
||||
|
||||
def _have_secp_curves():
|
||||
if not ssl.HAS_ECDH:
|
||||
return False
|
||||
|
@ -372,17 +359,15 @@ class BasicSocketTests(unittest.TestCase):
|
|||
ssl.OP_SINGLE_DH_USE
|
||||
if ssl.HAS_ECDH:
|
||||
ssl.OP_SINGLE_ECDH_USE
|
||||
if ssl.OPENSSL_VERSION_INFO >= (1, 0):
|
||||
ssl.OP_NO_COMPRESSION
|
||||
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
|
||||
ssl.OP_NO_TLSv1_1
|
||||
ssl.OP_NO_TLSv1_2
|
||||
self.assertEqual(ssl.PROTOCOL_TLS, ssl.PROTOCOL_SSLv23)
|
||||
|
||||
def test_private_init(self):
|
||||
|
@ -1161,7 +1146,6 @@ class ContextTests(unittest.TestCase):
|
|||
self.assertNotIn("RC4", name)
|
||||
self.assertNotIn("3DES", name)
|
||||
|
||||
@unittest.skipIf(ssl.OPENSSL_VERSION_INFO < (1, 0, 2, 0, 0), 'OpenSSL too old')
|
||||
def test_get_ciphers(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
ctx.set_ciphers('AESGCM')
|
||||
|
@ -1181,15 +1165,11 @@ class ContextTests(unittest.TestCase):
|
|||
self.assertEqual(default, ctx.options)
|
||||
ctx.options |= ssl.OP_NO_TLSv1
|
||||
self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options)
|
||||
if can_clear_options():
|
||||
ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1)
|
||||
self.assertEqual(default, ctx.options)
|
||||
ctx.options = 0
|
||||
# Ubuntu has OP_NO_SSLv3 forced on by default
|
||||
self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3)
|
||||
else:
|
||||
with self.assertRaises(ValueError):
|
||||
ctx.options = 0
|
||||
ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1)
|
||||
self.assertEqual(default, ctx.options)
|
||||
ctx.options = 0
|
||||
# Ubuntu has OP_NO_SSLv3 forced on by default
|
||||
self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3)
|
||||
|
||||
def test_verify_mode_protocol(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||
|
@ -1327,8 +1307,6 @@ class ContextTests(unittest.TestCase):
|
|||
}
|
||||
self.assertIn(ctx.security_level, security_level_range)
|
||||
|
||||
@unittest.skipUnless(have_verify_flags(),
|
||||
"verify_flags need OpenSSL > 0.9.8")
|
||||
def test_verify_flags(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
# default value
|
||||
|
@ -1797,7 +1775,6 @@ class ContextTests(unittest.TestCase):
|
|||
obj = ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO())
|
||||
self.assertIsInstance(obj, MySSLObject)
|
||||
|
||||
@unittest.skipUnless(IS_OPENSSL_1_1_1, "Test requires OpenSSL 1.1.1")
|
||||
def test_num_tickest(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
self.assertEqual(ctx.num_tickets, 2)
|
||||
|
@ -2956,8 +2933,6 @@ class ThreadedTests(unittest.TestCase):
|
|||
after = ssl.cert_time_to_seconds(cert['notAfter'])
|
||||
self.assertLess(before, after)
|
||||
|
||||
@unittest.skipUnless(have_verify_flags(),
|
||||
"verify_flags need OpenSSL > 0.9.8")
|
||||
def test_crl_check(self):
|
||||
if support.verbose:
|
||||
sys.stdout.write("\n")
|
||||
|
@ -3859,12 +3834,7 @@ class ThreadedTests(unittest.TestCase):
|
|||
self.assertIs(s.version(), None)
|
||||
self.assertIs(s._sslobj, None)
|
||||
s.connect((HOST, server.port))
|
||||
if IS_OPENSSL_1_1_1 and has_tls_version('TLSv1_3'):
|
||||
self.assertEqual(s.version(), 'TLSv1.3')
|
||||
elif ssl.OPENSSL_VERSION_INFO >= (1, 0, 2):
|
||||
self.assertEqual(s.version(), 'TLSv1.2')
|
||||
else: # 0.9.8 to 1.0.1
|
||||
self.assertIn(s.version(), ('TLSv1', 'TLSv1.2'))
|
||||
self.assertEqual(s.version(), 'TLSv1.3')
|
||||
self.assertIs(s._sslobj, None)
|
||||
self.assertIs(s.version(), None)
|
||||
|
||||
|
@ -3966,8 +3936,6 @@ class ThreadedTests(unittest.TestCase):
|
|||
# explicitly using the 'ECCdraft' cipher alias. Otherwise,
|
||||
# our default cipher list should prefer ECDH-based ciphers
|
||||
# automatically.
|
||||
if ssl.OPENSSL_VERSION_INFO < (1, 0, 0):
|
||||
context.set_ciphers("ECCdraft:ECDH")
|
||||
with ThreadedEchoServer(context=context) as server:
|
||||
with context.wrap_socket(socket.socket()) as s:
|
||||
s.connect((HOST, server.port))
|
||||
|
@ -4099,15 +4067,11 @@ class ThreadedTests(unittest.TestCase):
|
|||
server_context.set_ciphers("ECDHE:!eNULL:!aNULL")
|
||||
server_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
|
||||
try:
|
||||
stats = server_params_test(client_context, server_context,
|
||||
chatty=True, connectionchatty=True,
|
||||
sni_name=hostname)
|
||||
server_params_test(client_context, server_context,
|
||||
chatty=True, connectionchatty=True,
|
||||
sni_name=hostname)
|
||||
except ssl.SSLError:
|
||||
pass
|
||||
else:
|
||||
# OpenSSL 1.0.2 does not fail although it should.
|
||||
if IS_OPENSSL_1_1_0:
|
||||
self.fail("mismatch curve did not fail")
|
||||
self.fail("mismatch curve did not fail")
|
||||
|
||||
def test_selected_alpn_protocol(self):
|
||||
# selected_alpn_protocol() is None unless ALPN is used.
|
||||
|
@ -4117,7 +4081,6 @@ class ThreadedTests(unittest.TestCase):
|
|||
sni_name=hostname)
|
||||
self.assertIs(stats['client_alpn_protocol'], None)
|
||||
|
||||
@unittest.skipUnless(ssl.HAS_ALPN, "ALPN support required")
|
||||
def test_selected_alpn_protocol_if_server_uses_alpn(self):
|
||||
# selected_alpn_protocol() is None unless ALPN is used by the client.
|
||||
client_context, server_context, hostname = testing_context()
|
||||
|
@ -4127,7 +4090,6 @@ class ThreadedTests(unittest.TestCase):
|
|||
sni_name=hostname)
|
||||
self.assertIs(stats['client_alpn_protocol'], None)
|
||||
|
||||
@unittest.skipUnless(ssl.HAS_ALPN, "ALPN support needed for this test")
|
||||
def test_alpn_protocols(self):
|
||||
server_protocols = ['foo', 'bar', 'milkshake']
|
||||
protocol_tests = [
|
||||
|
@ -4150,22 +4112,17 @@ class ThreadedTests(unittest.TestCase):
|
|||
except ssl.SSLError as e:
|
||||
stats = e
|
||||
|
||||
if (expected is None and IS_OPENSSL_1_1_0
|
||||
and ssl.OPENSSL_VERSION_INFO < (1, 1, 0, 6)):
|
||||
# OpenSSL 1.1.0 to 1.1.0e raises handshake error
|
||||
self.assertIsInstance(stats, ssl.SSLError)
|
||||
else:
|
||||
msg = "failed trying %s (s) and %s (c).\n" \
|
||||
"was expecting %s, but got %%s from the %%s" \
|
||||
% (str(server_protocols), str(client_protocols),
|
||||
str(expected))
|
||||
client_result = stats['client_alpn_protocol']
|
||||
self.assertEqual(client_result, expected,
|
||||
msg % (client_result, "client"))
|
||||
server_result = stats['server_alpn_protocols'][-1] \
|
||||
if len(stats['server_alpn_protocols']) else 'nothing'
|
||||
self.assertEqual(server_result, expected,
|
||||
msg % (server_result, "server"))
|
||||
msg = "failed trying %s (s) and %s (c).\n" \
|
||||
"was expecting %s, but got %%s from the %%s" \
|
||||
% (str(server_protocols), str(client_protocols),
|
||||
str(expected))
|
||||
client_result = stats['client_alpn_protocol']
|
||||
self.assertEqual(client_result, expected,
|
||||
msg % (client_result, "client"))
|
||||
server_result = stats['server_alpn_protocols'][-1] \
|
||||
if len(stats['server_alpn_protocols']) else 'nothing'
|
||||
self.assertEqual(server_result, expected,
|
||||
msg % (server_result, "server"))
|
||||
|
||||
def test_selected_npn_protocol(self):
|
||||
# selected_npn_protocol() is None unless NPN is used
|
||||
|
@ -4175,31 +4132,8 @@ class ThreadedTests(unittest.TestCase):
|
|||
sni_name=hostname)
|
||||
self.assertIs(stats['client_npn_protocol'], None)
|
||||
|
||||
@unittest.skipUnless(ssl.HAS_NPN, "NPN support needed for this test")
|
||||
def test_npn_protocols(self):
|
||||
server_protocols = ['http/1.1', 'spdy/2']
|
||||
protocol_tests = [
|
||||
(['http/1.1', 'spdy/2'], 'http/1.1'),
|
||||
(['spdy/2', 'http/1.1'], 'http/1.1'),
|
||||
(['spdy/2', 'test'], 'spdy/2'),
|
||||
(['abc', 'def'], 'abc')
|
||||
]
|
||||
for client_protocols, expected in protocol_tests:
|
||||
client_context, server_context, hostname = testing_context()
|
||||
server_context.set_npn_protocols(server_protocols)
|
||||
client_context.set_npn_protocols(client_protocols)
|
||||
stats = server_params_test(client_context, server_context,
|
||||
chatty=True, connectionchatty=True,
|
||||
sni_name=hostname)
|
||||
msg = "failed trying %s (s) and %s (c).\n" \
|
||||
"was expecting %s, but got %%s from the %%s" \
|
||||
% (str(server_protocols), str(client_protocols),
|
||||
str(expected))
|
||||
client_result = stats['client_npn_protocol']
|
||||
self.assertEqual(client_result, expected, msg % (client_result, "client"))
|
||||
server_result = stats['server_npn_protocols'][-1] \
|
||||
if len(stats['server_npn_protocols']) else 'nothing'
|
||||
self.assertEqual(server_result, expected, msg % (server_result, "server"))
|
||||
assert not ssl.HAS_NPN
|
||||
|
||||
def sni_contexts(self):
|
||||
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
|
@ -4369,8 +4303,7 @@ class ThreadedTests(unittest.TestCase):
|
|||
self.assertGreater(session.time, 0)
|
||||
self.assertGreater(session.timeout, 0)
|
||||
self.assertTrue(session.has_ticket)
|
||||
if ssl.OPENSSL_VERSION_INFO > (1, 0, 1):
|
||||
self.assertGreater(session.ticket_lifetime_hint, 0)
|
||||
self.assertGreater(session.ticket_lifetime_hint, 0)
|
||||
self.assertFalse(stats['session_reused'])
|
||||
sess_stat = server_context.session_stats()
|
||||
self.assertEqual(sess_stat['accept'], 1)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue