Skip to content

Commit c2fc7c4

Browse files
committed
Issue #26470: Port ssl and hashlib module to OpenSSL 1.1.0.
1 parent 3445827 commit c2fc7c4

File tree

6 files changed

+342
-135
lines changed

6 files changed

+342
-135
lines changed

Doc/library/ssl.rst

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,16 @@ purposes.
322322
Random generation
323323
^^^^^^^^^^^^^^^^^
324324

325+
.. deprecated::
326+
327+
2.7.13 OpenSSL has deprecated :func:`ssl.RAND_pseudo_bytes`, use
328+
:func:`ssl.RAND_bytes` instead.
329+
330+
.. deprecated::
331+
332+
2.7.13 OpenSSL has deprecated :func:`ssl.RAND_pseudo_bytes`, use
333+
:func:`ssl.RAND_bytes` instead.
334+
325335
.. function:: RAND_status()
326336

327337
Return ``True`` if the SSL pseudo-random number generator has been seeded
@@ -340,7 +350,7 @@ Random generation
340350
See http://egd.sourceforge.net/ or http://prngd.sourceforge.net/ for sources
341351
of entropy-gathering daemons.
342352

343-
Availability: not available with LibreSSL.
353+
Availability: not available with LibreSSL and OpenSSL > 1.1.0
344354

345355
.. function:: RAND_add(bytes, entropy)
346356

@@ -444,6 +454,9 @@ Certificate handling
444454
* :attr:`openssl_capath_env` - OpenSSL's environment key that points to a capath,
445455
* :attr:`openssl_capath` - hard coded path to a capath directory
446456

457+
Availability: LibreSSL ignores the environment vars
458+
:attr:`openssl_cafile_env` and :attr:`openssl_capath_env`
459+
447460
.. versionadded:: 2.7.9
448461

449462
.. function:: enum_certificates(store_name)
@@ -561,11 +574,19 @@ Constants
561574

562575
.. versionadded:: 2.7.10
563576

564-
.. data:: PROTOCOL_SSLv23
577+
.. data:: PROTOCOL_TLS
565578

566579
Selects the highest protocol version that both the client and server support.
567580
Despite the name, this option can select "TLS" protocols as well as "SSL".
568581

582+
.. versionadded:: 2.7.13
583+
584+
.. data:: PROTOCOL_SSLv23
585+
586+
Alias for ``PROTOCOL_TLS``.
587+
588+
.. deprecated:: 2.7.13 Use ``PROTOCOL_TLS`` instead.
589+
569590
.. data:: PROTOCOL_SSLv2
570591

571592
Selects SSL version 2 as the channel encryption protocol.
@@ -577,6 +598,8 @@ Constants
577598

578599
SSL version 2 is insecure. Its use is highly discouraged.
579600

601+
.. deprecated:: 2.7.13 OpenSSL has removed support for SSLv2.
602+
580603
.. data:: PROTOCOL_SSLv3
581604

582605
Selects SSL version 3 as the channel encryption protocol.
@@ -588,17 +611,32 @@ Constants
588611

589612
SSL version 3 is insecure. Its use is highly discouraged.
590613

614+
.. deprecated:: 2.7.13
615+
616+
OpenSSL has deprecated all version specific protocols. Use the default
617+
protocol with flags like ``OP_NO_SSLv3`` instead.
618+
591619
.. data:: PROTOCOL_TLSv1
592620

593621
Selects TLS version 1.0 as the channel encryption protocol.
594622

623+
.. deprecated:: 2.7.13
624+
625+
OpenSSL has deprecated all version specific protocols. Use the default
626+
protocol with flags like ``OP_NO_SSLv3`` instead.
627+
595628
.. data:: PROTOCOL_TLSv1_1
596629

597630
Selects TLS version 1.1 as the channel encryption protocol.
598631
Available only with openssl version 1.0.1+.
599632

600633
.. versionadded:: 2.7.9
601634

635+
.. deprecated:: 2.7.13
636+
637+
OpenSSL has deprecated all version specific protocols. Use the default
638+
protocol with flags like ``OP_NO_SSLv3`` instead.
639+
602640
.. data:: PROTOCOL_TLSv1_2
603641

604642
Selects TLS version 1.2 as the channel encryption protocol. This is the
@@ -607,6 +645,12 @@ Constants
607645

608646
.. versionadded:: 2.7.9
609647

648+
.. deprecated:: 2.7.13
649+
650+
OpenSSL has deprecated all version specific protocols. Use the default
651+
protocol with flags like ``OP_NO_SSLv3`` instead.
652+
653+
610654
.. data:: OP_ALL
611655

612656
Enables workarounds for various bugs present in other SSL implementations.
@@ -1112,6 +1156,9 @@ to speed up repeated connections from the same clients.
11121156
This method will raise :exc:`NotImplementedError` if :data:`HAS_ALPN` is
11131157
False.
11141158

1159+
OpenSSL 1.1.0+ will abort the handshake and raise :exc:`SSLError` when
1160+
both sides support ALPN but cannot agree on a protocol.
1161+
11151162
.. versionadded:: 2.7.10
11161163

11171164
.. method:: SSLContext.set_npn_protocols(protocols)

Lib/ssl.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
PROTOCOL_SSLv2
5252
PROTOCOL_SSLv3
5353
PROTOCOL_SSLv23
54+
PROTOCOL_TLS
5455
PROTOCOL_TLSv1
5556
PROTOCOL_TLSv1_1
5657
PROTOCOL_TLSv1_2
@@ -126,7 +127,10 @@ def _import_symbols(prefix):
126127

127128
from _ssl import _OPENSSL_API_VERSION
128129

129-
_PROTOCOL_NAMES = {value: name for name, value in globals().items() if name.startswith('PROTOCOL_')}
130+
_PROTOCOL_NAMES = {value: name for name, value in globals().items()
131+
if name.startswith('PROTOCOL_')
132+
and name != 'PROTOCOL_SSLv23'}
133+
PROTOCOL_SSLv23 = PROTOCOL_TLS
130134

131135
try:
132136
_SSLv2_IF_EXISTS = PROTOCOL_SSLv2
@@ -408,7 +412,7 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None,
408412
if not isinstance(purpose, _ASN1Object):
409413
raise TypeError(purpose)
410414

411-
context = SSLContext(PROTOCOL_SSLv23)
415+
context = SSLContext(PROTOCOL_TLS)
412416

413417
# SSLv2 considered harmful.
414418
context.options |= OP_NO_SSLv2
@@ -445,7 +449,7 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None,
445449
context.load_default_certs(purpose)
446450
return context
447451

448-
def _create_unverified_context(protocol=PROTOCOL_SSLv23, cert_reqs=None,
452+
def _create_unverified_context(protocol=PROTOCOL_TLS, cert_reqs=None,
449453
check_hostname=False, purpose=Purpose.SERVER_AUTH,
450454
certfile=None, keyfile=None,
451455
cafile=None, capath=None, cadata=None):
@@ -518,7 +522,7 @@ class SSLSocket(socket):
518522

519523
def __init__(self, sock=None, keyfile=None, certfile=None,
520524
server_side=False, cert_reqs=CERT_NONE,
521-
ssl_version=PROTOCOL_SSLv23, ca_certs=None,
525+
ssl_version=PROTOCOL_TLS, ca_certs=None,
522526
do_handshake_on_connect=True,
523527
family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None,
524528
suppress_ragged_eofs=True, npn_protocols=None, ciphers=None,
@@ -920,7 +924,7 @@ def version(self):
920924

921925
def wrap_socket(sock, keyfile=None, certfile=None,
922926
server_side=False, cert_reqs=CERT_NONE,
923-
ssl_version=PROTOCOL_SSLv23, ca_certs=None,
927+
ssl_version=PROTOCOL_TLS, ca_certs=None,
924928
do_handshake_on_connect=True,
925929
suppress_ragged_eofs=True,
926930
ciphers=None):
@@ -989,7 +993,7 @@ def PEM_cert_to_DER_cert(pem_cert_string):
989993
d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)]
990994
return base64.decodestring(d.encode('ASCII', 'strict'))
991995

992-
def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv23, ca_certs=None):
996+
def get_server_certificate(addr, ssl_version=PROTOCOL_TLS, ca_certs=None):
993997
"""Retrieve the certificate from the server at the specified address,
994998
and return it as a PEM-encoded string.
995999
If 'ca_certs' is specified, validate the server cert against it.

Lib/test/test_ssl.py

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626

2727
PROTOCOLS = sorted(ssl._PROTOCOL_NAMES)
2828
HOST = support.HOST
29+
IS_LIBRESSL = ssl.OPENSSL_VERSION.startswith('LibreSSL')
30+
IS_OPENSSL_1_1 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0)
31+
2932

3033
def data_file(*name):
3134
return os.path.join(os.path.dirname(__file__), *name)
@@ -164,7 +167,6 @@ def test_constants(self):
164167
self.assertIn(ssl.HAS_SNI, {True, False})
165168
self.assertIn(ssl.HAS_ECDH, {True, False})
166169

167-
168170
def test_random(self):
169171
v = ssl.RAND_status()
170172
if support.verbose:
@@ -281,9 +283,9 @@ def test_openssl_version(self):
281283
self.assertGreaterEqual(status, 0)
282284
self.assertLessEqual(status, 15)
283285
# Version string as returned by {Open,Libre}SSL, the format might change
284-
if "LibreSSL" in s:
285-
self.assertTrue(s.startswith("LibreSSL {:d}.{:d}".format(major, minor)),
286-
(s, t))
286+
if IS_LIBRESSL:
287+
self.assertTrue(s.startswith("LibreSSL {:d}".format(major)),
288+
(s, t, hex(n)))
287289
else:
288290
self.assertTrue(s.startswith("OpenSSL {:d}.{:d}.{:d}".format(major, minor, fix)),
289291
(s, t))
@@ -742,15 +744,15 @@ def test_ciphers(self):
742744
def test_options(self):
743745
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
744746
# OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value
745-
self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3,
746-
ctx.options)
747+
default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3)
748+
if not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0):
749+
default |= ssl.OP_NO_COMPRESSION
750+
self.assertEqual(default, ctx.options)
747751
ctx.options |= ssl.OP_NO_TLSv1
748-
self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1,
749-
ctx.options)
752+
self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options)
750753
if can_clear_options():
751-
ctx.options = (ctx.options & ~ssl.OP_NO_SSLv2) | ssl.OP_NO_TLSv1
752-
self.assertEqual(ssl.OP_ALL | ssl.OP_NO_TLSv1 | ssl.OP_NO_SSLv3,
753-
ctx.options)
754+
ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1)
755+
self.assertEqual(default, ctx.options)
754756
ctx.options = 0
755757
self.assertEqual(0, ctx.options)
756758
else:
@@ -1088,6 +1090,7 @@ def test_load_default_certs(self):
10881090
self.assertRaises(TypeError, ctx.load_default_certs, 'SERVER_AUTH')
10891091

10901092
@unittest.skipIf(sys.platform == "win32", "not-Windows specific")
1093+
@unittest.skipIf(IS_LIBRESSL, "LibreSSL doesn't support env vars")
10911094
def test_load_default_certs_env(self):
10921095
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
10931096
with support.EnvironmentVarGuard() as env:
@@ -1534,7 +1537,6 @@ def _test_get_server_certificate(host, port, cert=None):
15341537
sys.stdout.write("%s\n" % x)
15351538
else:
15361539
self.fail("Got server certificate %s for %s:%s!" % (pem, host, port))
1537-
15381540
pem = ssl.get_server_certificate((host, port),
15391541
ca_certs=cert)
15401542
if not pem:
@@ -2783,7 +2785,7 @@ def test_version_basic(self):
27832785
with closing(context.wrap_socket(socket.socket())) as s:
27842786
self.assertIs(s.version(), None)
27852787
s.connect((HOST, server.port))
2786-
self.assertEqual(s.version(), "TLSv1")
2788+
self.assertEqual(s.version(), 'TLSv1')
27872789
self.assertIs(s.version(), None)
27882790

27892791
@unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL")
@@ -2925,24 +2927,36 @@ def test_alpn_protocols(self):
29252927
(['http/3.0', 'http/4.0'], None)
29262928
]
29272929
for client_protocols, expected in protocol_tests:
2928-
server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
2930+
server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
29292931
server_context.load_cert_chain(CERTFILE)
29302932
server_context.set_alpn_protocols(server_protocols)
2931-
client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
2933+
client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
29322934
client_context.load_cert_chain(CERTFILE)
29332935
client_context.set_alpn_protocols(client_protocols)
2934-
stats = server_params_test(client_context, server_context,
2935-
chatty=True, connectionchatty=True)
29362936

2937-
msg = "failed trying %s (s) and %s (c).\n" \
2938-
"was expecting %s, but got %%s from the %%s" \
2939-
% (str(server_protocols), str(client_protocols),
2940-
str(expected))
2941-
client_result = stats['client_alpn_protocol']
2942-
self.assertEqual(client_result, expected, msg % (client_result, "client"))
2943-
server_result = stats['server_alpn_protocols'][-1] \
2944-
if len(stats['server_alpn_protocols']) else 'nothing'
2945-
self.assertEqual(server_result, expected, msg % (server_result, "server"))
2937+
try:
2938+
stats = server_params_test(client_context,
2939+
server_context,
2940+
chatty=True,
2941+
connectionchatty=True)
2942+
except ssl.SSLError as e:
2943+
stats = e
2944+
2945+
if expected is None and IS_OPENSSL_1_1:
2946+
# OpenSSL 1.1.0 raises handshake error
2947+
self.assertIsInstance(stats, ssl.SSLError)
2948+
else:
2949+
msg = "failed trying %s (s) and %s (c).\n" \
2950+
"was expecting %s, but got %%s from the %%s" \
2951+
% (str(server_protocols), str(client_protocols),
2952+
str(expected))
2953+
client_result = stats['client_alpn_protocol']
2954+
self.assertEqual(client_result, expected,
2955+
msg % (client_result, "client"))
2956+
server_result = stats['server_alpn_protocols'][-1] \
2957+
if len(stats['server_alpn_protocols']) else 'nothing'
2958+
self.assertEqual(server_result, expected,
2959+
msg % (server_result, "server"))
29462960

29472961
def test_selected_npn_protocol(self):
29482962
# selected_npn_protocol() is None unless NPN is used

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Core and Builtins
3636
Library
3737
-------
3838

39+
- Issue #26470: Port ssl and hashlib module to OpenSSL 1.1.0.
40+
3941
- Issue #27944: Fix some memory-corruption bugs in the log reading code of the
4042
_hotshot module.
4143

0 commit comments

Comments
 (0)