Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SSL_CTX_set_min_proto_version/SSL_CTX_set_max_proto_version bindings #985

Merged
merged 8 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ jobs:
PYTHON:
# Base builds
- {VERSION: "2.7", TOXENV: "py27"}
- {VERSION: "3.5", TOXENV: "py35"}
- {VERSION: "3.6", TOXENV: "py36"}
- {VERSION: "3.7", TOXENV: "py37"}
- {VERSION: "3.8", TOXENV: "py38"}
Expand All @@ -26,7 +25,6 @@ jobs:
- {VERSION: "pypy3", TOXENV: "pypy3-cryptographyMaster"}
# -cryptographyMinimum
- {VERSION: "2.7", TOXENV: "py27-cryptographyMinimum"}
- {VERSION: "3.5", TOXENV: "py35-cryptographyMinimum"}
- {VERSION: "3.6", TOXENV: "py36-cryptographyMinimum"}
- {VERSION: "3.7", TOXENV: "py37-cryptographyMinimum"}
- {VERSION: "3.8", TOXENV: "py38-cryptographyMinimum"}
Expand Down Expand Up @@ -66,13 +64,14 @@ jobs:
matrix:
TEST:
- {CONTAINER: "stretch", TOXENV: "py27"}
- {CONTAINER: "stretch", TOXENV: "py35"}
- {CONTAINER: "ubuntu-bionic", TOXENV: "py36"}
mhils marked this conversation as resolved.
Show resolved Hide resolved
name: "${{ matrix.TEST.TOXENV }} on ${{ matrix.TEST.CONTAINER }}"
steps:
- uses: actions/checkout@v2
- run: tox -v
env:
TOXENV: ${{ matrix.TEST.TOXENV }}
RUSTUP_HOME: /root/.rustup
- name: Upload coverage
run: |
curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ The third digit is only for regressions.
Backward-incompatible changes:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

- The minimum ``cryptography`` version is now 3.3.
- Drop support for Python 3.5

Deprecations:
^^^^^^^^^^^^^

Expand All @@ -18,6 +21,8 @@ Changes:

- Raise an error when an invalid ALPN value is set.
`#993 <https://github.com/pyca/pyopenssl/pull/993>`_
- Added ``OpenSSL.SSL.Context.set_min_proto_version`` and ``OpenSSL.SSL.Context.set_max_proto_version``
to set the minimum and maximum supported TLS version `#985 <https://github.com/pyca/pyopenssl/pull/985>`_.

20.0.1 (2020-12-15)
-------------------
Expand Down
19 changes: 16 additions & 3 deletions doc/api/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,32 @@
This module handles things specific to SSL. There are two objects defined:
Context, Connection.

.. py:data:: SSLv2_METHOD
.. py:data:: TLS_METHOD
TLS_SERVER_METHOD
TLS_CLIENT_METHOD
SSLv2_METHOD
SSLv3_METHOD
SSLv23_METHOD
TLSv1_METHOD
TLSv1_1_METHOD
TLSv1_2_METHOD

These constants represent the different SSL methods to use when creating a
context object. If the underlying OpenSSL build is missing support for any
of these protocols, constructing a :py:class:`Context` using the
context object. New code should only use ``TLS_METHOD``, ``TLS_SERVER_METHOD``,
or ``TLS_CLIENT_METHOD``. If the underlying OpenSSL build is missing support
for any of these protocols, constructing a :py:class:`Context` using the
corresponding :py:const:`*_METHOD` will raise an exception.


.. py:data:: SSL3_VERSION
TLS1_VERSION
TLS1_1_VERSION
TLS1_2_VERSION
TLS1_3_VERSION

These constants represent the different TLS versions to use when
setting the minimum or maximum TLS version.

.. py:data:: VERIFY_NONE
VERIFY_PEER
VERIFY_FAIL_IF_NO_PEER_CERT
Expand Down
2 changes: 1 addition & 1 deletion doc/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Other OpenSSL wrappers for Python at the time were also limited, though in diffe
Later it was maintained by `Jean-Paul Calderone`_ who among other things managed to make pyOpenSSL a pure Python project which the current maintainers are *very* grateful for.

Over the time the standard library's ``ssl`` module improved, never reaching the completeness of pyOpenSSL's API coverage.
Despite `PEP 466`_ many useful features remain Python 3-only and pyOpenSSL remains the only alternative for full-featured TLS code across all noteworthy Python versions from 2.7 through 3.5 and PyPy_.
Despite `PEP 466`_ many useful features remain Python 3-only and pyOpenSSL remains the only alternative for full-featured TLS code across all noteworthy Python versions from 2.7 through 3.6 and PyPy_.


Development
Expand Down
7 changes: 4 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ def find_meta(meta):
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
Expand All @@ -90,12 +89,14 @@ def find_meta(meta):
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: System :: Networking",
],
python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*",
python_requires=(
">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*"
),
packages=find_packages(where="src"),
package_dir={"": "src"},
install_requires=[
# Fix cryptographyMinimum in tox.ini when changing this!
"cryptography>=3.2",
"cryptography>=3.3",
"six>=1.5.2",
],
extras_require={
Expand Down
60 changes: 58 additions & 2 deletions src/OpenSSL/SSL.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@
"TLSv1_METHOD",
"TLSv1_1_METHOD",
"TLSv1_2_METHOD",
"TLS_METHOD",
"TLS_SERVER_METHOD",
"TLS_CLIENT_METHOD",
"SSL3_VERSION",
"TLS1_VERSION",
"TLS1_1_VERSION",
"TLS1_2_VERSION",
"TLS1_3_VERSION",
"OP_NO_SSLv2",
"OP_NO_SSLv3",
"OP_NO_TLSv1",
Expand Down Expand Up @@ -139,6 +147,24 @@ class _buffer(object):
TLSv1_METHOD = 4
TLSv1_1_METHOD = 5
TLSv1_2_METHOD = 6
TLS_METHOD = 7
TLS_SERVER_METHOD = 8
TLS_CLIENT_METHOD = 9

try:
SSL3_VERSION = _lib.SSL3_VERSION
TLS1_VERSION = _lib.TLS1_VERSION
TLS1_1_VERSION = _lib.TLS1_1_VERSION
TLS1_2_VERSION = _lib.TLS1_2_VERSION
TLS1_3_VERSION = _lib.TLS1_3_VERSION
except AttributeError:
# Hardcode constants for cryptography < 3.4, see
# https://github.com/pyca/pyopenssl/pull/985#issuecomment-775186682
SSL3_VERSION = 768
TLS1_VERSION = 769
TLS1_1_VERSION = 770
TLS1_2_VERSION = 771
TLS1_3_VERSION = 772

OP_NO_SSLv2 = _lib.SSL_OP_NO_SSLv2
OP_NO_SSLv3 = _lib.SSL_OP_NO_SSLv3
Expand Down Expand Up @@ -603,8 +629,9 @@ class Context(object):
:class:`OpenSSL.SSL.Context` instances define the parameters for setting
up new SSL connections.

:param method: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or
TLSv1_METHOD.
:param method: One of TLS_METHOD, TLS_CLIENT_METHOD, or TLS_SERVER_METHOD.
SSLv23_METHOD, TLSv1_METHOD, etc. are deprecated and should
not be used.
"""

_methods = {
Expand All @@ -614,6 +641,9 @@ class Context(object):
TLSv1_METHOD: "TLSv1_method",
TLSv1_1_METHOD: "TLSv1_1_method",
TLSv1_2_METHOD: "TLSv1_2_method",
TLS_METHOD: "TLS_method",
TLS_SERVER_METHOD: "TLS_server_method",
TLS_CLIENT_METHOD: "TLS_client_method",
}
_methods = dict(
(identifier, getattr(_lib, name))
Expand Down Expand Up @@ -661,6 +691,32 @@ def __init__(self, method):

self.set_mode(_lib.SSL_MODE_ENABLE_PARTIAL_WRITE)

def set_min_proto_version(self, version):
"""
Set the minimum supported protocol version. Setting the minimum
version to 0 will enable protocol versions down to the lowest version
supported by the library.

If the underlying OpenSSL build is missing support for the selected
version, this method will raise an exception.
"""
_openssl_assert(
_lib.SSL_CTX_set_min_proto_version(self._context, version) == 1
)

def set_max_proto_version(self, version):
"""
Set the maximum supported protocol version. Setting the maximum
version to 0 will enable protocol versions up to the highest version
supported by the library.

If the underlying OpenSSL build is missing support for the selected
version, this method will raise an exception.
"""
_openssl_assert(
_lib.SSL_CTX_set_max_proto_version(self._context, version) == 1
)

def load_verify_locations(self, cafile, capath=None):
"""
Let SSL know where we can find trusted certificates for the certificate
Expand Down
28 changes: 27 additions & 1 deletion tests/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,14 @@
from OpenSSL.crypto import dump_certificate, load_certificate
from OpenSSL.crypto import get_elliptic_curves

from OpenSSL.SSL import OPENSSL_VERSION_NUMBER, SSLEAY_VERSION, SSLEAY_CFLAGS
from OpenSSL.SSL import (
OPENSSL_VERSION_NUMBER,
SSLEAY_VERSION,
SSLEAY_CFLAGS,
TLS_METHOD,
TLS1_2_VERSION,
TLS1_1_VERSION,
)
from OpenSSL.SSL import SSLEAY_PLATFORM, SSLEAY_DIR, SSLEAY_BUILT_ON
from OpenSSL.SSL import SENT_SHUTDOWN, RECEIVED_SHUTDOWN
from OpenSSL.SSL import (
Expand Down Expand Up @@ -1039,6 +1046,25 @@ def keylog(conn, line):
assert all(isinstance(conn, Connection) for conn, line in called)
assert all(b"CLIENT_RANDOM" in line for conn, line in called)

def test_set_proto_version(self):
server_context = Context(TLS_METHOD)
server_context.use_certificate(
load_certificate(FILETYPE_PEM, root_cert_pem)
)
server_context.use_privatekey(
load_privatekey(FILETYPE_PEM, root_key_pem)
)
server_context.set_min_proto_version(TLS1_2_VERSION)

client_context = Context(TLS_METHOD)
client_context.set_max_proto_version(TLS1_1_VERSION)

with pytest.raises(Error, match="unsupported protocol"):
self._handshake_test(server_context, client_context)

client_context.set_max_proto_version(0)
self._handshake_test(server_context, client_context)

def _load_verify_locations_test(self, *args):
"""
Create a client context which will verify the peer certificate and call
Expand Down
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = {pypy,pypy3,py27,py35,py36,py37,py38,py39}{,-cryptographyMaster,-cryptographyMinimum}{,-randomorder},py37-twistedMaster,pypi-readme,check-manifest,flake8,docs,coverage-report
envlist = {pypy,pypy3,py27,py36,py37,py38,py39}{,-cryptographyMaster,-cryptographyMinimum}{,-randomorder},py37-twistedMaster,pypi-readme,check-manifest,flake8,docs,coverage-report

[testenv]
whitelist_externals =
Expand All @@ -10,7 +10,7 @@ extras =
deps =
coverage>=4.2
cryptographyMaster: git+https://github.com/pyca/cryptography.git
cryptographyMinimum: cryptography==3.2
cryptographyMinimum: cryptography==3.3
randomorder: pytest-randomly
setenv =
# Do not allow the executing environment to pollute the test environment
Expand Down