Skip to content

Commit

Permalink
bpo-43880: Show DeprecationWarnings for deprecated ssl module features (
Browse files Browse the repository at this point in the history
GH-25455)

* ssl.OP_NO_SSLv2
* ssl.OP_NO_SSLv3
* ssl.OP_NO_TLSv1
* ssl.OP_NO_TLSv1_1
* ssl.OP_NO_TLSv1_2
* ssl.OP_NO_TLSv1_3
* ssl.PROTOCOL_SSLv2
* ssl.PROTOCOL_SSLv3
* ssl.PROTOCOL_SSLv23 (alias for PROTOCOL_TLS)
* ssl.PROTOCOL_TLS
* ssl.PROTOCOL_TLSv1
* ssl.PROTOCOL_TLSv1_1
* ssl.PROTOCOL_TLSv1_2
* ssl.TLSVersion.SSLv3
* ssl.TLSVersion.TLSv1
* ssl.TLSVersion.TLSv1_1
* ssl.wrap_socket()
* ssl.RAND_pseudo_bytes()
* ssl.RAND_egd() (already removed since it's not supported by OpenSSL 1.1.1)
* ssl.SSLContext() without a protocol argument
* ssl.match_hostname()
* hashlib.pbkdf2_hmac() (pure Python implementation, fast OpenSSL
  function will stay)

Signed-off-by: Christian Heimes <christian@python.org>
  • Loading branch information
tiran authored Apr 19, 2021
1 parent 89d1550 commit 2875c60
Show file tree
Hide file tree
Showing 14 changed files with 305 additions and 201 deletions.
6 changes: 6 additions & 0 deletions Doc/library/hashlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,12 @@ include a `salt <https://en.wikipedia.org/wiki/Salt_%28cryptography%29>`_.
Python implementation uses an inline version of :mod:`hmac`. It is about
three times slower and doesn't release the GIL.

.. deprecated:: 3.10

Slow Python implementation of *pbkdf2_hmac* is deprecated. In the
future the function will only be available when Python is compiled
with OpenSSL.

.. function:: scrypt(password, *, salt, n, r, p, maxmem=0, dklen=64)

The function provides scrypt password-based key derivation function as
Expand Down
61 changes: 44 additions & 17 deletions Doc/library/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ probably additional platforms, as long as OpenSSL is installed on that platform.

Some behavior may be platform dependent, since calls are made to the
operating system socket APIs. The installed version of OpenSSL may also
cause variations in behavior. For example, TLSv1.1 and TLSv1.2 come with
openssl version 1.0.1.
cause variations in behavior. For example, TLSv1.3 with OpenSSL version
1.1.1.

.. warning::
Don't use this module without reading the :ref:`ssl-security`. Doing so
Expand Down Expand Up @@ -63,6 +63,8 @@ by SSL sockets created through the :meth:`SSLContext.wrap_socket` method.
:pep:`644` has been implemented. The ssl module requires OpenSSL 1.1.1
or newer.

Use of deprecated constants and functions result in deprecation warnings.


Functions, Constants, and Exceptions
------------------------------------
Expand Down Expand Up @@ -136,8 +138,9 @@ purposes.
:const:`None`, this function can choose to trust the system's default
CA certificates instead.

The settings are: :data:`PROTOCOL_TLS`, :data:`OP_NO_SSLv2`, and
:data:`OP_NO_SSLv3` with high encryption cipher suites without RC4 and
The settings are: :data:`PROTOCOL_TLS_CLIENT` or
:data:`PROTOCOL_TLS_SERVER`, :data:`OP_NO_SSLv2`, and :data:`OP_NO_SSLv3`
with high encryption cipher suites without RC4 and
without unauthenticated cipher suites. Passing :data:`~Purpose.SERVER_AUTH`
as *purpose* sets :data:`~SSLContext.verify_mode` to :data:`CERT_REQUIRED`
and either loads CA certificates (when at least one of *cafile*, *capath* or
Expand Down Expand Up @@ -185,6 +188,12 @@ purposes.

Support for key logging to :envvar:`SSLKEYLOGFILE` was added.

.. versionchanged:: 3.10

The context now uses :data:`PROTOCOL_TLS_CLIENT` or
:data:`PROTOCOL_TLS_SERVER` protocol instead of generic
:data:`PROTOCOL_TLS`.


Exceptions
^^^^^^^^^^
Expand Down Expand Up @@ -417,7 +426,7 @@ Certificate handling
previously. Return an integer (no fractions of a second in the
input format)

.. function:: get_server_certificate(addr, ssl_version=PROTOCOL_TLS, ca_certs=None)
.. function:: get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT, ca_certs=None)

Given the address ``addr`` of an SSL-protected server, as a (*hostname*,
*port-number*) pair, fetches the server's certificate, and returns it as a
Expand Down Expand Up @@ -654,6 +663,8 @@ Constants

.. versionadded:: 3.6

.. deprecated:: 3.10

.. data:: PROTOCOL_TLS_CLIENT

Auto-negotiate the highest protocol version like :data:`PROTOCOL_TLS`,
Expand Down Expand Up @@ -707,16 +718,18 @@ Constants
.. deprecated:: 3.6

OpenSSL has deprecated all version specific protocols. Use the default
protocol :data:`PROTOCOL_TLS` with flags like :data:`OP_NO_SSLv3` instead.
protocol :data:`PROTOCOL_TLS_SERVER` or :data:`PROTOCOL_TLS_CLIENT`
with :attr:`SSLContext.minimum_version` and
:attr:`SSLContext.maximum_version` instead.


.. data:: PROTOCOL_TLSv1

Selects TLS version 1.0 as the channel encryption protocol.

.. deprecated:: 3.6

OpenSSL has deprecated all version specific protocols. Use the default
protocol :data:`PROTOCOL_TLS` with flags like :data:`OP_NO_SSLv3` instead.
OpenSSL has deprecated all version specific protocols.

.. data:: PROTOCOL_TLSv1_1

Expand All @@ -727,8 +740,7 @@ Constants

.. deprecated:: 3.6

OpenSSL has deprecated all version specific protocols. Use the default
protocol :data:`PROTOCOL_TLS` with flags like :data:`OP_NO_SSLv3` instead.
OpenSSL has deprecated all version specific protocols.

.. data:: PROTOCOL_TLSv1_2

Expand All @@ -739,8 +751,7 @@ Constants

.. deprecated:: 3.6

OpenSSL has deprecated all version specific protocols. Use the default
protocol :data:`PROTOCOL_TLS` with flags like :data:`OP_NO_SSLv3` instead.
OpenSSL has deprecated all version specific protocols.

.. data:: OP_ALL

Expand All @@ -762,7 +773,6 @@ Constants

SSLv2 is deprecated


.. data:: OP_NO_SSLv3

Prevents an SSLv3 connection. This option is only applicable in
Expand Down Expand Up @@ -1068,6 +1078,11 @@ Constants

SSL 3.0 to TLS 1.3.

.. deprecated:: 3.10

All :class:`TLSVersion` members except :attr:`TLSVersion.TLSv1_2` and
:attr:`TLSVersion.TLSv1_3` are deprecated.


SSL Sockets
-----------
Expand Down Expand Up @@ -1423,7 +1438,7 @@ such as SSL configuration options, certificate(s) and private key(s).
It also manages a cache of SSL sessions for server-side sockets, in order
to speed up repeated connections from the same clients.

.. class:: SSLContext(protocol=PROTOCOL_TLS)
.. class:: SSLContext(protocol=None)

Create a new SSL context. You may pass *protocol* which must be one
of the ``PROTOCOL_*`` constants defined in this module. The parameter
Expand Down Expand Up @@ -1472,6 +1487,12 @@ to speed up repeated connections from the same clients.
ciphers, no ``NULL`` ciphers and no ``MD5`` ciphers (except for
:data:`PROTOCOL_SSLv2`).

.. deprecated:: 3.10

:class:`SSLContext` without protocol argument is deprecated. The
context class will either require :data:`PROTOCOL_TLS_CLIENT` or
:data:`PROTOCOL_TLS_SERVER` protocol in the future.


:class:`SSLContext` objects have the following methods and attributes:

Expand Down Expand Up @@ -1934,7 +1955,7 @@ to speed up repeated connections from the same clients.
.. attribute:: SSLContext.num_tickets

Control the number of TLS 1.3 session tickets of a
:attr:`TLS_PROTOCOL_SERVER` context. The setting has no impact on TLS
:attr:`PROTOCOL_TLS_SERVER` context. The setting has no impact on TLS
1.0 to 1.2 connections.

.. versionadded:: 3.8
Expand All @@ -1951,6 +1972,12 @@ to speed up repeated connections from the same clients.
>>> ssl.create_default_context().options # doctest: +SKIP
<Options.OP_ALL|OP_NO_SSLv3|OP_NO_SSLv2|OP_NO_COMPRESSION: 2197947391>

.. deprecated:: 3.7

All ``OP_NO_SSL*`` and ``OP_NO_TLS*`` options have been deprecated since
Python 3.7. Use :attr:`SSLContext.minimum_version` and
:attr:`SSLContext.maximum_version` instead.

.. attribute:: SSLContext.post_handshake_auth

Enable TLS 1.3 post-handshake client authentication. Post-handshake auth
Expand Down Expand Up @@ -2623,8 +2650,8 @@ disabled by default.
::

>>> client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> client_context.options |= ssl.OP_NO_TLSv1
>>> client_context.options |= ssl.OP_NO_TLSv1_1
>>> client_context.minimum_version = ssl.TLSVersion.TLSv1_3
>>> client_context.maximum_version = ssl.TLSVersion.TLSv1_3


The SSL context created above will only allow TLSv1.2 and later (if
Expand Down
6 changes: 6 additions & 0 deletions Lib/hashlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ def __hash_new(name, data=b'', **kwargs):
# OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA
from _hashlib import pbkdf2_hmac
except ImportError:
from warnings import warn as _warn
_trans_5C = bytes((x ^ 0x5C) for x in range(256))
_trans_36 = bytes((x ^ 0x36) for x in range(256))

Expand All @@ -191,6 +192,11 @@ def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None):
as OpenSSL's PKCS5_PBKDF2_HMAC for short passwords and much faster
for long passwords.
"""
_warn(
"Python implementation of pbkdf2_hmac() is deprecated.",
category=DeprecationWarning,
stacklevel=2
)
if not isinstance(hash_name, str):
raise TypeError(hash_name)

Expand Down
53 changes: 44 additions & 9 deletions Lib/ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,11 @@ def match_hostname(cert, hostname):
CertificateError is raised on failure. On success, the function
returns nothing.
"""
warnings.warn(
"ssl module: match_hostname() is deprecated",
category=DeprecationWarning,
stacklevel=2
)
if not cert:
raise ValueError("empty or no certificate, match_hostname needs a "
"SSL socket or SSL context with either "
Expand Down Expand Up @@ -479,7 +484,15 @@ class SSLContext(_SSLContext):
sslsocket_class = None # SSLSocket is assigned later.
sslobject_class = None # SSLObject is assigned later.

def __new__(cls, protocol=PROTOCOL_TLS, *args, **kwargs):
def __new__(cls, protocol=None, *args, **kwargs):
if protocol is None:
warnings.warn(
"ssl module: "
"SSLContext() without protocol argument is deprecated.",
category=DeprecationWarning,
stacklevel=2
)
protocol = PROTOCOL_TLS
self = _SSLContext.__new__(cls, protocol)
return self

Expand Down Expand Up @@ -518,6 +531,7 @@ def wrap_bio(self, incoming, outgoing, server_side=False,
)

def set_npn_protocols(self, npn_protocols):
warnings.warn("NPN is deprecated, use ALPN instead", stacklevel=2)
protos = bytearray()
for protocol in npn_protocols:
b = bytes(protocol, 'ascii')
Expand Down Expand Up @@ -734,12 +748,15 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None,
# SSLContext sets OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION,
# OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE and OP_SINGLE_ECDH_USE
# by default.
context = SSLContext(PROTOCOL_TLS)

if purpose == Purpose.SERVER_AUTH:
# verify certs and host name in client mode
context = SSLContext(PROTOCOL_TLS_CLIENT)
context.verify_mode = CERT_REQUIRED
context.check_hostname = True
elif purpose == Purpose.CLIENT_AUTH:
context = SSLContext(PROTOCOL_TLS_SERVER)
else:
raise ValueError(purpose)

if cafile or capath or cadata:
context.load_verify_locations(cafile, capath, cadata)
Expand All @@ -755,7 +772,7 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None,
context.keylog_filename = keylogfile
return context

def _create_unverified_context(protocol=PROTOCOL_TLS, *, cert_reqs=CERT_NONE,
def _create_unverified_context(protocol=None, *, cert_reqs=CERT_NONE,
check_hostname=False, purpose=Purpose.SERVER_AUTH,
certfile=None, keyfile=None,
cafile=None, capath=None, cadata=None):
Expand All @@ -772,10 +789,18 @@ def _create_unverified_context(protocol=PROTOCOL_TLS, *, cert_reqs=CERT_NONE,
# SSLContext sets OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION,
# OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE and OP_SINGLE_ECDH_USE
# by default.
context = SSLContext(protocol)
if purpose == Purpose.SERVER_AUTH:
# verify certs and host name in client mode
if protocol is None:
protocol = PROTOCOL_TLS_CLIENT
elif purpose == Purpose.CLIENT_AUTH:
if protocol is None:
protocol = PROTOCOL_TLS_SERVER
else:
raise ValueError(purpose)

if not check_hostname:
context.check_hostname = False
context = SSLContext(protocol)
context.check_hostname = check_hostname
if cert_reqs is not None:
context.verify_mode = cert_reqs
if check_hostname:
Expand Down Expand Up @@ -909,6 +934,9 @@ def selected_npn_protocol(self):
"""Return the currently selected NPN protocol as a string, or ``None``
if a next protocol was not negotiated or if NPN is not supported by one
of the peers."""
warnings.warn(
"ssl module: NPN is deprecated, use ALPN instead", stacklevel=2
)

def selected_alpn_protocol(self):
"""Return the currently selected ALPN protocol as a string, or ``None``
Expand Down Expand Up @@ -1123,6 +1151,9 @@ def getpeercert(self, binary_form=False):
@_sslcopydoc
def selected_npn_protocol(self):
self._checkClosed()
warnings.warn(
"ssl module: NPN is deprecated, use ALPN instead", stacklevel=2
)
return None

@_sslcopydoc
Expand Down Expand Up @@ -1382,7 +1413,11 @@ def wrap_socket(sock, keyfile=None, certfile=None,
do_handshake_on_connect=True,
suppress_ragged_eofs=True,
ciphers=None):

warnings.warn(
"ssl module: wrap_socket is deprecated, use SSLContext.wrap_socket()",
category=DeprecationWarning,
stacklevel=2
)
if server_side and not certfile:
raise ValueError("certfile must be specified for server-side "
"operations")
Expand Down Expand Up @@ -1460,7 +1495,7 @@ def PEM_cert_to_DER_cert(pem_cert_string):
d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)]
return base64.decodebytes(d.encode('ASCII', 'strict'))

def get_server_certificate(addr, ssl_version=PROTOCOL_TLS, ca_certs=None):
def get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT, ca_certs=None):
"""Retrieve the certificate from the server at the specified address,
and return it as a PEM-encoded string.
If 'ca_certs' is specified, validate the server cert against it.
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/pythoninfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ def format_attr(attr, value):
copy_attributes(info_add, ssl, 'ssl.%s', attributes, formatter=format_attr)

for name, ctx in (
('SSLContext', ssl.SSLContext()),
('SSLContext', ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)),
('default_https_context', ssl._create_default_https_context()),
('stdlib_context', ssl._create_stdlib_context()),
):
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_asyncio/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def dummy_ssl_context():
if ssl is None:
return None
else:
return ssl.SSLContext(ssl.PROTOCOL_TLS)
return simple_client_sslcontext(disable_verify=True)


def run_briefly(loop):
Expand Down Expand Up @@ -158,7 +158,7 @@ def finish_request(self, request, client_address):
# contains the ssl key and certificate files) differs
# between the stdlib and stand-alone asyncio.
# Prefer our own if we can find it.
context = ssl.SSLContext()
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(ONLYCERT, ONLYKEY)

ssock = context.wrap_socket(request, server_side=True)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_ftplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ class SSLConnection(asyncore.dispatcher):
_ssl_closing = False

def secure_connection(self):
context = ssl.SSLContext()
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(CERTFILE)
socket = context.wrap_socket(self.socket,
suppress_ragged_eofs=False,
Expand Down
6 changes: 5 additions & 1 deletion Lib/test/test_hashlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from test.support import _4G, bigmemtest
from test.support.import_helper import import_fresh_module
from test.support import threading_helper
from test.support import warnings_helper
from http.client import HTTPException

# Were we compiled --with-pydebug or with #define Py_DEBUG?
Expand Down Expand Up @@ -1021,7 +1022,10 @@ def _test_pbkdf2_hmac(self, pbkdf2, supported):

@unittest.skipIf(builtin_hashlib is None, "test requires builtin_hashlib")
def test_pbkdf2_hmac_py(self):
self._test_pbkdf2_hmac(builtin_hashlib.pbkdf2_hmac, builtin_hashes)
with warnings_helper.check_warnings():
self._test_pbkdf2_hmac(
builtin_hashlib.pbkdf2_hmac, builtin_hashes
)

@unittest.skipUnless(hasattr(openssl_hashlib, 'pbkdf2_hmac'),
' test requires OpenSSL > 1.0')
Expand Down
Loading

0 comments on commit 2875c60

Please sign in to comment.