From c2624de7ee23493c9f2a98271e16cf0f0a6cf2bc Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Thu, 27 Aug 2020 00:54:03 -0500 Subject: [PATCH 01/99] bump version for 3.2 dev (#5431) --- CHANGELOG.rst | 7 +++++++ src/cryptography/__about__.py | 2 +- vectors/cryptography_vectors/__about__.py | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3facb2ac1642..f1e2fd59ed4a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,13 @@ Changelog ========= +.. _v3-2: + +3.2 - `master`_ +~~~~~~~~~~~~~~~ + +.. note:: This version is not yet released and is under active development. + .. _v3-1: 3.1 - 2020-08-26 diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py index fd1b7a581734..57905d3ef4e9 100644 --- a/src/cryptography/__about__.py +++ b/src/cryptography/__about__.py @@ -22,7 +22,7 @@ ) __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.1" +__version__ = "3.2.dev1" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" diff --git a/vectors/cryptography_vectors/__about__.py b/vectors/cryptography_vectors/__about__.py index 0ae2179b0c46..e1a01178cdc4 100644 --- a/vectors/cryptography_vectors/__about__.py +++ b/vectors/cryptography_vectors/__about__.py @@ -20,7 +20,7 @@ __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.1" +__version__ = "3.2.dev1" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" From 761e9aa9901fbce8ea5d020ee9ebb6375e01b842 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 27 Aug 2020 17:32:38 -0400 Subject: [PATCH 02/99] re-enable paramiko downstream testing (#5436) --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 103c2f835409..44a7ea0e3734 100644 --- a/.travis.yml +++ b/.travis.yml @@ -118,10 +118,8 @@ matrix: env: DOWNSTREAM=pyopenssl - python: 3.7 env: DOWNSTREAM=twisted OPENSSL=1.1.1g - # Temporary disabled until - # https://github.com/paramiko/paramiko/pull/1723 is merged - # - python: 2.7 - # env: DOWNSTREAM=paramiko + - python: 2.7 + env: DOWNSTREAM=paramiko - python: 3.7 env: DOWNSTREAM=aws-encryption-sdk - python: 3.7 From b31ecb0ff93427c89ad002a7a7eee63b3ad8cf39 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 27 Aug 2020 19:11:37 -0400 Subject: [PATCH 03/99] try running paramiko downstream tests on py3 (#5437) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 44a7ea0e3734..2985521617ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -118,7 +118,7 @@ matrix: env: DOWNSTREAM=pyopenssl - python: 3.7 env: DOWNSTREAM=twisted OPENSSL=1.1.1g - - python: 2.7 + - python: 3.7 env: DOWNSTREAM=paramiko - python: 3.7 env: DOWNSTREAM=aws-encryption-sdk From 1fd7cacdb8675242bb8438cf427b9417dcea6968 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 28 Aug 2020 09:29:04 -0400 Subject: [PATCH 04/99] Removed urllib3 downstream test (#5442) --- .travis.yml | 4 ---- .travis/downstream.d/urllib3.sh | 18 ------------------ 2 files changed, 22 deletions(-) delete mode 100755 .travis/downstream.d/urllib3.sh diff --git a/.travis.yml b/.travis.yml index 2985521617ed..fca538be18b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -130,10 +130,6 @@ matrix: env: DOWNSTREAM=certbot - python: 3.8 env: DOWNSTREAM=certbot-josepy - - python: 3.8 - env: DOWNSTREAM=urllib3 - # Tests hang when run under bionic/focal - dist: xenial install: - ./.travis/install.sh diff --git a/.travis/downstream.d/urllib3.sh b/.travis/downstream.d/urllib3.sh deleted file mode 100755 index dad06159846f..000000000000 --- a/.travis/downstream.d/urllib3.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -ex - -case "${1}" in - install) - git clone --depth 1 https://github.com/shazow/urllib3 - cd urllib3 - git rev-parse HEAD - pip install -r ./dev-requirements.txt - pip install -e ".[socks]" - ;; - run) - cd urllib3 - pytest test - ;; - *) - exit 1 - ;; -esac From 8bc6920444afcdaeb4307c365b718b3ddc9e6c0c Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 28 Aug 2020 10:55:41 -0400 Subject: [PATCH 05/99] Break users on OpenSSL 1.0.2 (#5438) fixes #5432 --- CHANGELOG.rst | 3 +++ docs/faq.rst | 13 ++++++++++++ docs/installation.rst | 3 ++- .../hazmat/bindings/openssl/binding.py | 20 +++++++++++++------ tests/hazmat/bindings/test_openssl.py | 12 +++++++++++ tox.ini | 2 ++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f1e2fd59ed4a..60d8b6182174 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,9 @@ Changelog .. note:: This version is not yet released and is under active development. +* Support for OpenSSL 1.0.2 has been removed. Users on older version of OpenSSL + will need to upgrade. + .. _v3-1: 3.1 - 2020-08-26 diff --git a/docs/faq.rst b/docs/faq.rst index dba7b05ed9ac..33c5417d12db 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -109,6 +109,19 @@ Your ``pip`` and/or ``setuptools`` are outdated. Please upgrade to the latest versions with ``pip install -U pip setuptools`` (or on Windows ``python -m pip install -U pip setuptools``). +Importing cryptography causes a ``RuntimeError`` about OpenSSL 1.0.2 +-------------------------------------------------------------------- + +The OpenSSL project has dropped support for the 1.0.2 release series. Since it +is no longer receiving security patches from upstream, ``cryptography`` is also +dropping support for it. To fix this issue you should upgrade to a newer +version of OpenSSL (1.1.0 or later). This may require you to upgrade to a newer +operating system. + +For the 3.2 release, you can set the ``CRYPTOGRAPHY_ALLOW_OPENSSL_102`` +environment variable. Please note that this is *temporary* and will be removed +in ``cryptography`` 3.3. + Installing cryptography with OpenSSL 0.9.8, 1.0.0, 1.0.1 fails -------------------------------------------------------------- diff --git a/docs/installation.rst b/docs/installation.rst index 62126fc76816..c773fdce5d69 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -33,7 +33,8 @@ OpenSSL releases: .. warning:: - Cryptography 3.1 has deprecated support for OpenSSL 1.0.2. + Cryptography 3.2 has dropped support for OpenSSL 1.0.2, see the + :doc:`FAQ ` for more details Building cryptography on Windows -------------------------------- diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index 178a81e0d56c..bf6f12029b5b 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function import collections +import os import threading import types import warnings @@ -170,12 +171,19 @@ def _verify_openssl_version(lib): lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 and not lib.CRYPTOGRAPHY_IS_LIBRESSL ): - warnings.warn( - "OpenSSL version 1.0.2 is no longer supported by the OpenSSL " - "project, please upgrade. The next version of cryptography will " - "drop support for it.", - utils.CryptographyDeprecationWarning, - ) + if os.environ.get("CRYPTOGRAPHY_ALLOW_OPENSSL_102"): + warnings.warn( + "OpenSSL version 1.0.2 is no longer supported by the OpenSSL " + "project, please upgrade. The next version of cryptography " + "will completely remove support for it.", + utils.CryptographyDeprecationWarning, + ) + else: + raise RuntimeError( + "You are linking against OpenSSL 1.0.2, which is no longer " + "supported by the OpenSSL project. You need to upgrade to a " + "newer version of OpenSSL." + ) def _verify_package_version(version): diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py index a4f6ac015695..ecee34091dc7 100644 --- a/tests/hazmat/bindings/test_openssl.py +++ b/tests/hazmat/bindings/test_openssl.py @@ -4,6 +4,8 @@ from __future__ import absolute_import, division, print_function +import pretend + import pytest from cryptography.exceptions import InternalError @@ -11,6 +13,7 @@ Binding, _consume_errors, _openssl_assert, + _verify_openssl_version, _verify_package_version, ) @@ -125,3 +128,12 @@ def test_check_startup_errors_are_allowed(self): def test_version_mismatch(self): with pytest.raises(ImportError): _verify_package_version("nottherightversion") + + def test_verify_openssl_version(self, monkeypatch): + monkeypatch.delenv("CRYPTOGRAPHY_ALLOW_OPENSSL_102", raising=False) + lib = pretend.stub( + CRYPTOGRAPHY_OPENSSL_LESS_THAN_110=True, + CRYPTOGRAPHY_IS_LIBRESSL=False, + ) + with pytest.raises(RuntimeError): + _verify_openssl_version(lib) diff --git a/tox.ini b/tox.ini index bc5de1ad9953..e94d3c1e0753 100644 --- a/tox.ini +++ b/tox.ini @@ -13,6 +13,8 @@ deps = ./vectors randomorder: pytest-randomly passenv = ARCHFLAGS LDFLAGS CFLAGS INCLUDE LIB LD_LIBRARY_PATH USERNAME PYTHONIOENCODING OPENSSL_FORCE_FIPS_MODE +setenv = + CRYPTOGRAPHY_ALLOW_OPENSSL_102=1 commands = pip list # We use parallel mode and then combine here so that coverage.py will take From 31a5da73f88f04558aac538682b0901326c37152 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 29 Aug 2020 08:28:32 -0500 Subject: [PATCH 06/99] update docs to not use backends (#5404) --- docs/fernet.rst | 2 - docs/hazmat/primitives/asymmetric/dh.rst | 16 ++------ docs/hazmat/primitives/asymmetric/dsa.rst | 6 +-- docs/hazmat/primitives/asymmetric/ec.rst | 30 +++++--------- docs/hazmat/primitives/asymmetric/rsa.rst | 8 +--- .../primitives/asymmetric/serialization.rst | 16 +++----- docs/hazmat/primitives/asymmetric/utils.rst | 2 - docs/hazmat/primitives/asymmetric/x25519.rst | 3 -- docs/hazmat/primitives/asymmetric/x448.rst | 3 -- .../primitives/cryptographic-hashes.rst | 3 +- .../primitives/key-derivation-functions.rst | 32 --------------- docs/hazmat/primitives/mac/cmac.rst | 5 +-- docs/hazmat/primitives/mac/hmac.rst | 5 +-- .../primitives/symmetric-encryption.rst | 17 ++------ docs/hazmat/primitives/twofactor.rst | 8 ++-- docs/x509/ocsp.rst | 15 +++---- docs/x509/reference.rst | 40 ++++++------------- docs/x509/tutorial.rst | 7 +--- 18 files changed, 56 insertions(+), 162 deletions(-) diff --git a/docs/fernet.rst b/docs/fernet.rst index dd9d75bd1dc6..960f47137850 100644 --- a/docs/fernet.rst +++ b/docs/fernet.rst @@ -229,7 +229,6 @@ password through a key derivation function such as >>> import base64 >>> import os >>> from cryptography.fernet import Fernet - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC >>> password = b"password" @@ -239,7 +238,6 @@ password through a key derivation function such as ... length=32, ... salt=salt, ... iterations=100000, - ... backend=default_backend() ... ) >>> key = base64.urlsafe_b64encode(kdf.derive(password)) >>> f = Fernet(key) diff --git a/docs/hazmat/primitives/asymmetric/dh.rst b/docs/hazmat/primitives/asymmetric/dh.rst index 145196adc581..6b47da089378 100644 --- a/docs/hazmat/primitives/asymmetric/dh.rst +++ b/docs/hazmat/primitives/asymmetric/dh.rst @@ -31,13 +31,11 @@ present. .. code-block:: pycon - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import dh >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF >>> # Generate some parameters. These can be reused. - >>> parameters = dh.generate_parameters(generator=2, key_size=2048, - ... backend=default_backend()) + >>> parameters = dh.generate_parameters(generator=2, key_size=2048) >>> # Generate a private key for use in the exchange. >>> server_private_key = parameters.generate_private_key() >>> # In a real handshake the peer is a remote client. For this @@ -51,7 +49,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # And now we can demonstrate that the handshake performed in the >>> # opposite direction gives the same final value @@ -63,7 +60,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(same_shared_key) >>> derived_key == same_derived_key @@ -75,13 +71,11 @@ example of the ephemeral form: .. code-block:: pycon - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import dh >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF >>> # Generate some parameters. These can be reused. - >>> parameters = dh.generate_parameters(generator=2, key_size=2048, - ... backend=default_backend()) + >>> parameters = dh.generate_parameters(generator=2, key_size=2048) >>> # Generate a private key for use in the exchange. >>> private_key = parameters.generate_private_key() >>> # In a real handshake the peer_public_key will be received from the @@ -96,7 +90,6 @@ example of the ephemeral form: ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # For the next handshake we MUST generate another private key, but >>> # we can reuse the parameters. @@ -108,7 +101,6 @@ example of the ephemeral form: ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key_2) To assemble a :class:`~DHParameters` and a :class:`~DHPublicKey` from @@ -118,9 +110,9 @@ example, if **p**, **g**, and **y** are :class:`int` objects received from a peer:: pn = dh.DHParameterNumbers(p, g) - parameters = pn.parameters(default_backend()) + parameters = pn.parameters() peer_public_numbers = dh.DHPublicNumbers(y, pn) - peer_public_key = peer_public_numbers.public_key(default_backend()) + peer_public_key = peer_public_numbers.public_key() See also the :class:`~cryptography.hazmat.backends.interfaces.DHBackend` diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst index 2eae56df1c36..788e4270b886 100644 --- a/docs/hazmat/primitives/asymmetric/dsa.rst +++ b/docs/hazmat/primitives/asymmetric/dsa.rst @@ -78,12 +78,10 @@ instance. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import dsa >>> private_key = dsa.generate_private_key( ... key_size=1024, - ... backend=default_backend() ... ) >>> data = b"this is some data I'd like to sign" >>> signature = private_key.sign( @@ -103,7 +101,7 @@ separately and pass that value using >>> from cryptography.hazmat.primitives.asymmetric import utils >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() @@ -146,7 +144,7 @@ separately and pass that value using .. doctest:: >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst index 72768f8332cd..5691560f3c76 100644 --- a/docs/hazmat/primitives/asymmetric/ec.rst +++ b/docs/hazmat/primitives/asymmetric/ec.rst @@ -56,11 +56,10 @@ Elliptic Curve Signature Algorithms .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import ec >>> private_key = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ) >>> data = b"this is some data I'd like to sign" >>> signature = private_key.sign( @@ -80,7 +79,7 @@ Elliptic Curve Signature Algorithms >>> from cryptography.hazmat.primitives.asymmetric import utils >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() @@ -112,7 +111,7 @@ Elliptic Curve Signature Algorithms .. doctest:: >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() @@ -270,18 +269,17 @@ Elliptic Curve Key Exchange algorithm .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import ec >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF >>> # Generate a private key for use in the exchange. >>> server_private_key = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ) >>> # In a real handshake the peer is a remote client. For this >>> # example we'll generate another local private key though. >>> peer_private_key = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ) >>> shared_key = server_private_key.exchange( ... ec.ECDH(), peer_private_key.public_key()) @@ -291,7 +289,6 @@ Elliptic Curve Key Exchange algorithm ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # And now we can demonstrate that the handshake performed in the >>> # opposite direction gives the same final value @@ -303,7 +300,6 @@ Elliptic Curve Key Exchange algorithm ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(same_shared_key) >>> derived_key == same_derived_key True @@ -316,19 +312,18 @@ Elliptic Curve Key Exchange algorithm .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import ec >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF >>> # Generate a private key for use in the exchange. >>> private_key = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ) >>> # In a real handshake the peer_public_key will be received from the >>> # other party. For this example we'll generate another private key >>> # and get a public key from that. >>> peer_public_key = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ).public_key() >>> shared_key = private_key.exchange(ec.ECDH(), peer_public_key) >>> # Perform key derivation. @@ -337,14 +332,13 @@ Elliptic Curve Key Exchange algorithm ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # For the next handshake we MUST generate another private key. >>> private_key_2 = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ) >>> peer_public_key_2 = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ).public_key() >>> shared_key_2 = private_key_2.exchange(ec.ECDH(), peer_public_key_2) >>> derived_key_2 = HKDF( @@ -352,7 +346,6 @@ Elliptic Curve Key Exchange algorithm ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key_2) Elliptic Curves @@ -787,11 +780,10 @@ This sample demonstrates how to generate a private key and serialize it. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import serialization >>> from cryptography.hazmat.primitives.asymmetric import ec - >>> private_key = ec.generate_private_key(ec.SECP384R1(), default_backend()) + >>> private_key = ec.generate_private_key(ec.SECP384R1()) >>> serialized_private = private_key.private_bytes( ... encoding=serialization.Encoding.PEM, @@ -831,14 +823,12 @@ in PEM format. >>> loaded_public_key = serialization.load_pem_public_key( ... serialized_public, - ... backend=default_backend() ... ) >>> loaded_private_key = serialization.load_pem_private_key( ... serialized_private, ... # or password=None, if in plain text ... password=b'testpassword', - ... backend=default_backend() ... ) diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index c3311c80c96c..b8060e4740fd 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -32,12 +32,10 @@ mathematical properties`_. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> private_key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) :param int public_exponent: The public exponent of the new key. @@ -68,14 +66,12 @@ markers), you can load it: .. code-block:: pycon - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import serialization >>> with open("path/to/key.pem", "rb") as key_file: ... private_key = serialization.load_pem_private_key( ... key_file.read(), ... password=None, - ... backend=default_backend() ... ) Serialized keys may optionally be encrypted on disk using a password. In this @@ -171,7 +167,7 @@ separately and pass that value using >>> from cryptography.hazmat.primitives.asymmetric import utils >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() @@ -221,7 +217,7 @@ separately and pass that value using .. doctest:: >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index 914fe51df7ca..56b2c696ccaa 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -88,10 +88,9 @@ methods. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.asymmetric import dsa, rsa >>> from cryptography.hazmat.primitives.serialization import load_pem_private_key - >>> key = load_pem_private_key(pem_data, password=None, backend=default_backend()) + >>> key = load_pem_private_key(pem_data, password=None) >>> if isinstance(key, rsa.RSAPrivateKey): ... signature = sign_with_rsa_key(key, message) ... elif isinstance(key, dsa.DSAPrivateKey): @@ -173,7 +172,7 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END .. doctest:: >>> from cryptography.hazmat.primitives.serialization import load_pem_public_key - >>> key = load_pem_public_key(public_pem_data, backend=default_backend()) + >>> key = load_pem_public_key(public_pem_data) >>> isinstance(key, rsa.RSAPublicKey) True @@ -208,7 +207,7 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END >>> from cryptography.hazmat.primitives.serialization import load_pem_parameters >>> from cryptography.hazmat.primitives.asymmetric import dh - >>> parameters = load_pem_parameters(parameters_pem_data, backend=default_backend()) + >>> parameters = load_pem_parameters(parameters_pem_data) >>> isinstance(parameters, dh.DHParameters) True @@ -274,10 +273,9 @@ the rest. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> from cryptography.hazmat.primitives.serialization import load_der_private_key - >>> key = load_der_private_key(der_data, password=None, backend=default_backend()) + >>> key = load_der_private_key(der_data, password=None) >>> isinstance(key, rsa.RSAPrivateKey) True @@ -310,10 +308,9 @@ the rest. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> from cryptography.hazmat.primitives.serialization import load_der_public_key - >>> key = load_der_public_key(public_der_data, backend=default_backend()) + >>> key = load_der_public_key(public_der_data) >>> isinstance(key, rsa.RSAPublicKey) True @@ -341,10 +338,9 @@ the rest. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.asymmetric import dh >>> from cryptography.hazmat.primitives.serialization import load_der_parameters - >>> parameters = load_der_parameters(parameters_der_data, backend=default_backend()) + >>> parameters = load_der_parameters(parameters_der_data) >>> isinstance(parameters, dh.DHParameters) True diff --git a/docs/hazmat/primitives/asymmetric/utils.rst b/docs/hazmat/primitives/asymmetric/utils.rst index f46acb2ec081..487926e91256 100644 --- a/docs/hazmat/primitives/asymmetric/utils.rst +++ b/docs/hazmat/primitives/asymmetric/utils.rst @@ -57,7 +57,6 @@ Asymmetric Utilities .. doctest:: >>> import hashlib - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import ( ... padding, rsa, utils @@ -65,7 +64,6 @@ Asymmetric Utilities >>> private_key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> prehashed_msg = hashlib.sha256(b"A message I want to sign").digest() >>> signature = private_key.sign( diff --git a/docs/hazmat/primitives/asymmetric/x25519.rst b/docs/hazmat/primitives/asymmetric/x25519.rst index ea01fbaa08e7..014f3d01d5d3 100644 --- a/docs/hazmat/primitives/asymmetric/x25519.rst +++ b/docs/hazmat/primitives/asymmetric/x25519.rst @@ -21,7 +21,6 @@ present. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF @@ -39,7 +38,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # For the next handshake we MUST generate another private key. >>> private_key_2 = X25519PrivateKey.generate() @@ -50,7 +48,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key_2) Key interfaces diff --git a/docs/hazmat/primitives/asymmetric/x448.rst b/docs/hazmat/primitives/asymmetric/x448.rst index 4e1f0421f542..f166355b83fa 100644 --- a/docs/hazmat/primitives/asymmetric/x448.rst +++ b/docs/hazmat/primitives/asymmetric/x448.rst @@ -21,7 +21,6 @@ present. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric.x448 import X448PrivateKey >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF @@ -39,7 +38,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # For the next handshake we MUST generate another private key. >>> private_key_2 = X448PrivateKey.generate() @@ -50,7 +48,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key_2) Key interfaces diff --git a/docs/hazmat/primitives/cryptographic-hashes.rst b/docs/hazmat/primitives/cryptographic-hashes.rst index 6615ba6fa5ba..4cdc034a6b84 100644 --- a/docs/hazmat/primitives/cryptographic-hashes.rst +++ b/docs/hazmat/primitives/cryptographic-hashes.rst @@ -20,9 +20,8 @@ Message digests (Hashing) .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes - >>> digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + >>> digest = hashes.Hash(hashes.SHA256()) >>> digest.update(b"abc") >>> digest.update(b"123") >>> digest.finalize() diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst index fc74a98f039d..62457b28490c 100644 --- a/docs/hazmat/primitives/key-derivation-functions.rst +++ b/docs/hazmat/primitives/key-derivation-functions.rst @@ -55,8 +55,6 @@ PBKDF2 >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> # Salts should be randomly generated >>> salt = os.urandom(16) >>> # derive @@ -65,7 +63,6 @@ PBKDF2 ... length=32, ... salt=salt, ... iterations=100000, - ... backend=backend ... ) >>> key = kdf.derive(b"my great password") >>> # verify @@ -74,7 +71,6 @@ PBKDF2 ... length=32, ... salt=salt, ... iterations=100000, - ... backend=backend ... ) >>> kdf.verify(b"my great password", key) @@ -159,8 +155,6 @@ Scrypt >>> import os >>> from cryptography.hazmat.primitives.kdf.scrypt import Scrypt - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> salt = os.urandom(16) >>> # derive >>> kdf = Scrypt( @@ -169,7 +163,6 @@ Scrypt ... n=2**14, ... r=8, ... p=1, - ... backend=backend ... ) >>> key = kdf.derive(b"my great password") >>> # verify @@ -179,7 +172,6 @@ Scrypt ... n=2**14, ... r=8, ... p=1, - ... backend=backend ... ) >>> kdf.verify(b"my great password", key) @@ -276,21 +268,17 @@ ConcatKDF >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> otherinfo = b"concatkdf-example" >>> ckdf = ConcatKDFHash( ... algorithm=hashes.SHA256(), ... length=32, ... otherinfo=otherinfo, - ... backend=backend ... ) >>> key = ckdf.derive(b"input key") >>> ckdf = ConcatKDFHash( ... algorithm=hashes.SHA256(), ... length=32, ... otherinfo=otherinfo, - ... backend=backend ... ) >>> ckdf.verify(b"input key", key) @@ -364,8 +352,6 @@ ConcatKDF >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHMAC - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> salt = os.urandom(16) >>> otherinfo = b"concatkdf-example" >>> ckdf = ConcatKDFHMAC( @@ -373,7 +359,6 @@ ConcatKDF ... length=32, ... salt=salt, ... otherinfo=otherinfo, - ... backend=backend ... ) >>> key = ckdf.derive(b"input key") >>> ckdf = ConcatKDFHMAC( @@ -381,7 +366,6 @@ ConcatKDF ... length=32, ... salt=salt, ... otherinfo=otherinfo, - ... backend=backend ... ) >>> ckdf.verify(b"input key", key) @@ -468,8 +452,6 @@ HKDF >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> salt = os.urandom(16) >>> info = b"hkdf-example" >>> hkdf = HKDF( @@ -477,7 +459,6 @@ HKDF ... length=32, ... salt=salt, ... info=info, - ... backend=backend ... ) >>> key = hkdf.derive(b"input key") >>> hkdf = HKDF( @@ -485,7 +466,6 @@ HKDF ... length=32, ... salt=salt, ... info=info, - ... backend=backend ... ) >>> hkdf.verify(b"input key", key) @@ -575,22 +555,18 @@ HKDF >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDFExpand - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> info = b"hkdf-example" >>> key_material = os.urandom(16) >>> hkdf = HKDFExpand( ... algorithm=hashes.SHA256(), ... length=32, ... info=info, - ... backend=backend ... ) >>> key = hkdf.derive(key_material) >>> hkdf = HKDFExpand( ... algorithm=hashes.SHA256(), ... length=32, ... info=info, - ... backend=backend ... ) >>> hkdf.verify(key_material, key) @@ -676,8 +652,6 @@ KBKDF >>> from cryptography.hazmat.primitives.kdf.kbkdf import ( ... CounterLocation, KBKDFHMAC, Mode ... ) - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> label = b"KBKDF HMAC Label" >>> context = b"KBKDF HMAC Context" >>> kdf = KBKDFHMAC( @@ -690,7 +664,6 @@ KBKDF ... label=label, ... context=context, ... fixed=None, - ... backend=backend ... ) >>> key = kdf.derive(b"input key") >>> kdf = KBKDFHMAC( @@ -703,7 +676,6 @@ KBKDF ... label=label, ... context=context, ... fixed=None, - ... backend=backend ... ) >>> kdf.verify(b"input key", key) @@ -835,21 +807,17 @@ X963KDF >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.x963kdf import X963KDF - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> sharedinfo = b"ANSI X9.63 Example" >>> xkdf = X963KDF( ... algorithm=hashes.SHA256(), ... length=32, ... sharedinfo=sharedinfo, - ... backend=backend ... ) >>> key = xkdf.derive(b"input key") >>> xkdf = X963KDF( ... algorithm=hashes.SHA256(), ... length=32, ... sharedinfo=sharedinfo, - ... backend=backend ... ) >>> xkdf.verify(b"input key", key) diff --git a/docs/hazmat/primitives/mac/cmac.rst b/docs/hazmat/primitives/mac/cmac.rst index 796af22007b1..b40a90a82aa9 100644 --- a/docs/hazmat/primitives/mac/cmac.rst +++ b/docs/hazmat/primitives/mac/cmac.rst @@ -26,10 +26,9 @@ A subset of CMAC with the AES-128 algorithm is described in :rfc:`4493`. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import cmac >>> from cryptography.hazmat.primitives.ciphers import algorithms - >>> c = cmac.CMAC(algorithms.AES(key), backend=default_backend()) + >>> c = cmac.CMAC(algorithms.AES(key)) >>> c.update(b"message to authenticate") >>> c.finalize() b'CT\x1d\xc8\x0e\x15\xbe4e\xdb\xb6\x84\xca\xd9Xk' @@ -47,7 +46,7 @@ A subset of CMAC with the AES-128 algorithm is described in :rfc:`4493`. .. doctest:: - >>> c = cmac.CMAC(algorithms.AES(key), backend=default_backend()) + >>> c = cmac.CMAC(algorithms.AES(key)) >>> c.update(b"message to authenticate") >>> c.verify(b"an incorrect signature") Traceback (most recent call last): diff --git a/docs/hazmat/primitives/mac/hmac.rst b/docs/hazmat/primitives/mac/hmac.rst index ba49da224236..3695270b7ab2 100644 --- a/docs/hazmat/primitives/mac/hmac.rst +++ b/docs/hazmat/primitives/mac/hmac.rst @@ -27,9 +27,8 @@ of a message. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes, hmac - >>> h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend()) + >>> h = hmac.HMAC(key, hashes.SHA256()) >>> h.update(b"message to hash") >>> h.finalize() b'#F\xdaI\x8b"e\xc4\xf1\xbb\x9a\x8fc\xff\xf5\xdex.\xbc\xcd/+\x8a\x86\x1d\x84\'\xc3\xa6\x1d\xd8J' @@ -47,7 +46,7 @@ of a message. .. doctest:: - >>> h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend()) + >>> h = hmac.HMAC(key, hashes.SHA256()) >>> h.update(b"message to hash") >>> h.verify(b"an incorrect signature") Traceback (most recent call last): diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index 74db180cb2a9..287607df2c7f 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -33,11 +33,9 @@ it fits your needs before implementing anything using this module.** >>> import os >>> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> key = os.urandom(32) >>> iv = os.urandom(16) - >>> cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) + >>> cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) >>> encryptor = cipher.encryptor() >>> ct = encryptor.update(b"a secret message") + encryptor.finalize() >>> decryptor = cipher.decryptor() @@ -147,10 +145,9 @@ Algorithms .. doctest:: >>> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - >>> from cryptography.hazmat.backends import default_backend >>> nonce = os.urandom(16) >>> algorithm = algorithms.ChaCha20(key, nonce) - >>> cipher = Cipher(algorithm, mode=None, backend=default_backend()) + >>> cipher = Cipher(algorithm, mode=None) >>> encryptor = cipher.encryptor() >>> ct = encryptor.update(b"a secret message") >>> decryptor = cipher.decryptor() @@ -231,9 +228,8 @@ Weak ciphers .. doctest:: >>> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - >>> from cryptography.hazmat.backends import default_backend >>> algorithm = algorithms.ARC4(key) - >>> cipher = Cipher(algorithm, mode=None, backend=default_backend()) + >>> cipher = Cipher(algorithm, mode=None) >>> encryptor = cipher.encryptor() >>> ct = encryptor.update(b"a secret message") >>> decryptor = cipher.decryptor() @@ -425,7 +421,6 @@ Modes import os - from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, modes ) @@ -439,7 +434,6 @@ Modes encryptor = Cipher( algorithms.AES(key), modes.GCM(iv), - backend=default_backend() ).encryptor() # associated_data will be authenticated but not encrypted, @@ -458,7 +452,6 @@ Modes decryptor = Cipher( algorithms.AES(key), modes.GCM(iv, tag), - backend=default_backend() ).decryptor() # We put associated_data back in or the tag will fail to verify @@ -595,11 +588,9 @@ Interfaces >>> import os >>> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> key = os.urandom(32) >>> iv = os.urandom(16) - >>> cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) + >>> cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) >>> encryptor = cipher.encryptor() >>> # the buffer needs to be at least len(data) + n - 1 where n is cipher/mode block size in bytes >>> buf = bytearray(31) diff --git a/docs/hazmat/primitives/twofactor.rst b/docs/hazmat/primitives/twofactor.rst index 838e5e1055c3..1d2ab452ce0a 100644 --- a/docs/hazmat/primitives/twofactor.rst +++ b/docs/hazmat/primitives/twofactor.rst @@ -33,11 +33,10 @@ codes (HMAC). .. doctest:: >>> import os - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.twofactor.hotp import HOTP >>> from cryptography.hazmat.primitives.hashes import SHA1 >>> key = os.urandom(20) - >>> hotp = HOTP(key, 6, SHA1(), backend=default_backend()) + >>> hotp = HOTP(key, 6, SHA1()) >>> hotp_value = hotp.generate(0) >>> hotp.verify(hotp_value, 0) @@ -129,7 +128,7 @@ similar to the following code. assert look_ahead >= 0 correct_counter = None - otp = HOTP(key, 6, default_backend()) + otp = HOTP(key, 6) for count in range(counter, counter + look_ahead): try: otp.verify(hotp, count) @@ -155,11 +154,10 @@ similar to the following code. >>> import os >>> import time - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.twofactor.totp import TOTP >>> from cryptography.hazmat.primitives.hashes import SHA1 >>> key = os.urandom(20) - >>> totp = TOTP(key, 8, SHA1(), 30, backend=default_backend()) + >>> totp = TOTP(key, 8, SHA1(), 30) >>> time_value = time.time() >>> totp_value = totp.generate(time_value) >>> totp.verify(totp_value, time_value) diff --git a/docs/x509/ocsp.rst b/docs/x509/ocsp.rst index 80ff99087c78..0c2d07aef852 100644 --- a/docs/x509/ocsp.rst +++ b/docs/x509/ocsp.rst @@ -167,12 +167,11 @@ Creating Requests .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import serialization >>> from cryptography.hazmat.primitives.hashes import SHA1 >>> from cryptography.x509 import load_pem_x509_certificate, ocsp - >>> cert = load_pem_x509_certificate(pem_cert, default_backend()) - >>> issuer = load_pem_x509_certificate(pem_issuer, default_backend()) + >>> cert = load_pem_x509_certificate(pem_cert) + >>> issuer = load_pem_x509_certificate(pem_issuer) >>> builder = ocsp.OCSPRequestBuilder() >>> # SHA1 is in this example because RFC 5019 mandates its use. >>> builder = builder.add_certificate(cert, issuer, SHA1()) @@ -315,13 +314,12 @@ Creating Responses .. doctest:: >>> import datetime - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes, serialization >>> from cryptography.x509 import load_pem_x509_certificate, ocsp - >>> cert = load_pem_x509_certificate(pem_cert, default_backend()) - >>> issuer = load_pem_x509_certificate(pem_issuer, default_backend()) - >>> responder_cert = load_pem_x509_certificate(pem_responder_cert, default_backend()) - >>> responder_key = serialization.load_pem_private_key(pem_responder_key, None, default_backend()) + >>> cert = load_pem_x509_certificate(pem_cert) + >>> issuer = load_pem_x509_certificate(pem_issuer) + >>> responder_cert = load_pem_x509_certificate(pem_responder_cert) + >>> responder_key = serialization.load_pem_private_key(pem_responder_key, None) >>> builder = ocsp.OCSPResponseBuilder() >>> # SHA1 is in this example because RFC 5019 mandates its use. >>> builder = builder.add_response( @@ -350,7 +348,6 @@ Creating Responses .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes, serialization >>> from cryptography.x509 import load_pem_x509_certificate, ocsp >>> response = ocsp.OCSPResponseBuilder.build_unsuccessful( diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 484339e61036..a46c5d623238 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -168,8 +168,7 @@ Loading Certificates .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend - >>> cert = x509.load_pem_x509_certificate(pem_data, default_backend()) + >>> cert = x509.load_pem_x509_certificate(pem_data) >>> cert.serial_number 2 @@ -212,9 +211,8 @@ Loading Certificate Revocation Lists .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes - >>> crl = x509.load_pem_x509_crl(pem_crl_data, default_backend()) + >>> crl = x509.load_pem_x509_crl(pem_crl_data) >>> isinstance(crl.signature_hash_algorithm, hashes.SHA256) True @@ -258,9 +256,8 @@ Loading Certificate Signing Requests .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes - >>> csr = x509.load_pem_x509_csr(pem_req_data, default_backend()) + >>> csr = x509.load_pem_x509_csr(pem_req_data) >>> isinstance(csr.signature_hash_algorithm, hashes.SHA256) True @@ -474,8 +471,8 @@ X.509 Certificate Object >>> from cryptography.hazmat.primitives.serialization import load_pem_public_key >>> from cryptography.hazmat.primitives.asymmetric import padding - >>> issuer_public_key = load_pem_public_key(pem_issuer_public_key, default_backend()) - >>> cert_to_check = x509.load_pem_x509_certificate(pem_data_to_check, default_backend()) + >>> issuer_public_key = load_pem_public_key(pem_issuer_public_key) + >>> cert_to_check = x509.load_pem_x509_certificate(pem_data_to_check) >>> issuer_public_key.verify( ... cert_to_check.signature, ... cert_to_check.tbs_certificate_bytes, @@ -671,7 +668,6 @@ X.509 Certificate Builder .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> from cryptography.x509.oid import NameOID @@ -680,7 +676,6 @@ X.509 Certificate Builder >>> private_key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> public_key = private_key.public_key() >>> builder = x509.CertificateBuilder() @@ -705,7 +700,6 @@ X.509 Certificate Builder ... ) >>> certificate = builder.sign( ... private_key=private_key, algorithm=hashes.SHA256(), - ... backend=default_backend() ... ) >>> isinstance(certificate, x509.Certificate) True @@ -945,7 +939,6 @@ X.509 Certificate Revocation List Builder .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> from cryptography.x509.oid import NameOID @@ -954,7 +947,6 @@ X.509 Certificate Revocation List Builder >>> private_key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> builder = x509.CertificateRevocationListBuilder() >>> builder = builder.issuer_name(x509.Name([ @@ -966,11 +958,10 @@ X.509 Certificate Revocation List Builder ... 333 ... ).revocation_date( ... datetime.datetime.today() - ... ).build(default_backend()) + ... ).build() >>> builder = builder.add_revoked_certificate(revoked_cert) >>> crl = builder.sign( ... private_key=private_key, algorithm=hashes.SHA256(), - ... backend=default_backend() ... ) >>> len(crl) 1 @@ -1107,12 +1098,11 @@ X.509 Revoked Certificate Builder .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> import datetime >>> builder = x509.RevokedCertificateBuilder() >>> builder = builder.revocation_date(datetime.datetime.today()) >>> builder = builder.serial_number(3333) - >>> revoked_certificate = builder.build(default_backend()) + >>> revoked_certificate = builder.build() >>> isinstance(revoked_certificate, x509.RevokedCertificate) True @@ -1161,14 +1151,12 @@ X.509 CSR (Certificate Signing Request) Builder Object .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> from cryptography.x509.oid import AttributeOID, NameOID >>> private_key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> builder = x509.CertificateSigningRequestBuilder() >>> builder = builder.subject_name(x509.Name([ @@ -1181,7 +1169,7 @@ X.509 CSR (Certificate Signing Request) Builder Object ... AttributeOID.CHALLENGE_PASSWORD, b"changeit" ... ) >>> request = builder.sign( - ... private_key, hashes.SHA256(), default_backend() + ... private_key, hashes.SHA256() ... ) >>> isinstance(request, x509.CertificateSigningRequest) True @@ -1907,8 +1895,7 @@ X.509 Extensions .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend - >>> issuer_cert = x509.load_pem_x509_certificate(pem_data, default_backend()) + >>> issuer_cert = x509.load_pem_x509_certificate(pem_data) >>> x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_cert.public_key()) @@ -1937,8 +1924,7 @@ X.509 Extensions .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend - >>> issuer_cert = x509.load_pem_x509_certificate(pem_data, default_backend()) + >>> issuer_cert = x509.load_pem_x509_certificate(pem_data) >>> ski_ext = issuer_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier) >>> x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ski_ext.value) @@ -1985,8 +1971,7 @@ X.509 Extensions .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend - >>> csr = x509.load_pem_x509_csr(pem_req_data, default_backend()) + >>> csr = x509.load_pem_x509_csr(pem_req_data) >>> x509.SubjectKeyIdentifier.from_public_key(csr.public_key()) @@ -2021,9 +2006,8 @@ X.509 Extensions .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes - >>> cert = x509.load_pem_x509_certificate(cryptography_cert_pem, default_backend()) + >>> cert = x509.load_pem_x509_certificate(cryptography_cert_pem) >>> # Get the subjectAltName extension from the certificate >>> ext = cert.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME) >>> # Get the dNSName entries from the SAN extension diff --git a/docs/x509/tutorial.rst b/docs/x509/tutorial.rst index cc2ffb770683..f5ca416ceb9f 100644 --- a/docs/x509/tutorial.rst +++ b/docs/x509/tutorial.rst @@ -27,14 +27,12 @@ are the most common types of keys on the web right now): .. code-block:: pycon - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import serialization >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> # Generate our key >>> key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> # Write our key to disk for safe keeping >>> with open("path/to/store/key.pem", "wb") as f: @@ -76,7 +74,7 @@ a few details: ... ]), ... critical=False, ... # Sign the CSR with our private key. - ... ).sign(key, hashes.SHA256(), default_backend()) + ... ).sign(key, hashes.SHA256()) >>> # Write our CSR out to disk. >>> with open("path/to/csr.pem", "wb") as f: ... f.write(csr.public_bytes(serialization.Encoding.PEM)) @@ -105,7 +103,6 @@ Like generating a CSR, we start with creating a new private key: >>> key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> # Write our key to disk for safe keeping >>> with open("path/to/store/key.pem", "wb") as f: @@ -145,7 +142,7 @@ Then we generate the certificate itself: ... x509.SubjectAlternativeName([x509.DNSName(u"localhost")]), ... critical=False, ... # Sign our certificate with our private key - ... ).sign(key, hashes.SHA256(), default_backend()) + ... ).sign(key, hashes.SHA256()) >>> # Write our certificate out to disk. >>> with open("path/to/certificate.pem", "wb") as f: ... f.write(cert.public_bytes(serialization.Encoding.PEM)) From 63dfc57fca688d0f8d0515001f249c317d5e54dc Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 29 Aug 2020 10:39:31 -0400 Subject: [PATCH 07/99] fixed verify script that couldn't have ever worked (#5443) --- docs/development/custom-vectors/secp256k1/verify_secp256k1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/development/custom-vectors/secp256k1/verify_secp256k1.py b/docs/development/custom-vectors/secp256k1/verify_secp256k1.py index 485f0718bc02..f721b0001213 100644 --- a/docs/development/custom-vectors/secp256k1/verify_secp256k1.py +++ b/docs/development/custom-vectors/secp256k1/verify_secp256k1.py @@ -35,12 +35,12 @@ def verify_one_vector(vector): signature, ec.ECDSA(CRYPTOGRAPHY_HASH_TYPES[digest_algorithm]()) ) verifier.update(message) - return verifier.verify() + verifier.verify() def verify_vectors(vectors): for vector in vectors: - assert verify_one_vector(vector) + verify_one_vector(vector) vector_path = os.path.join("asymmetric", "ECDSA", "SECP256K1", "SigGen.txt") From d9f182d8c0ba8923673e6da1fbdddc2f050d298f Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 1 Sep 2020 18:34:24 +0200 Subject: [PATCH 08/99] Add a missing space to py35 deprecation warning (#5448) This fixes a typo that's been introduced in #5387. --- src/cryptography/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptography/__init__.py b/src/cryptography/__init__.py index f16efce6ba78..7211614d7f4a 100644 --- a/src/cryptography/__init__.py +++ b/src/cryptography/__init__.py @@ -41,7 +41,7 @@ ) if sys.version_info[:2] == (3, 5): warnings.warn( - "Python 3.5 support will be dropped in the next release of" + "Python 3.5 support will be dropped in the next release of " "cryptography. Please upgrade your Python.", CryptographyDeprecationWarning, stacklevel=2, From ad05ebbb32677607344a6c68b37f253ac7e419cc Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 1 Sep 2020 15:21:13 -0500 Subject: [PATCH 09/99] re-add a few engine bindings for advanced users (#5449) * re-add a few engine bindings for advanced users For users who are capable of compiling cryptography against custom openssl and properly using these functions this hopefully allows PKCS11 usage through OpenSSL engines. * forgot to save my buffer --- src/_cffi_src/openssl/engine.py | 15 +++++++++++++++ .../hazmat/bindings/openssl/_conditional.py | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/src/_cffi_src/openssl/engine.py b/src/_cffi_src/openssl/engine.py index fa503a2644b5..24cdd42a8393 100644 --- a/src/_cffi_src/openssl/engine.py +++ b/src/_cffi_src/openssl/engine.py @@ -10,6 +10,7 @@ TYPES = """ typedef ... ENGINE; +typedef ... UI_METHOD; static const long Cryptography_HAS_ENGINE; """ @@ -25,6 +26,12 @@ int ENGINE_free(ENGINE *); const char *ENGINE_get_name(const ENGINE *); +// These bindings are unused by cryptography or pyOpenSSL but are present +// for advanced users who need them. +int ENGINE_ctrl_cmd_string(ENGINE *, const char *, const char *, int); +void ENGINE_load_builtin_engines(void); +EVP_PKEY *ENGINE_load_private_key(ENGINE *, const char *, UI_METHOD *, void *); +EVP_PKEY *ENGINE_load_public_key(ENGINE *, const char *, UI_METHOD *, void *); """ CUSTOMIZATIONS = """ @@ -44,6 +51,14 @@ const char *(*ENGINE_get_id)(const ENGINE *) = NULL; const char *(*ENGINE_get_name)(const ENGINE *) = NULL; +int (*ENGINE_ctrl_cmd_string)(ENGINE *, const char *, const char *, + int) = NULL; +void (*ENGINE_load_builtin_engines)(void) = NULL; +EVP_PKEY *(*ENGINE_load_private_key)(ENGINE *, const char *, UI_METHOD *, + void *) = NULL; +EVP_PKEY *(*ENGINE_load_public_key)(ENGINE *, const char *, + UI_METHOD *, void *) = NULL; + #else static const long Cryptography_HAS_ENGINE = 1; #endif diff --git a/src/cryptography/hazmat/bindings/openssl/_conditional.py b/src/cryptography/hazmat/bindings/openssl/_conditional.py index 9cf489acde06..cdc18eab6848 100644 --- a/src/cryptography/hazmat/bindings/openssl/_conditional.py +++ b/src/cryptography/hazmat/bindings/openssl/_conditional.py @@ -270,6 +270,10 @@ def cryptography_has_engine(): "ENGINE_free", "ENGINE_get_name", "Cryptography_add_osrandom_engine", + "ENGINE_ctrl_cmd_string", + "ENGINE_load_builtin_engines", + "ENGINE_load_private_key", + "ENGINE_load_public_key", ] From 3367c18bf2e71639843e38498f5ad2159835122d Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 5 Sep 2020 11:46:34 -0400 Subject: [PATCH 10/99] Be clear that a lack of authentication often means you don't have secrecy (#5454) --- docs/hazmat/primitives/symmetric-encryption.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index 287607df2c7f..8551acb2693f 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -11,7 +11,8 @@ where the sender and receiver both use the same secret key. Note that symmetric encryption is **not** sufficient for most applications because it only provides secrecy but not authenticity. That means an attacker can't see the message but an attacker can create bogus messages and force the application to -decrypt them. +decrypt them. In many contexts, a lack of authentication on encrypted messages +can result in a loss of secrecy as well. For this reason it is **strongly** recommended to combine encryption with a message authentication code, such as :doc:`HMAC `, From bfe7b455c52437e3cf555472d7ca03f00740277d Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 13 Sep 2020 20:03:36 -0400 Subject: [PATCH 11/99] Account for Bruce redoing his website or something (#5461) --- docs/development/test-vectors.rst | 2 +- vectors/cryptography_vectors/ciphers/Blowfish/bf-cbc.txt | 2 +- vectors/cryptography_vectors/ciphers/Blowfish/bf-cfb.txt | 2 +- vectors/cryptography_vectors/ciphers/Blowfish/bf-ecb.txt | 2 +- vectors/cryptography_vectors/ciphers/Blowfish/bf-ofb.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index 720bbbfb1cb7..212f0e847e18 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -731,7 +731,7 @@ header format (substituting the correct information): .. _`IETF`: https://www.ietf.org/ .. _`Project Wycheproof`: https://github.com/google/wycheproof .. _`NIST CAVP`: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program -.. _`Bruce Schneier's vectors`: https://www.schneier.com/code/vectors.txt +.. _`Bruce Schneier's vectors`: https://www.schneier.com/wp-content/uploads/2015/12/vectors-2.txt .. _`Camellia page`: https://info.isl.ntt.co.jp/crypt/eng/camellia/ .. _`CRYPTREC`: https://www.cryptrec.go.jp .. _`OpenSSL's test vectors`: https://github.com/openssl/openssl/blob/97cf1f6c2854a3a955fd7dd3a1f113deba00c9ef/crypto/evp/evptests.txt#L232 diff --git a/vectors/cryptography_vectors/ciphers/Blowfish/bf-cbc.txt b/vectors/cryptography_vectors/ciphers/Blowfish/bf-cbc.txt index 184d9565fd98..ad3fa0cf2fe6 100644 --- a/vectors/cryptography_vectors/ciphers/Blowfish/bf-cbc.txt +++ b/vectors/cryptography_vectors/ciphers/Blowfish/bf-cbc.txt @@ -1,4 +1,4 @@ -# Reformatted from https://www.schneier.com/code/vectors.txt +# Reformatted from https://www.schneier.com/wp-content/uploads/2015/12/vectors-2.txt # to look like the NIST vectors [ENCRYPT] diff --git a/vectors/cryptography_vectors/ciphers/Blowfish/bf-cfb.txt b/vectors/cryptography_vectors/ciphers/Blowfish/bf-cfb.txt index 8a326f500d46..cd2f58ff91df 100644 --- a/vectors/cryptography_vectors/ciphers/Blowfish/bf-cfb.txt +++ b/vectors/cryptography_vectors/ciphers/Blowfish/bf-cfb.txt @@ -1,4 +1,4 @@ -# Reformatted from https://www.schneier.com/code/vectors.txt +# Reformatted from https://www.schneier.com/wp-content/uploads/2015/12/vectors-2.txt # to look like the NIST vectors [ENCRYPT] diff --git a/vectors/cryptography_vectors/ciphers/Blowfish/bf-ecb.txt b/vectors/cryptography_vectors/ciphers/Blowfish/bf-ecb.txt index bb18a5a3f1bc..70c1c030803f 100644 --- a/vectors/cryptography_vectors/ciphers/Blowfish/bf-ecb.txt +++ b/vectors/cryptography_vectors/ciphers/Blowfish/bf-ecb.txt @@ -1,4 +1,4 @@ -# Reformatted from https://www.schneier.com/code/vectors.txt +# Reformatted from https://www.schneier.com/wp-content/uploads/2015/12/vectors-2.txt # to look like the NIST vectors [ENCRYPT] diff --git a/vectors/cryptography_vectors/ciphers/Blowfish/bf-ofb.txt b/vectors/cryptography_vectors/ciphers/Blowfish/bf-ofb.txt index 21a7421842f2..f87609a996ca 100644 --- a/vectors/cryptography_vectors/ciphers/Blowfish/bf-ofb.txt +++ b/vectors/cryptography_vectors/ciphers/Blowfish/bf-ofb.txt @@ -1,4 +1,4 @@ -# Reformatted from https://www.schneier.com/code/vectors.txt +# Reformatted from https://www.schneier.com/wp-content/uploads/2015/12/vectors-2.txt # to look like the NIST vectors [ENCRYPT] From 1a9e2e1ad7d4c7aadbad24d447896e74177f6fd7 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 13 Sep 2020 19:04:18 -0500 Subject: [PATCH 12/99] allow bytes-like for padding (#5462) this doesn't improve efficiency in any way (copies galore!), but it does make it consistent between a cipher context and a padding context --- docs/hazmat/primitives/padding.rst | 3 ++- src/cryptography/hazmat/primitives/padding.py | 8 +++---- tests/hazmat/primitives/test_padding.py | 24 +++++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/docs/hazmat/primitives/padding.rst b/docs/hazmat/primitives/padding.rst index 9581df88bd70..99d500a05b68 100644 --- a/docs/hazmat/primitives/padding.rst +++ b/docs/hazmat/primitives/padding.rst @@ -107,7 +107,8 @@ multiple of the block size. .. method:: update(data) - :param bytes data: The data you wish to pass into the context. + :param data: The data you wish to pass into the context. + :type data: :term:`bytes-like` :return bytes: Returns the data that was padded or unpadded. :raises TypeError: Raised if data is not bytes. :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`. diff --git a/src/cryptography/hazmat/primitives/padding.py b/src/cryptography/hazmat/primitives/padding.py index 95913614cb2d..d3dc7093ae51 100644 --- a/src/cryptography/hazmat/primitives/padding.py +++ b/src/cryptography/hazmat/primitives/padding.py @@ -40,9 +40,9 @@ def _byte_padding_update(buffer_, data, block_size): if buffer_ is None: raise AlreadyFinalized("Context was already finalized.") - utils._check_bytes("data", data) + utils._check_byteslike("data", data) - buffer_ += data + buffer_ += bytes(data) finished_blocks = len(buffer_) // (block_size // 8) @@ -64,9 +64,9 @@ def _byte_unpadding_update(buffer_, data, block_size): if buffer_ is None: raise AlreadyFinalized("Context was already finalized.") - utils._check_bytes("data", data) + utils._check_byteslike("data", data) - buffer_ += data + buffer_ += bytes(data) finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0) diff --git a/tests/hazmat/primitives/test_padding.py b/tests/hazmat/primitives/test_padding.py index f66d0ee8521d..bf5379730131 100644 --- a/tests/hazmat/primitives/test_padding.py +++ b/tests/hazmat/primitives/test_padding.py @@ -109,6 +109,18 @@ def test_large_padding(self): assert data == b"" + def test_bytearray(self): + padder = padding.PKCS7(128).padder() + unpadded = bytearray(b"t" * 38) + padded = ( + padder.update(unpadded) + + padder.update(unpadded) + + padder.finalize() + ) + unpadder = padding.PKCS7(128).unpadder() + final = unpadder.update(padded) + unpadder.finalize() + assert final == unpadded + unpadded + class TestANSIX923(object): @pytest.mark.parametrize("size", [127, 4096, -2]) @@ -193,3 +205,15 @@ def test_use_after_finalize(self): unpadder.update(b"") with pytest.raises(AlreadyFinalized): unpadder.finalize() + + def test_bytearray(self): + padder = padding.ANSIX923(128).padder() + unpadded = bytearray(b"t" * 38) + padded = ( + padder.update(unpadded) + + padder.update(unpadded) + + padder.finalize() + ) + unpadder = padding.ANSIX923(128).unpadder() + final = unpadder.update(padded) + unpadder.finalize() + assert final == unpadded + unpadded From 6d3644f4e55d491b85bc790f51a989c5edccb9fb Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 13 Sep 2020 19:20:09 -0500 Subject: [PATCH 13/99] add pkcs7/smime bindings (#5458) * add pkcs7/smime bindings * Update src/_cffi_src/openssl/pkcs7.py Co-authored-by: Alex Gaynor * Update src/_cffi_src/openssl/pkcs7.py Co-authored-by: Alex Gaynor Co-authored-by: Alex Gaynor --- src/_cffi_src/openssl/pkcs7.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/_cffi_src/openssl/pkcs7.py b/src/_cffi_src/openssl/pkcs7.py index 7f31b82b6296..72f9c2130246 100644 --- a/src/_cffi_src/openssl/pkcs7.py +++ b/src/_cffi_src/openssl/pkcs7.py @@ -24,6 +24,7 @@ typedef ... PKCS7_DIGEST; typedef ... PKCS7_ENCRYPT; typedef ... PKCS7_ENVELOPE; +typedef ... PKCS7_SIGNER_INFO; typedef struct { ASN1_OBJECT *type; @@ -51,10 +52,21 @@ static const int PKCS7_NOVERIFY; static const int PKCS7_STREAM; static const int PKCS7_TEXT; +static const int PKCS7_PARTIAL; """ FUNCTIONS = """ void PKCS7_free(PKCS7 *); +PKCS7 *PKCS7_sign(X509 *, EVP_PKEY *, Cryptography_STACK_OF_X509 *, + BIO *, int); +int SMIME_write_PKCS7(BIO *, PKCS7 *, BIO *, int); +PKCS7_SIGNER_INFO *PKCS7_sign_add_signer(PKCS7 *, X509 *, EVP_PKEY *, + const EVP_MD *, int); +int PKCS7_final(PKCS7 *, BIO *, int); +/* Included verify due to external consumer, see + https://github.com/pyca/cryptography/issues/5433 */ +int PKCS7_verify(PKCS7 *, Cryptography_STACK_OF_X509 *, X509_STORE *, BIO *, + BIO *, int); int PKCS7_type_is_signed(PKCS7 *); int PKCS7_type_is_enveloped(PKCS7 *); From 00ba159220a2dff6ee32d64fcfeff48f742946b4 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 14 Sep 2020 18:40:05 -0500 Subject: [PATCH 14/99] add RSA 4096-bit self-signed CA for some upcoming tests (#5464) --- docs/development/test-vectors.rst | 4 ++ .../x509/custom/ca/rsa_ca.pem | 28 ++++++++++ .../x509/custom/ca/rsa_key.pem | 52 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 vectors/cryptography_vectors/x509/custom/ca/rsa_ca.pem create mode 100644 vectors/cryptography_vectors/x509/custom/ca/rsa_key.pem diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index 212f0e847e18..6146c9c139b9 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -109,6 +109,8 @@ Custom asymmetric vectors * ``x509/custom/ca/ca_key.pem`` - An unencrypted PCKS8 ``secp256r1`` key. It is the private key for the certificate ``x509/custom/ca/ca.pem``. This key is encoded in several of the PKCS12 custom vectors. +* ``x509/custom/ca/rsa_key.pem`` - An unencrypted PCKS8 4096 bit RSA key. It is + the private key for the certificate ``x509/custom/ca/rsa_ca.pem``. * ``asymmetric/EC/compressed_points.txt`` - Contains compressed public points generated using OpenSSL. * ``asymmetric/X448/x448-pkcs8-enc.pem`` and @@ -414,6 +416,8 @@ Custom X.509 Vectors * ``rsa_pss.pem`` - A certificate with an RSA PSS signature. * ``root-ed448.pem`` - An ``ed448`` self-signed CA certificate using ``ed448-pkcs8.pem`` as key. +* ``ca/rsa_ca.pem`` - A self-signed RSA certificate with ``basicConstraints`` + set to true. Its private key is ``ca/rsa_key.pem``. Custom X.509 Request Vectors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/vectors/cryptography_vectors/x509/custom/ca/rsa_ca.pem b/vectors/cryptography_vectors/x509/custom/ca/rsa_ca.pem new file mode 100644 index 000000000000..089bcce10e72 --- /dev/null +++ b/vectors/cryptography_vectors/x509/custom/ca/rsa_ca.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIExzCCAq+gAwIBAgIJAOcS06ClbtbJMA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNV +BAMMD2NyeXB0b2dyYXBoeSBDQTAeFw0yMDA5MTQyMTQwNDJaFw00ODAxMzEyMTQw +NDJaMBoxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANBIheRc1HT4MzV5GvUbDk9CFU6DTomRApNqRmizriRq +m6OY4Ht3d71BXog6/IBkqAnZ4/XJQ40G4sVDb52k11oPvfJ/F5pc+6UqPBL+QGzY +GkJoubAqXFpI6ow0qayFNQLv0T9o4yh0QQOoGvgCmv91qmitLrZNXu4U9S76G+Di +GST+QyMkMxj+VsGRsRRBufV1urcnvFWjU6Q2+cr2cp0mMAG96NTyIskYiJ8vL03W +z4DX4klO4X47fPmDnU/OMn4SbvMZ896j1L0J04S+uVThTkxQWcFcqXhX5qM8kzcj +JUmybFlbf150j3WiucW48K/j7fJ0x9q3iUo4Gva0coScglJWcgo/BBCwFDw8NVba +7npxSRMiaS3qTv0dEFcRnvByc+7hyGxxlWdTE9tHisUI1eZVk9P9ziqNOZKscY8Z +X1+/C4M9X69Y7A8I74F5dO27IRycEgOrSo2z1NhfSwbqJr9a2TBtRsFinn8rjKBI +zNn0E5p9jO1WjxtkcjHfXXpLN8FFMvoYI9l/K+ZWDm9sboaF8jrgozSc004AFemA +H79mmCGVRKXn1vDAo4DLC6p3NiBFYQcYbW9V+beGD6srsF6xJtuY/UwtPROLWSzu +CCrZ/4BlmpNsR0ehIFFvzEKjX6rR2yp3YKlguDbMBMKMpfSGxAFwcZ7OiaxR20UH +AgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBADSveDS4 +y2V/N6Li2n9ChGNdCMr/45M0cl+GpL55aA36AWYMRLv0wip7MWV3yOj4mkjGBlTE +awKHH1FtetsE6B4a7M2hHhOXyXE60uUdptEx6ckGrJ1iyqu5cQUX1P+VnXbmOxfF +bl+Ugzjbgirx239rA4ezkDRuOvKcCbDOFV/gw3ZHfJ/IQeRXIQRl/y51wcnFUvFM +JEESYiijeDbEcY8r1/phmVQL0CO7WLMmTxlFj4X/TR3MTZWJQIap9GiLs5+n3QiO +jsZ3GuFOomB8oTebYkXniwbNu5hgLP/seRQzGA7B9VDZryAhCtvGgjtQh0eW2Qxt +sgmDJGOPKnKT3O5U0v3+IPLEYpe8JSzgAhhh6H1rAJRUNwP2gRcO4eOUJSkdl218 +fRNT0ILzosuWxwprER9ciMQF8q0JJKMhcfHRMH0S5mWVJAIkj68KY05oCy2zNyYa +oruopKSWXe0Bzr40znm40P7xIkui2BGQMlDPpbCaEfLsLqyctfbdmMlxac/QgIfY +TltrbqmI3MNy5uqGViGFpWPCB+kD8EsJF9nlKJXlu/i55qgUr/2/2CdeWlZDBP8A +1fdzmpYpWnwhE0KobzLS2z3AwDxiY/RSWUfypLZA0K/lpaEtYB6UHMDZ0/8WqgZV +gNucCuty0cA4Kf7eX1TlAKVwH8hTkVmJc2rX +-----END CERTIFICATE----- diff --git a/vectors/cryptography_vectors/x509/custom/ca/rsa_key.pem b/vectors/cryptography_vectors/x509/custom/ca/rsa_key.pem new file mode 100644 index 000000000000..97e39a501f20 --- /dev/null +++ b/vectors/cryptography_vectors/x509/custom/ca/rsa_key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDQSIXkXNR0+DM1 +eRr1Gw5PQhVOg06JkQKTakZos64kapujmOB7d3e9QV6IOvyAZKgJ2eP1yUONBuLF +Q2+dpNdaD73yfxeaXPulKjwS/kBs2BpCaLmwKlxaSOqMNKmshTUC79E/aOModEED +qBr4Apr/daporS62TV7uFPUu+hvg4hkk/kMjJDMY/lbBkbEUQbn1dbq3J7xVo1Ok +NvnK9nKdJjABvejU8iLJGIifLy9N1s+A1+JJTuF+O3z5g51PzjJ+Em7zGfPeo9S9 +CdOEvrlU4U5MUFnBXKl4V+ajPJM3IyVJsmxZW39edI91ornFuPCv4+3ydMfat4lK +OBr2tHKEnIJSVnIKPwQQsBQ8PDVW2u56cUkTImkt6k79HRBXEZ7wcnPu4chscZVn +UxPbR4rFCNXmVZPT/c4qjTmSrHGPGV9fvwuDPV+vWOwPCO+BeXTtuyEcnBIDq0qN +s9TYX0sG6ia/WtkwbUbBYp5/K4ygSMzZ9BOafYztVo8bZHIx3116SzfBRTL6GCPZ +fyvmVg5vbG6GhfI64KM0nNNOABXpgB+/ZpghlUSl59bwwKOAywuqdzYgRWEHGG1v +Vfm3hg+rK7BesSbbmP1MLT0Ti1ks7ggq2f+AZZqTbEdHoSBRb8xCo1+q0dsqd2Cp +YLg2zATCjKX0hsQBcHGezomsUdtFBwIDAQABAoICAQDH6YQRvwPwzTWhkn7MWU6v +xjbbJ+7e3T9CrNOttSBlNanzKU31U6KrFS4dxbgLqBEde3Rwud/LYZuRSPu9rLVC +bS+crF3EPJEQY2xLspu1nOn/abMoolAIHEp7jiR5QVWzXulRWmQFtSed0eEowJ9y +qMaKOAdI1RRToev/TfIqM/l8Z0ubVChzSdONcUAsuDU7ouc22r3K2Lv0Nwwkwc0a +hse3NEdg9JNsvs6LM2fM52w9N3ircjm+xmxatPft3HTcSucREIzg2hDb7K2HkOQj +0ykq2Eh97ml+56eocADBAEvO46FZVxf2WhxEBY8Xdz4VJMmDWJFmnZj5ksZWmrX6 +U5BfFY7DZvE2EpoZ5ph1Fm6dcXrJFkaZEyJLlzFKehXMipVenjCanIPpEEUvIz+p +m0QVoNJRj/GcNyIEZ0BCXedBOUWU4XE1pG4r6oZqwUvcjsVrqXP5kbJMVybiS6Kd +6T8ve+4qsn3ZvGRVKjInqf2WI0Wvum2sTF+4OAkYvFel9dKNjpYnnj4tLFc/EKWz +9+pE/Zz5fMOyMD9qXM6bdVkPjWjy1vXmNW4qFCZljrb395hTvsAPMsO6bbAM+lu6 +YcdOAf8k7awTb79kPMrPcbCygyKSGN9C9T3a/Nhrbr3TPi9SD9hC5Q8bL9uSHcR2 +hgRQcApxsfDRrGwy2lheEQKCAQEA/Hrynao+k6sYtlDc/ueCjb323EzsuhOxPqUZ +fKtGeFkJzKuaKTtymasvVpAAqJBEhTALrptGWlJQ0Y/EVaPpZ9pmk791EWNXdXsX +wwufbHxm6K9aOeogev8cd+B/9wUAQPQVotyRzCcOfbVe7t81cBNktqam5Zb9Y4Zr +qu63gBB1UttdmIF5qitl3JcFztlBjiza2UrqgVdKE+d9vLR84IBRy3dyQIOi6C1c +y37GNgObjx8ZcUVV54/KgvoVvDkvN6TEbUdC9eQz7FW7DA7MMVqyDvWZrSjBzVhK +2bTrd+Pi6S4n/ETvA6XRufHC8af4bdE2hzuq5VZO1kkgH37djwKCAQEA0y/YU0b4 +vCYpZ1MNhBFI6J9346DHD55Zu5dWFRqNkC0PiO6xEMUaUMbG4gxkiQPNT5WvddQs +EbRQTnd4FFdqB7XWoH+wERN7zjbT+BZVrHVC4gxEEy33s5oXGn7/ATxaowo7I4oq +15MwgZu3hBNxVUtuePZ6D9/ePNGOGOUtdMRrusmVX7gZEXxwvlLJXyVepl2V4JV1 +otI8EZCcoRhSfeYNEs4VhN0WmfMSV7ge0eFfVb6Lb+6PCcasYED8S0tBN2vjzvol +zCMv8skPATm7SopqBDoBPcXCHwN/gUFXHf/lrvE6bbeX1ZMxnRYKdQLLNYyQK9cr +nCUJXuNM21tVCQKCAQBapCkFwWDF0t8EVPOB78tG57QAUv2JsBgpzUvhHfwmqJCE +Efc+ZkE2Oea8xOX3nhN7XUxUWxpewr6Q/XQW6smYpye8UzfMDkYPvylAtKN/Zwnq +70kNEainf37Q6qAGJp14tCgwV89f44WoS7zRNQESQ2QczqeMNTCy0kdFDn6CU2ZL +YMWxQopTNVFUaEOFhympySCoceTOmm/VxX22iXVrg6XZzgAOeTO69s4hoFm4eoMW +Vqvjpmi4wT6K1w2GjWEOMPDz6ml3rX2WkxCbu5RDA7R4+mM5bzBkcBYvImyGliGY +ZSGlx3mnbZhlkQ3Tg+IESt+wnRM1Uk7rT0VhCUKxAoIBABWYuPibM2iaRnWoiqNM +2TXgyPPgRzsTqH2ElmsGEiACW6pXLohWf8Bu83u+ZLGWT/Kpjg3wqqkM1YGQuhjq +b49mSxKSvECiy3BlLvwZ3J0MSNCxDG0hsEkPovk0r4NC1soBi9awlH0DMlyuve+l +xVtBoYSBQC5LaICztWJaXXGpfJLXdo0ZWIbvQOBVuv4d5jYBMAiNgEAsW7Q4I6xd +vmHdmsyngo/ZxCvuLZwG2jAAai1slPnXXY1UYeBeBO72PS8bu2o5LpBXsNmVMhGg +A8U1rm3MOMBGbvmY8/sV4YDR4H0pch4yPja7HMHBtUQOCxXoz/2LvYv0RacMe5mb +F3ECggEAWxQZnT8pObxKrISZpHSKi54VxuLYbemS63Tdr4HE/KuiFAvbM6AeZOki +jbiMnqrCTOhJRS/i9HV78zSxRZZyVm961tnsjqMyaamX/S4yD7v3Vzu1mfsdVCa2 +Sl+JUUxsEgs/G3Fu6I/0TsCSn/HgNLM8b3f8TDkbpnOqKX165ddojXqSCfxjuYau +Szih/+jF1dz2/zBye1ARkLRdY/SzlzGl0cVn8bfkE0YEde7wvQ624Biy7r9i1o40 +7cy/8EQBR2FcXpOAZ7UgOqgGLNhXnd4FPsX4ldKOf5De8FErQOFirJ8pCUxFGr0U +fDWXtBuybAb5u+ZaVwHgqaaPCkKkVQ== +-----END PRIVATE KEY----- From c61f24bb4df584bd3a2fb640298dad4bd6351ecd Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 14 Sep 2020 23:05:14 -0500 Subject: [PATCH 15/99] add prelim python 3.9 CI (#5466) * add prelim python 3.9 CI * do we need v2? --- .github/workflows/ci.yml | 6 ++++-- .travis.yml | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3f514c1c5ba..afac37aaf956 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,11 +18,12 @@ jobs: - {VERSION: "2.7", TOXENV: "py27", EXTRA_CFLAGS: ""} - {VERSION: "3.5", TOXENV: "py35", EXTRA_CFLAGS: ""} - {VERSION: "3.8", TOXENV: "py38", EXTRA_CFLAGS: "-DUSE_OSRANDOM_RNG_FOR_TESTING"} + - {VERSION: "3.9.0-rc.1", TOXENV: "py39"} name: "Python ${{ matrix.PYTHON.VERSION }} on macOS" steps: - uses: actions/checkout@master - name: Setup python - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.PYTHON.VERSION }} @@ -63,11 +64,12 @@ jobs: - {VERSION: "3.6", TOXENV: "py36", MSVC_VERSION: "2019", CL_FLAGS: ""} - {VERSION: "3.7", TOXENV: "py37", MSVC_VERSION: "2019", CL_FLAGS: ""} - {VERSION: "3.8", TOXENV: "py38", MSVC_VERSION: "2019", CL_FLAGS: "/D USE_OSRANDOM_RNG_FOR_TESTING"} + - {VERSION: "3.9.0-rc.1", TOXENV: "py39", MSVC_VERSION: "2019", CL_FLAGS: ""} name: "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}" steps: - uses: actions/checkout@master - name: Setup python - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.PYTHON.VERSION }} architecture: ${{ matrix.WINDOWS.ARCH }} diff --git a/.travis.yml b/.travis.yml index fca538be18b9..b2d5ba949785 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,8 @@ matrix: # Setting 'python' is just to make travis's UI a bit prettier - python: 3.6 env: TOXENV=py36 + - python: 3.9-dev + env: TOXENV=py39 # Travis lists available Pythons (including PyPy) by arch and distro here: # https://docs.travis-ci.com/user/languages/python/#python-versions - python: pypy2.7-7.3.1 From 20c0388086b4eec91fdf1f9fd9535f4c741e4851 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 19 Sep 2020 18:07:26 -0500 Subject: [PATCH 16/99] smime signer support (#5465) * smime signer support * fix ed25519 check * change some wording * python 2.7... * review feedback * s/secure/signed * do some verification in the tests * review feedback * doc return value --- CHANGELOG.rst | 2 + docs/hazmat/primitives/index.rst | 1 + docs/hazmat/primitives/smime.rst | 128 +++++ src/_cffi_src/openssl/pkcs7.py | 1 + .../hazmat/backends/openssl/backend.py | 62 ++- src/cryptography/hazmat/primitives/smime.py | 109 ++++ tests/hazmat/primitives/test_smime.py | 518 ++++++++++++++++++ 7 files changed, 820 insertions(+), 1 deletion(-) create mode 100644 docs/hazmat/primitives/smime.rst create mode 100644 src/cryptography/hazmat/primitives/smime.py create mode 100644 tests/hazmat/primitives/test_smime.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 60d8b6182174..1c8925803e22 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,8 @@ Changelog * Support for OpenSSL 1.0.2 has been removed. Users on older version of OpenSSL will need to upgrade. +* Added basic support for SMIME signing via + :class:`~cryptography.hazmat.primitives.smime.SMIMESignatureBuilder`. .. _v3-1: diff --git a/docs/hazmat/primitives/index.rst b/docs/hazmat/primitives/index.rst index 72e5b26ce33d..bbe4418e7c18 100644 --- a/docs/hazmat/primitives/index.rst +++ b/docs/hazmat/primitives/index.rst @@ -14,5 +14,6 @@ Primitives mac/index cryptographic-hashes symmetric-encryption + smime padding twofactor diff --git a/docs/hazmat/primitives/smime.rst b/docs/hazmat/primitives/smime.rst new file mode 100644 index 000000000000..556dd9b55bad --- /dev/null +++ b/docs/hazmat/primitives/smime.rst @@ -0,0 +1,128 @@ +.. hazmat:: + +S/MIME +====== + +.. module:: cryptography.hazmat.primitives.smime + +.. testsetup:: + + ca_key = b""" + -----BEGIN PRIVATE KEY----- + MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgA8Zqz5vLeR0ePZUe + jBfdyMmnnI4U5uAJApWTsMn/RuWhRANCAAQY/8+7+Tm49d3D7sBAiwZ1BqtPzdgs + UiROH+AQRme1XxW5Yr07zwxvvhr3tKEPtLnLboazUPlsUb/Bgte+xfkF + -----END PRIVATE KEY----- + """.strip() + + ca_cert = b""" + -----BEGIN CERTIFICATE----- + MIIBUTCB96ADAgECAgIDCTAKBggqhkjOPQQDAjAnMQswCQYDVQQGEwJVUzEYMBYG + A1UEAwwPY3J5cHRvZ3JhcGh5IENBMB4XDTE3MDEwMTEyMDEwMFoXDTM4MTIzMTA4 + MzAwMFowJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBDQTBZ + MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBj/z7v5Obj13cPuwECLBnUGq0/N2CxS + JE4f4BBGZ7VfFblivTvPDG++Gve0oQ+0uctuhrNQ+WxRv8GC177F+QWjEzARMA8G + A1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhANES742XWm64tkGnz8Dn + pG6u2lHkZFQr3oaVvPcemvlbAiEA0WGGzmYx5C9UvfXIK7NEziT4pQtyESE0uRVK + Xw4nMqk= + -----END CERTIFICATE----- + """.strip() + + +S/MIME provides a method to send and receive signed MIME messages. It is +commonly used in email. S/MIME has multiple versions, but this +module implements a subset of :rfc:`2632`, also known as S/MIME Version 3. + + +.. class:: SMIMESignatureBuilder + + .. versionadded:: 3.2 + + .. doctest:: + + >>> from cryptography.hazmat.primitives import hashes, serialization, smime + >>> from cryptography import x509 + >>> cert = x509.load_pem_x509_certificate(ca_cert) + >>> key = serialization.load_pem_private_key(ca_key, None) + >>> options = [smime.SMIMEOptions.DetachedSignature] + >>> smime.SMIMESignatureBuilder().set_data( + ... b"data to sign" + ... ).add_signer( + ... cert, key, hashes.SHA256() + ... ).sign( + ... serialization.Encoding.PEM, options + ... ) + b'...' + + .. method:: set_data(data) + + :param data: The data to be hashed and signed. + :type data: :term:`bytes-like` + + .. method:: add_signer(certificate, private_key, hash_algorithm) + + :param certificate: The :class:`~cryptography.x509.Certificate`. + + :param private_key: The + :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` or + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` + associated with the certificate provided. + + :param hash_algorithm: The + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` that + will be used to generate the signature. This must be an instance of + :class:`~cryptography.hazmat.primitives.hashes.SHA1`, + :class:`~cryptography.hazmat.primitives.hashes.SHA224`, + :class:`~cryptography.hazmat.primitives.hashes.SHA256`, + :class:`~cryptography.hazmat.primitives.hashes.SHA384`, or + :class:`~cryptography.hazmat.primitives.hashes.SHA512`. + + .. method:: sign(encoding, options, backend=None) + + :param encoding: :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` + or :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`. + + :param options: A list of :class:`~cryptography.hazmat.primitives.smime.SMIMEOptions`. + + :return bytes: The signed S/MIME message. + + :param backend: An optional backend. + + +.. class:: SMIMEOptions + + .. versionadded:: 3.2 + + An enumeration of options for S/MIME signature creation. + + .. attribute:: Text + + The text option adds ``text/plain`` headers to the S/MIME message when + serializing to + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`. + This option is disallowed with ``DER`` serialization. + + .. attribute:: Binary + + S/MIME signing normally converts line endings (LF to CRLF). When + passing this option the data will not be converted. + + .. attribute:: DetachedSignature + + Don't embed the signed data within the ASN.1. When signing with + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` this + also results in the data being added as clear text before the + PEM encoded structure. + + .. attribute:: NoCapabilities + + S/MIME structures contain a ``MIMECapabilities`` section inside the + ``authenticatedAttributes``. Passing this as an option removes + ``MIMECapabilities``. + + .. attribute:: NoAttributes + + S/MIME structures contain an ``authenticatedAttributes`` section. + Passing this as an option removes that section. Note that if you + pass ``NoAttributes`` you can't pass ``NoCapabilities`` since + ``NoAttributes`` removes ``MIMECapabilities`` and more. diff --git a/src/_cffi_src/openssl/pkcs7.py b/src/_cffi_src/openssl/pkcs7.py index 72f9c2130246..1878ec59dc15 100644 --- a/src/_cffi_src/openssl/pkcs7.py +++ b/src/_cffi_src/openssl/pkcs7.py @@ -67,6 +67,7 @@ https://github.com/pyca/cryptography/issues/5433 */ int PKCS7_verify(PKCS7 *, Cryptography_STACK_OF_X509 *, X509_STORE *, BIO *, BIO *, int); +PKCS7 *SMIME_read_PKCS7(BIO *, BIO **); int PKCS7_type_is_signed(PKCS7 *); int PKCS7_type_is_enveloped(PKCS7 *); diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 97c7fd054495..f7d6a47c7a9c 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -115,7 +115,7 @@ _RevokedCertificate, ) from cryptography.hazmat.bindings.openssl import binding -from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives import hashes, serialization, smime from cryptography.hazmat.primitives.asymmetric import ( dsa, ec, @@ -2690,6 +2690,66 @@ def _load_pkcs7_certificates(self, p7): return certs + def smime_sign(self, builder, encoding, options): + bio = self._bytes_to_bio(builder._data) + init_flags = self._lib.PKCS7_PARTIAL + final_flags = 0 + + if smime.SMIMEOptions.DetachedSignature in options: + # Don't embed the data in the PKCS7 structure + init_flags |= self._lib.PKCS7_DETACHED + final_flags |= self._lib.PKCS7_DETACHED + + # This just inits a structure for us. However, there + # are flags we need to set, joy. + p7 = self._lib.PKCS7_sign( + self._ffi.NULL, + self._ffi.NULL, + self._ffi.NULL, + self._ffi.NULL, + init_flags, + ) + self.openssl_assert(p7 != self._ffi.NULL) + p7 = self._ffi.gc(p7, self._lib.PKCS7_free) + signer_flags = 0 + # These flags are configurable on a per-signature basis + # but we've deliberately chosen to make the API only allow + # setting it across all signatures for now. + if smime.SMIMEOptions.NoCapabilities in options: + signer_flags |= self._lib.PKCS7_NOSMIMECAP + elif smime.SMIMEOptions.NoAttributes in options: + signer_flags |= self._lib.PKCS7_NOATTR + for certificate, private_key, hash_algorithm in builder._signers: + md = self._evp_md_non_null_from_algorithm(hash_algorithm) + p7signerinfo = self._lib.PKCS7_sign_add_signer( + p7, certificate._x509, private_key._evp_pkey, md, signer_flags + ) + self.openssl_assert(p7signerinfo != self._ffi.NULL) + + for option in options: + # DetachedSignature, NoCapabilities, and NoAttributes are already + # handled so we just need to check these last two options. + if option is smime.SMIMEOptions.Text: + final_flags |= self._lib.PKCS7_TEXT + elif option is smime.SMIMEOptions.Binary: + final_flags |= self._lib.PKCS7_BINARY + + bio_out = self._create_mem_bio_gc() + if encoding is serialization.Encoding.PEM: + # This finalizes the structure + res = self._lib.SMIME_write_PKCS7( + bio_out, p7, bio.bio, final_flags + ) + else: + assert encoding is serialization.Encoding.DER + # We need to call finalize here becauase i2d_PKCS7_bio does not + # finalize. + res = self._lib.PKCS7_final(p7, bio.bio, final_flags) + self.openssl_assert(res == 1) + res = self._lib.i2d_PKCS7_bio(bio_out, p7) + self.openssl_assert(res == 1) + return self._read_mem_bio(bio_out) + class GetCipherByName(object): def __init__(self, fmt): diff --git a/src/cryptography/hazmat/primitives/smime.py b/src/cryptography/hazmat/primitives/smime.py new file mode 100644 index 000000000000..538ba6a00012 --- /dev/null +++ b/src/cryptography/hazmat/primitives/smime.py @@ -0,0 +1,109 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +from enum import Enum + +from cryptography import x509 +from cryptography.hazmat.backends import _get_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, rsa +from cryptography.utils import _check_byteslike + + +class SMIMESignatureBuilder(object): + def __init__(self, data=None, signers=[]): + self._data = data + self._signers = signers + + def set_data(self, data): + _check_byteslike("data", data) + if self._data is not None: + raise ValueError("data may only be set once") + + return SMIMESignatureBuilder(data, self._signers) + + def add_signer(self, certificate, private_key, hash_algorithm): + if not isinstance( + hash_algorithm, + ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + ), + ): + raise TypeError( + "hash_algorithm must be one of hashes.SHA1, SHA224, " + "SHA256, SHA384, or SHA512" + ) + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + if not isinstance( + private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) + ): + raise TypeError("Only RSA & EC keys are supported at this time.") + + return SMIMESignatureBuilder( + self._data, + self._signers + [(certificate, private_key, hash_algorithm)], + ) + + def sign(self, encoding, options, backend=None): + if len(self._signers) == 0: + raise ValueError("Must have at least one signer") + if self._data is None: + raise ValueError("You must add data to sign") + options = list(options) + if not all(isinstance(x, SMIMEOptions) for x in options): + raise ValueError("options must be from the SMIMEOptions enum") + if ( + encoding is not serialization.Encoding.PEM + and encoding is not serialization.Encoding.DER + ): + raise ValueError("Must be PEM or DER from the Encoding enum") + + # Text is a meaningless option unless it is accompanied by + # DetachedSignature + if ( + SMIMEOptions.Text in options + and SMIMEOptions.DetachedSignature not in options + ): + raise ValueError( + "When passing the Text option you must also pass " + "DetachedSignature" + ) + + if ( + SMIMEOptions.Text in options + and encoding is serialization.Encoding.DER + ): + raise ValueError( + "The Text option does nothing when serializing to DER" + ) + + # No attributes implies no capabilities so we'll error if you try to + # pass both. + if ( + SMIMEOptions.NoAttributes in options + and SMIMEOptions.NoCapabilities in options + ): + raise ValueError( + "NoAttributes is a superset of NoCapabilities. Do not pass " + "both values." + ) + + backend = _get_backend(backend) + return backend.smime_sign(self, encoding, options) + + +class SMIMEOptions(Enum): + Text = "Add text/plain MIME type" + Binary = "Don't translate input data into canonical MIME format" + DetachedSignature = "Don't embed data in the PKCS7 structure" + NoCapabilities = "Don't embed SMIME capabilities" + NoAttributes = "Don't embed authenticatedAttributes" diff --git a/tests/hazmat/primitives/test_smime.py b/tests/hazmat/primitives/test_smime.py new file mode 100644 index 000000000000..c2ff275e0999 --- /dev/null +++ b/tests/hazmat/primitives/test_smime.py @@ -0,0 +1,518 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import os + +import pytest + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization, smime +from cryptography.hazmat.primitives.asymmetric import ed25519 + +from .utils import load_vectors_from_file + + +# We have no public verification API and won't be adding one until we get +# some requirements from users so this function exists to give us basic +# verification for the signing tests. +def _smime_verify(encoding, sig, msg, certs, options, backend): + sig_bio = backend._bytes_to_bio(sig) + if encoding is serialization.Encoding.DER: + p7 = backend._lib.d2i_PKCS7_bio(sig_bio.bio, backend._ffi.NULL) + else: + p7 = backend._lib.SMIME_read_PKCS7(sig_bio.bio, backend._ffi.NULL) + backend.openssl_assert(p7 != backend._ffi.NULL) + p7 = backend._ffi.gc(p7, backend._lib.PKCS7_free) + flags = 0 + for option in options: + if option is smime.SMIMEOptions.Text: + flags |= backend._lib.PKCS7_TEXT + store = backend._lib.X509_STORE_new() + backend.openssl_assert(store != backend._ffi.NULL) + store = backend._ffi.gc(store, backend._lib.X509_STORE_free) + for cert in certs: + res = backend._lib.X509_STORE_add_cert(store, cert._x509) + backend.openssl_assert(res == 1) + if msg is None: + res = backend._lib.PKCS7_verify( + p7, + backend._ffi.NULL, + store, + backend._ffi.NULL, + backend._ffi.NULL, + flags, + ) + else: + msg_bio = backend._bytes_to_bio(msg) + res = backend._lib.PKCS7_verify( + p7, backend._ffi.NULL, store, msg_bio.bio, backend._ffi.NULL, flags + ) + backend.openssl_assert(res == 1) + + +def _load_cert_key(): + key = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "ca_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read(), None + ), + mode="rb", + ) + cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate(pemfile.read()), + mode="rb", + ) + return cert, key + + +class TestSMIMEBuilder(object): + def test_invalid_data(self): + builder = smime.SMIMESignatureBuilder() + with pytest.raises(TypeError): + builder.set_data(u"not bytes") + + def test_set_data_twice(self): + builder = smime.SMIMESignatureBuilder().set_data(b"test") + with pytest.raises(ValueError): + builder.set_data(b"test") + + def test_sign_no_signer(self): + builder = smime.SMIMESignatureBuilder().set_data(b"test") + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.PEM, []) + + def test_sign_no_data(self): + cert, key = _load_cert_key() + builder = smime.SMIMESignatureBuilder().add_signer( + cert, key, hashes.SHA256() + ) + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.PEM, []) + + def test_unsupported_hash_alg(self): + cert, key = _load_cert_key() + with pytest.raises(TypeError): + smime.SMIMESignatureBuilder().add_signer( + cert, key, hashes.SHA512_256() + ) + + def test_not_a_cert(self): + cert, key = _load_cert_key() + with pytest.raises(TypeError): + smime.SMIMESignatureBuilder().add_signer( + b"notacert", key, hashes.SHA256() + ) + + @pytest.mark.supported( + only_if=lambda backend: backend.ed25519_supported(), + skip_message="Does not support ed25519.", + ) + def test_unsupported_key_type(self, backend): + cert, _ = _load_cert_key() + key = ed25519.Ed25519PrivateKey.generate() + with pytest.raises(TypeError): + smime.SMIMESignatureBuilder().add_signer( + cert, key, hashes.SHA256() + ) + + def test_sign_invalid_options(self): + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.PEM, [b"invalid"]) + + def test_sign_invalid_encoding(self): + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.Raw, []) + + def test_sign_invalid_options_text_no_detached(self): + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + options = [smime.SMIMEOptions.Text] + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.PEM, options) + + def test_sign_invalid_options_text_der_encoding(self): + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + options = [ + smime.SMIMEOptions.Text, + smime.SMIMEOptions.DetachedSignature, + ] + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.DER, options) + + def test_sign_invalid_options_no_attrs_and_no_caps(self): + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + options = [ + smime.SMIMEOptions.NoAttributes, + smime.SMIMEOptions.NoCapabilities, + ] + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.PEM, options) + + def test_smime_sign_detached(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + options = [smime.SMIMEOptions.DetachedSignature] + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig = builder.sign(serialization.Encoding.PEM, options) + sig_binary = builder.sign(serialization.Encoding.DER, options) + # We don't have a generic ASN.1 parser available to us so we instead + # will assert on specific byte sequences being present based on the + # parameters chosen above. + assert b"sha-256" in sig + # Detached signature means that the signed data is *not* embedded into + # the PKCS7 structure itself, but is present in the PEM serialization + # as a separate section before the PKCS7 data. So we should expect to + # have data in sig but not in sig_binary + assert data in sig + _smime_verify( + serialization.Encoding.PEM, sig, data, [cert], options, backend + ) + assert data not in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + data, + [cert], + options, + backend, + ) + + def test_sign_byteslike(self): + data = bytearray(b"hello world") + cert, key = _load_cert_key() + options = [smime.SMIMEOptions.DetachedSignature] + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig = builder.sign(serialization.Encoding.PEM, options) + assert bytes(data) in sig + + @pytest.mark.parametrize( + ("hash_alg", "expected_value"), + [ + (hashes.SHA1(), b"\x06\x05+\x0e\x03\x02\x1a"), + (hashes.SHA256(), b"\x06\t`\x86H\x01e\x03\x04\x02\x01"), + (hashes.SHA384(), b"\x06\t`\x86H\x01e\x03\x04\x02\x02"), + (hashes.SHA512(), b"\x06\t`\x86H\x01e\x03\x04\x02\x03"), + ], + ) + def test_smime_sign_alternate_digests_der( + self, hash_alg, expected_value, backend + ): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hash_alg) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + assert expected_value in sig + _smime_verify( + serialization.Encoding.DER, sig, None, [cert], options, backend + ) + + @pytest.mark.parametrize( + ("hash_alg", "expected_value"), + [ + (hashes.SHA1(), b"sha1"), + (hashes.SHA256(), b"sha-256"), + (hashes.SHA384(), b"sha-384"), + (hashes.SHA512(), b"sha-512"), + ], + ) + def test_smime_sign_alternate_digests_detached_pem( + self, hash_alg, expected_value + ): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hash_alg) + ) + options = [smime.SMIMEOptions.DetachedSignature] + sig = builder.sign(serialization.Encoding.PEM, options) + # When in detached signature mode the hash algorithm is stored as a + # byte string like "sha-384". + assert expected_value in sig + + def test_smime_sign_attached(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + options = [] + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig_binary = builder.sign(serialization.Encoding.DER, options) + # When not passing detached signature the signed data is embedded into + # the PKCS7 structure itself + assert data in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_smime_sign_binary(self, backend): + data = b"hello\nworld" + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + options = [] + sig_no_binary = builder.sign(serialization.Encoding.DER, options) + sig_binary = builder.sign( + serialization.Encoding.DER, [smime.SMIMEOptions.Binary] + ) + # Binary prevents translation of LF to CR+LF (SMIME canonical form) + # so data should not be present in sig_no_binary, but should be present + # in sig_binary + assert data not in sig_no_binary + _smime_verify( + serialization.Encoding.DER, + sig_no_binary, + None, + [cert], + options, + backend, + ) + assert data in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_smime_sign_smime_canonicalization(self, backend): + data = b"hello\nworld" + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [] + sig_binary = builder.sign(serialization.Encoding.DER, options) + # LF gets converted to CR+LF (SMIME canonical form) + # so data should not be present in the sig + assert data not in sig_binary + assert b"hello\r\nworld" in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_smime_sign_text(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [ + smime.SMIMEOptions.Text, + smime.SMIMEOptions.DetachedSignature, + ] + sig_pem = builder.sign(serialization.Encoding.PEM, options) + # The text option adds text/plain headers to the S/MIME message + # These headers are only relevant in PEM mode, not binary, which is + # just the PKCS7 structure itself. + assert b"text/plain" in sig_pem + # When passing the Text option the header is prepended so the actual + # signed data is this. + signed_data = b"Content-Type: text/plain\r\n\r\nhello world" + _smime_verify( + serialization.Encoding.PEM, + sig_pem, + signed_data, + [cert], + options, + backend, + ) + + def test_smime_sign_no_capabilities(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [smime.SMIMEOptions.NoCapabilities] + sig_binary = builder.sign(serialization.Encoding.DER, options) + # NoCapabilities removes the SMIMECapabilities attribute from the + # PKCS7 structure. This is an ASN.1 sequence with the + # OID 1.2.840.113549.1.9.15. It does NOT remove all authenticated + # attributes, so we verify that by looking for the signingTime OID. + + # 1.2.840.113549.1.9.15 SMIMECapabilities as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x0f" not in sig_binary + # 1.2.840.113549.1.9.5 signingTime as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x05" in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_smime_sign_no_attributes(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [smime.SMIMEOptions.NoAttributes] + sig_binary = builder.sign(serialization.Encoding.DER, options) + # NoAttributes removes all authenticated attributes, so we shouldn't + # find SMIMECapabilities or signingTime. + + # 1.2.840.113549.1.9.15 SMIMECapabilities as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x0f" not in sig_binary + # 1.2.840.113549.1.9.5 signingTime as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x05" not in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_multiple_signers(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + rsa_key = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read(), None + ), + mode="rb", + ) + rsa_cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA512()) + .add_signer(rsa_cert, rsa_key, hashes.SHA512()) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + # There should be three SHA512 OIDs in this structure + assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x03") == 3 + _smime_verify( + serialization.Encoding.DER, + sig, + None, + [cert, rsa_cert], + options, + backend, + ) + + def test_multiple_signers_different_hash_algs(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + rsa_key = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read(), None + ), + mode="rb", + ) + rsa_cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + builder = ( + smime.SMIMESignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA384()) + .add_signer(rsa_cert, rsa_key, hashes.SHA512()) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + # There should be two SHA384 and two SHA512 OIDs in this structure + assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x02") == 2 + assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x03") == 2 + _smime_verify( + serialization.Encoding.DER, + sig, + None, + [cert, rsa_cert], + options, + backend, + ) From 0b35c5d701f4873b1a6c4fe31a66c4c59db170aa Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 20 Sep 2020 17:49:09 -0400 Subject: [PATCH 17/99] Remove bindings docs (#5469) --- docs/development/c-bindings.rst | 2 +- docs/hazmat/bindings/index.rst | 22 --------------- docs/hazmat/bindings/openssl.rst | 47 -------------------------------- docs/index.rst | 1 - 4 files changed, 1 insertion(+), 71 deletions(-) delete mode 100644 docs/hazmat/bindings/index.rst delete mode 100644 docs/hazmat/bindings/openssl.rst diff --git a/docs/development/c-bindings.rst b/docs/development/c-bindings.rst index 1b58dab6290f..efc21ca0a8f5 100644 --- a/docs/development/c-bindings.rst +++ b/docs/development/c-bindings.rst @@ -5,7 +5,7 @@ C bindings are bindings to C libraries, using cffi_ whenever possible. .. _cffi: https://cffi.readthedocs.io -Bindings live in :py:mod:`cryptography.hazmat.bindings`. +Bindings live in ``cryptography.hazmat.bindings``. When modifying the bindings you will need to recompile the C extensions to test the changes. This can be accomplished with ``pip install -e .`` in the diff --git a/docs/hazmat/bindings/index.rst b/docs/hazmat/bindings/index.rst deleted file mode 100644 index 655f4620d492..000000000000 --- a/docs/hazmat/bindings/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. hazmat:: - -Bindings -======== - -.. module:: cryptography.hazmat.bindings - -``cryptography`` aims to provide low-level CFFI based bindings to multiple -native C libraries. These provide no automatic initialization of the library -and may not provide complete wrappers for its API. - -Using these functions directly is likely to require you to be careful in -managing memory allocation, locking and other resources. - - -Individual bindings -------------------- - -.. toctree:: - :maxdepth: 1 - - openssl diff --git a/docs/hazmat/bindings/openssl.rst b/docs/hazmat/bindings/openssl.rst deleted file mode 100644 index bc7ec2d916b6..000000000000 --- a/docs/hazmat/bindings/openssl.rst +++ /dev/null @@ -1,47 +0,0 @@ -.. hazmat:: - -OpenSSL binding -=============== - -.. currentmodule:: cryptography.hazmat.bindings.openssl.binding - -These are `CFFI`_ bindings to the `OpenSSL`_ C library. Cryptography supports -OpenSSL version 1.0.2 and greater. - -.. class:: cryptography.hazmat.bindings.openssl.binding.Binding() - - This is the exposed API for the OpenSSL bindings. It has two public - attributes: - - .. attribute:: ffi - - This is a ``cffi.FFI`` instance. It can be used to allocate and - otherwise manipulate OpenSSL structures. - - .. attribute:: lib - - This is a ``cffi`` library. It can be used to call OpenSSL functions, - and access constants. - - .. classmethod:: init_static_locks - - Enables the best available locking callback for OpenSSL. - See :ref:`openssl-threading`. - -.. _openssl-threading: - -Threading ---------- - -``cryptography`` enables OpenSSLs `thread safety facilities`_ in several -different ways depending on the configuration of your system. For users on -OpenSSL 1.1.0 or newer (including anyone who uses a binary wheel) the OpenSSL -internal locking callbacks are automatically used. Otherwise, we first attempt -to use the callbacks provided by your Python implementation specifically for -OpenSSL. This will work in every case except where ``cryptography`` is linked -against a different version of OpenSSL than the one used by your Python -implementation. For this final case we have a C-based locking callback. - -.. _`CFFI`: https://cffi.readthedocs.io -.. _`OpenSSL`: https://www.openssl.org/ -.. _`thread safety facilities`: https://www.openssl.org/docs/man1.0.2/man3/CRYPTO_THREADID_set_callback.html diff --git a/docs/index.rst b/docs/index.rst index 396ed0b6e9d5..ec3913f41d8c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -68,7 +68,6 @@ hazmat layer only when necessary. exceptions random-numbers hazmat/backends/index - hazmat/bindings/index .. toctree:: :maxdepth: 2 From d473130a64ec50b5ac6f79ad4955c6e07d99c6ff Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 20 Sep 2020 22:19:53 -0400 Subject: [PATCH 18/99] bump to latest libressl point release (#5470) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b2d5ba949785..0c399c4171e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,7 @@ matrix: - python: 3.8 env: TOXENV=py38 LIBRESSL=3.1.4 - python: 3.8 - env: TOXENV=py38 LIBRESSL=3.2.0 + env: TOXENV=py38 LIBRESSL=3.2.1 - python: 2.7 services: docker From e11ed9bfa96d327f63392e4b9db4c173a751aeb8 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 22 Sep 2020 11:03:01 -0500 Subject: [PATCH 19/99] test against 1.1.1h (#5474) --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0c399c4171e9..51cb8841f43f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,13 +40,13 @@ matrix: - python: 3.8 env: TOXENV=py38 OPENSSL=1.1.0l - python: 2.7 - env: TOXENV=py27 OPENSSL=1.1.1g + env: TOXENV=py27 OPENSSL=1.1.1h - python: 3.8 - env: TOXENV=py38 OPENSSL=1.1.1g + env: TOXENV=py38 OPENSSL=1.1.1h - python: 3.8 - env: TOXENV=py38 OPENSSL=1.1.1g OPENSSL_CONFIG_FLAGS="no-engine no-rc2 no-srtp no-ct" + env: TOXENV=py38 OPENSSL=1.1.1h OPENSSL_CONFIG_FLAGS="no-engine no-rc2 no-srtp no-ct" - python: 3.8 - env: TOXENV=py38-ssh OPENSSL=1.1.1g + env: TOXENV=py38-ssh OPENSSL=1.1.1h - python: 3.8 env: TOXENV=py38 LIBRESSL=2.9.2 - python: 3.8 @@ -106,7 +106,7 @@ matrix: env: TOXENV=py38 DOCKER=pyca/cryptography-runner-alpine:latest - python: 3.8 - env: TOXENV=docs OPENSSL=1.1.1g + env: TOXENV=docs OPENSSL=1.1.1h addons: apt: packages: @@ -119,7 +119,7 @@ matrix: - python: 3.8 env: DOWNSTREAM=pyopenssl - python: 3.7 - env: DOWNSTREAM=twisted OPENSSL=1.1.1g + env: DOWNSTREAM=twisted OPENSSL=1.1.1h - python: 3.7 env: DOWNSTREAM=paramiko - python: 3.7 From 3268a91c64bbbbea67c919ca59520df97518db6e Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 22 Sep 2020 14:05:02 -0400 Subject: [PATCH 20/99] Forward-port 3.1.1 changelog (#5476) --- CHANGELOG.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1c8925803e22..0a4b5ad4519b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,6 +13,14 @@ Changelog * Added basic support for SMIME signing via :class:`~cryptography.hazmat.primitives.smime.SMIMESignatureBuilder`. +.. _v3-1-1: + +3.1.1 - 2020-09-22 +~~~~~~~~~~~~~~~~~~ + +* Updated Windows, macOS, and ``manylinux`` wheels to be compiled with + OpenSSL 1.1.1h. + .. _v3-1: 3.1 - 2020-08-26 From 9daa295d83ea6d9809c0da830f71068b1b32cd8c Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 2 Oct 2020 11:05:33 -0400 Subject: [PATCH 21/99] Update windows CI for new GHA syntax (#5477) * Update windows CI for new GHA syntax * Update ci.yml * Update ci.yml * Update ci.yml * Update ci.yml --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afac37aaf956..0450c1a813ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,11 +85,12 @@ jobs: - name: Download OpenSSL run: | python .github/workflows/download_openssl.py windows openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }} - echo "::set-env name=INCLUDE::C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/include;%INCLUDE%" - echo "::set-env name=LIB::C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/lib;%LIB%" - echo "::set-env name=CL::${{ matrix.PYTHON.CL_FLAGS }}" + echo "INCLUDE=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/include;$INCLUDE" >> $GITHUB_ENV + echo "LIB=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/lib;$LIB" >> $GITHUB_ENV + echo "CL=${{ matrix.PYTHON.CL_FLAGS }}" >> $GITHUB_ENV env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash - run: git clone https://github.com/google/wycheproof - run: tox -r -- --color=yes --wycheproof-root=wycheproof From cd6f6b02961239ac3c1290abf8dab03b825a98e5 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 2 Oct 2020 23:47:49 -0400 Subject: [PATCH 22/99] Update wheel builder for new GHA syntax (#5478) * Update wheel builder for new GHA syntax * Update wheel-builder.yml * Update wheel-builder.yml --- .github/workflows/wheel-builder.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wheel-builder.yml b/.github/workflows/wheel-builder.yml index d8ad49b595b9..ba15dd656a93 100644 --- a/.github/workflows/wheel-builder.yml +++ b/.github/workflows/wheel-builder.yml @@ -139,10 +139,11 @@ jobs: - name: Download OpenSSL run: | python .github/workflows/download_openssl.py windows openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }} - echo "::set-env name=INCLUDE::C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/include;%INCLUDE%" - echo "::set-env name=LIB::C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/lib;%LIB%" + echo "INCLUDE=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/include;$INCLUDE" >> $GITHUB_ENV + echo "LIB=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/lib;$LIB" >> $GITHUB_ENV env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash - run: python -m pip install -U pip wheel cffi six ipaddress "enum34; python_version < '3'" - run: pip download cryptography==${{ github.event.inputs.version }} --no-deps --no-binary cryptography && tar zxvf cryptography*.tar.gz && mkdir wheelhouse From e74895bffc5590639132a49d9905ef6bff1b0125 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 7 Oct 2020 09:26:05 -0400 Subject: [PATCH 23/99] Update CI for 3.9 release (#5480) --- .github/workflows/ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0450c1a813ab..717504a61229 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,8 +17,7 @@ jobs: PYTHON: - {VERSION: "2.7", TOXENV: "py27", EXTRA_CFLAGS: ""} - {VERSION: "3.5", TOXENV: "py35", EXTRA_CFLAGS: ""} - - {VERSION: "3.8", TOXENV: "py38", EXTRA_CFLAGS: "-DUSE_OSRANDOM_RNG_FOR_TESTING"} - - {VERSION: "3.9.0-rc.1", TOXENV: "py39"} + - {VERSION: "3.9", TOXENV: "py39", EXTRA_CFLAGS: "-DUSE_OSRANDOM_RNG_FOR_TESTING"} name: "Python ${{ matrix.PYTHON.VERSION }} on macOS" steps: - uses: actions/checkout@master @@ -63,8 +62,8 @@ jobs: - {VERSION: "3.5", TOXENV: "py35", MSVC_VERSION: "2019", CL_FLAGS: ""} - {VERSION: "3.6", TOXENV: "py36", MSVC_VERSION: "2019", CL_FLAGS: ""} - {VERSION: "3.7", TOXENV: "py37", MSVC_VERSION: "2019", CL_FLAGS: ""} - - {VERSION: "3.8", TOXENV: "py38", MSVC_VERSION: "2019", CL_FLAGS: "/D USE_OSRANDOM_RNG_FOR_TESTING"} - - {VERSION: "3.9.0-rc.1", TOXENV: "py39", MSVC_VERSION: "2019", CL_FLAGS: ""} + - {VERSION: "3.8", TOXENV: "py38", MSVC_VERSION: "2019", CL_FLAGS: ""} + - {VERSION: "3.9", TOXENV: "py39", MSVC_VERSION: "2019", CL_FLAGS: "/D USE_OSRANDOM_RNG_FOR_TESTING"} name: "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}" steps: - uses: actions/checkout@master From 27026d37ffde697f76f84c8eb716ff2b8b983110 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 17 Oct 2020 19:49:36 -0400 Subject: [PATCH 24/99] fixed twisted tests (#5489) --- .travis/downstream.d/twisted.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/downstream.d/twisted.sh b/.travis/downstream.d/twisted.sh index 3d45413bbe21..522e763ec3b7 100755 --- a/.travis/downstream.d/twisted.sh +++ b/.travis/downstream.d/twisted.sh @@ -5,7 +5,7 @@ case "${1}" in git clone --depth=1 https://github.com/twisted/twisted cd twisted git rev-parse HEAD - pip install ".[tls,conch,http2]" + pip install ".[all_non_platform]" ;; run) cd twisted From ca622468f7665ddda00b7358d0ba0652beba7a89 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 19 Oct 2020 10:28:09 -0400 Subject: [PATCH 25/99] bumped to latest libressl (#5491) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 51cb8841f43f..29349ed87109 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,7 @@ matrix: - python: 3.8 env: TOXENV=py38 LIBRESSL=3.1.4 - python: 3.8 - env: TOXENV=py38 LIBRESSL=3.2.1 + env: TOXENV=py38 LIBRESSL=3.2.2 - python: 2.7 services: docker From 5edf5b828a8ebac030bf8c6f27ec8bf0d008885c Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 24 Oct 2020 17:10:25 -0700 Subject: [PATCH 26/99] migrate smime builder to pkcs7 module and rename (#5496) * migrate smime builder to pkcs7 module and rename * missed a rename --- CHANGELOG.rst | 4 +- .../primitives/asymmetric/serialization.rst | 124 +++++ docs/hazmat/primitives/index.rst | 1 - docs/hazmat/primitives/smime.rst | 128 ----- .../hazmat/backends/openssl/backend.py | 16 +- .../hazmat/primitives/serialization/pkcs7.py | 102 ++++ src/cryptography/hazmat/primitives/smime.py | 109 ---- tests/hazmat/primitives/test_pkcs7.py | 505 +++++++++++++++++ tests/hazmat/primitives/test_smime.py | 518 ------------------ 9 files changed, 741 insertions(+), 766 deletions(-) delete mode 100644 docs/hazmat/primitives/smime.rst delete mode 100644 src/cryptography/hazmat/primitives/smime.py delete mode 100644 tests/hazmat/primitives/test_smime.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0a4b5ad4519b..32a6c8522547 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,8 +10,8 @@ Changelog * Support for OpenSSL 1.0.2 has been removed. Users on older version of OpenSSL will need to upgrade. -* Added basic support for SMIME signing via - :class:`~cryptography.hazmat.primitives.smime.SMIMESignatureBuilder`. +* Added basic support for PKCS7 signing (including SMIME) via + :class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7SignatureBuilder`. .. _v3-1-1: diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index 56b2c696ccaa..f3dca471a7f2 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -574,6 +574,130 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``, :raises cryptography.exceptions.UnsupportedAlgorithm: If the PKCS7 data is of a type that is not supported. +.. testsetup:: + + ca_key = b""" + -----BEGIN PRIVATE KEY----- + MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgA8Zqz5vLeR0ePZUe + jBfdyMmnnI4U5uAJApWTsMn/RuWhRANCAAQY/8+7+Tm49d3D7sBAiwZ1BqtPzdgs + UiROH+AQRme1XxW5Yr07zwxvvhr3tKEPtLnLboazUPlsUb/Bgte+xfkF + -----END PRIVATE KEY----- + """.strip() + + ca_cert = b""" + -----BEGIN CERTIFICATE----- + MIIBUTCB96ADAgECAgIDCTAKBggqhkjOPQQDAjAnMQswCQYDVQQGEwJVUzEYMBYG + A1UEAwwPY3J5cHRvZ3JhcGh5IENBMB4XDTE3MDEwMTEyMDEwMFoXDTM4MTIzMTA4 + MzAwMFowJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBDQTBZ + MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBj/z7v5Obj13cPuwECLBnUGq0/N2CxS + JE4f4BBGZ7VfFblivTvPDG++Gve0oQ+0uctuhrNQ+WxRv8GC177F+QWjEzARMA8G + A1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhANES742XWm64tkGnz8Dn + pG6u2lHkZFQr3oaVvPcemvlbAiEA0WGGzmYx5C9UvfXIK7NEziT4pQtyESE0uRVK + Xw4nMqk= + -----END CERTIFICATE----- + """.strip() + + +.. class:: PKCS7SignatureBuilder + + The PKCS7 signature builder can create both basic PKCS7 signed messages as + well as S/MIME messages, which are commonly used in email. S/MIME has + multiple versions, but this implements a subset of :rfc:`2632`, also known + as S/MIME Version 3. + + .. versionadded:: 3.2 + + .. doctest:: + + >>> from cryptography import x509 + >>> from cryptography.hazmat.primitives import hashes, serialization + >>> from cryptography.hazmat.primitives.serialization import pkcs7 + >>> cert = x509.load_pem_x509_certificate(ca_cert) + >>> key = serialization.load_pem_private_key(ca_key, None) + >>> options = [pkcs7.PKCS7Options.DetachedSignature] + >>> pkcs7.PKCS7SignatureBuilder().set_data( + ... b"data to sign" + ... ).add_signer( + ... cert, key, hashes.SHA256() + ... ).sign( + ... serialization.Encoding.PEM, options + ... ) + b'...' + + .. method:: set_data(data) + + :param data: The data to be hashed and signed. + :type data: :term:`bytes-like` + + .. method:: add_signer(certificate, private_key, hash_algorithm) + + :param certificate: The :class:`~cryptography.x509.Certificate`. + + :param private_key: The + :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` or + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` + associated with the certificate provided. + + :param hash_algorithm: The + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` that + will be used to generate the signature. This must be an instance of + :class:`~cryptography.hazmat.primitives.hashes.SHA1`, + :class:`~cryptography.hazmat.primitives.hashes.SHA224`, + :class:`~cryptography.hazmat.primitives.hashes.SHA256`, + :class:`~cryptography.hazmat.primitives.hashes.SHA384`, or + :class:`~cryptography.hazmat.primitives.hashes.SHA512`. + + .. method:: sign(encoding, options, backend=None) + + :param encoding: :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` + or :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`. + + :param options: A list of + :class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. + + :return bytes: The signed PKCS7 message. + + :param backend: An optional backend. + + +.. class:: PKCS7Options + + .. versionadded:: 3.2 + + An enumeration of options for PKCS7 signature creation. + + .. attribute:: Text + + The text option adds ``text/plain`` headers to an S/MIME message when + serializing to + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`. + This option is disallowed with ``DER`` serialization. + + .. attribute:: Binary + + S/MIME signing normally converts line endings (LF to CRLF). When + passing this option the data will not be converted. + + .. attribute:: DetachedSignature + + Don't embed the signed data within the ASN.1. When signing with + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` this + also results in the data being added as clear text before the + PEM encoded structure. + + .. attribute:: NoCapabilities + + PKCS7 structures contain a ``MIMECapabilities`` section inside the + ``authenticatedAttributes``. Passing this as an option removes + ``MIMECapabilities``. + + .. attribute:: NoAttributes + + PKCS7 structures contain an ``authenticatedAttributes`` section. + Passing this as an option removes that section. Note that if you + pass ``NoAttributes`` you can't pass ``NoCapabilities`` since + ``NoAttributes`` removes ``MIMECapabilities`` and more. + Serialization Formats ~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/hazmat/primitives/index.rst b/docs/hazmat/primitives/index.rst index bbe4418e7c18..72e5b26ce33d 100644 --- a/docs/hazmat/primitives/index.rst +++ b/docs/hazmat/primitives/index.rst @@ -14,6 +14,5 @@ Primitives mac/index cryptographic-hashes symmetric-encryption - smime padding twofactor diff --git a/docs/hazmat/primitives/smime.rst b/docs/hazmat/primitives/smime.rst deleted file mode 100644 index 556dd9b55bad..000000000000 --- a/docs/hazmat/primitives/smime.rst +++ /dev/null @@ -1,128 +0,0 @@ -.. hazmat:: - -S/MIME -====== - -.. module:: cryptography.hazmat.primitives.smime - -.. testsetup:: - - ca_key = b""" - -----BEGIN PRIVATE KEY----- - MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgA8Zqz5vLeR0ePZUe - jBfdyMmnnI4U5uAJApWTsMn/RuWhRANCAAQY/8+7+Tm49d3D7sBAiwZ1BqtPzdgs - UiROH+AQRme1XxW5Yr07zwxvvhr3tKEPtLnLboazUPlsUb/Bgte+xfkF - -----END PRIVATE KEY----- - """.strip() - - ca_cert = b""" - -----BEGIN CERTIFICATE----- - MIIBUTCB96ADAgECAgIDCTAKBggqhkjOPQQDAjAnMQswCQYDVQQGEwJVUzEYMBYG - A1UEAwwPY3J5cHRvZ3JhcGh5IENBMB4XDTE3MDEwMTEyMDEwMFoXDTM4MTIzMTA4 - MzAwMFowJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBDQTBZ - MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBj/z7v5Obj13cPuwECLBnUGq0/N2CxS - JE4f4BBGZ7VfFblivTvPDG++Gve0oQ+0uctuhrNQ+WxRv8GC177F+QWjEzARMA8G - A1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhANES742XWm64tkGnz8Dn - pG6u2lHkZFQr3oaVvPcemvlbAiEA0WGGzmYx5C9UvfXIK7NEziT4pQtyESE0uRVK - Xw4nMqk= - -----END CERTIFICATE----- - """.strip() - - -S/MIME provides a method to send and receive signed MIME messages. It is -commonly used in email. S/MIME has multiple versions, but this -module implements a subset of :rfc:`2632`, also known as S/MIME Version 3. - - -.. class:: SMIMESignatureBuilder - - .. versionadded:: 3.2 - - .. doctest:: - - >>> from cryptography.hazmat.primitives import hashes, serialization, smime - >>> from cryptography import x509 - >>> cert = x509.load_pem_x509_certificate(ca_cert) - >>> key = serialization.load_pem_private_key(ca_key, None) - >>> options = [smime.SMIMEOptions.DetachedSignature] - >>> smime.SMIMESignatureBuilder().set_data( - ... b"data to sign" - ... ).add_signer( - ... cert, key, hashes.SHA256() - ... ).sign( - ... serialization.Encoding.PEM, options - ... ) - b'...' - - .. method:: set_data(data) - - :param data: The data to be hashed and signed. - :type data: :term:`bytes-like` - - .. method:: add_signer(certificate, private_key, hash_algorithm) - - :param certificate: The :class:`~cryptography.x509.Certificate`. - - :param private_key: The - :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` or - :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` - associated with the certificate provided. - - :param hash_algorithm: The - :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` that - will be used to generate the signature. This must be an instance of - :class:`~cryptography.hazmat.primitives.hashes.SHA1`, - :class:`~cryptography.hazmat.primitives.hashes.SHA224`, - :class:`~cryptography.hazmat.primitives.hashes.SHA256`, - :class:`~cryptography.hazmat.primitives.hashes.SHA384`, or - :class:`~cryptography.hazmat.primitives.hashes.SHA512`. - - .. method:: sign(encoding, options, backend=None) - - :param encoding: :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` - or :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`. - - :param options: A list of :class:`~cryptography.hazmat.primitives.smime.SMIMEOptions`. - - :return bytes: The signed S/MIME message. - - :param backend: An optional backend. - - -.. class:: SMIMEOptions - - .. versionadded:: 3.2 - - An enumeration of options for S/MIME signature creation. - - .. attribute:: Text - - The text option adds ``text/plain`` headers to the S/MIME message when - serializing to - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`. - This option is disallowed with ``DER`` serialization. - - .. attribute:: Binary - - S/MIME signing normally converts line endings (LF to CRLF). When - passing this option the data will not be converted. - - .. attribute:: DetachedSignature - - Don't embed the signed data within the ASN.1. When signing with - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` this - also results in the data being added as clear text before the - PEM encoded structure. - - .. attribute:: NoCapabilities - - S/MIME structures contain a ``MIMECapabilities`` section inside the - ``authenticatedAttributes``. Passing this as an option removes - ``MIMECapabilities``. - - .. attribute:: NoAttributes - - S/MIME structures contain an ``authenticatedAttributes`` section. - Passing this as an option removes that section. Note that if you - pass ``NoAttributes`` you can't pass ``NoCapabilities`` since - ``NoAttributes`` removes ``MIMECapabilities`` and more. diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index f7d6a47c7a9c..3fd87ac5b1e1 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -115,7 +115,7 @@ _RevokedCertificate, ) from cryptography.hazmat.bindings.openssl import binding -from cryptography.hazmat.primitives import hashes, serialization, smime +from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ( dsa, ec, @@ -151,7 +151,7 @@ XTS, ) from cryptography.hazmat.primitives.kdf import scrypt -from cryptography.hazmat.primitives.serialization import ssh +from cryptography.hazmat.primitives.serialization import pkcs7, ssh from cryptography.x509 import ocsp @@ -2690,12 +2690,12 @@ def _load_pkcs7_certificates(self, p7): return certs - def smime_sign(self, builder, encoding, options): + def pkcs7_sign(self, builder, encoding, options): bio = self._bytes_to_bio(builder._data) init_flags = self._lib.PKCS7_PARTIAL final_flags = 0 - if smime.SMIMEOptions.DetachedSignature in options: + if pkcs7.PKCS7Options.DetachedSignature in options: # Don't embed the data in the PKCS7 structure init_flags |= self._lib.PKCS7_DETACHED final_flags |= self._lib.PKCS7_DETACHED @@ -2715,9 +2715,9 @@ def smime_sign(self, builder, encoding, options): # These flags are configurable on a per-signature basis # but we've deliberately chosen to make the API only allow # setting it across all signatures for now. - if smime.SMIMEOptions.NoCapabilities in options: + if pkcs7.PKCS7Options.NoCapabilities in options: signer_flags |= self._lib.PKCS7_NOSMIMECAP - elif smime.SMIMEOptions.NoAttributes in options: + elif pkcs7.PKCS7Options.NoAttributes in options: signer_flags |= self._lib.PKCS7_NOATTR for certificate, private_key, hash_algorithm in builder._signers: md = self._evp_md_non_null_from_algorithm(hash_algorithm) @@ -2729,9 +2729,9 @@ def smime_sign(self, builder, encoding, options): for option in options: # DetachedSignature, NoCapabilities, and NoAttributes are already # handled so we just need to check these last two options. - if option is smime.SMIMEOptions.Text: + if option is pkcs7.PKCS7Options.Text: final_flags |= self._lib.PKCS7_TEXT - elif option is smime.SMIMEOptions.Binary: + elif option is pkcs7.PKCS7Options.Binary: final_flags |= self._lib.PKCS7_BINARY bio_out = self._create_mem_bio_gc() diff --git a/src/cryptography/hazmat/primitives/serialization/pkcs7.py b/src/cryptography/hazmat/primitives/serialization/pkcs7.py index fcdd1c9aa3c8..658f7e5d2d18 100644 --- a/src/cryptography/hazmat/primitives/serialization/pkcs7.py +++ b/src/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -4,7 +4,13 @@ from __future__ import absolute_import, division, print_function +from enum import Enum + +from cryptography import x509 from cryptography.hazmat.backends import _get_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, rsa +from cryptography.utils import _check_byteslike def load_pem_pkcs7_certificates(data): @@ -15,3 +21,99 @@ def load_pem_pkcs7_certificates(data): def load_der_pkcs7_certificates(data): backend = _get_backend(None) return backend.load_der_pkcs7_certificates(data) + + +class PKCS7SignatureBuilder(object): + def __init__(self, data=None, signers=[]): + self._data = data + self._signers = signers + + def set_data(self, data): + _check_byteslike("data", data) + if self._data is not None: + raise ValueError("data may only be set once") + + return PKCS7SignatureBuilder(data, self._signers) + + def add_signer(self, certificate, private_key, hash_algorithm): + if not isinstance( + hash_algorithm, + ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + ), + ): + raise TypeError( + "hash_algorithm must be one of hashes.SHA1, SHA224, " + "SHA256, SHA384, or SHA512" + ) + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + if not isinstance( + private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) + ): + raise TypeError("Only RSA & EC keys are supported at this time.") + + return PKCS7SignatureBuilder( + self._data, + self._signers + [(certificate, private_key, hash_algorithm)], + ) + + def sign(self, encoding, options, backend=None): + if len(self._signers) == 0: + raise ValueError("Must have at least one signer") + if self._data is None: + raise ValueError("You must add data to sign") + options = list(options) + if not all(isinstance(x, PKCS7Options) for x in options): + raise ValueError("options must be from the PKCS7Options enum") + if ( + encoding is not serialization.Encoding.PEM + and encoding is not serialization.Encoding.DER + ): + raise ValueError("Must be PEM or DER from the Encoding enum") + + # Text is a meaningless option unless it is accompanied by + # DetachedSignature + if ( + PKCS7Options.Text in options + and PKCS7Options.DetachedSignature not in options + ): + raise ValueError( + "When passing the Text option you must also pass " + "DetachedSignature" + ) + + if ( + PKCS7Options.Text in options + and encoding is serialization.Encoding.DER + ): + raise ValueError( + "The Text option does nothing when serializing to DER" + ) + + # No attributes implies no capabilities so we'll error if you try to + # pass both. + if ( + PKCS7Options.NoAttributes in options + and PKCS7Options.NoCapabilities in options + ): + raise ValueError( + "NoAttributes is a superset of NoCapabilities. Do not pass " + "both values." + ) + + backend = _get_backend(backend) + return backend.pkcs7_sign(self, encoding, options) + + +class PKCS7Options(Enum): + Text = "Add text/plain MIME type" + Binary = "Don't translate input data into canonical MIME format" + DetachedSignature = "Don't embed data in the PKCS7 structure" + NoCapabilities = "Don't embed SMIME capabilities" + NoAttributes = "Don't embed authenticatedAttributes" diff --git a/src/cryptography/hazmat/primitives/smime.py b/src/cryptography/hazmat/primitives/smime.py deleted file mode 100644 index 538ba6a00012..000000000000 --- a/src/cryptography/hazmat/primitives/smime.py +++ /dev/null @@ -1,109 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import absolute_import, division, print_function - -from enum import Enum - -from cryptography import x509 -from cryptography.hazmat.backends import _get_backend -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import ec, rsa -from cryptography.utils import _check_byteslike - - -class SMIMESignatureBuilder(object): - def __init__(self, data=None, signers=[]): - self._data = data - self._signers = signers - - def set_data(self, data): - _check_byteslike("data", data) - if self._data is not None: - raise ValueError("data may only be set once") - - return SMIMESignatureBuilder(data, self._signers) - - def add_signer(self, certificate, private_key, hash_algorithm): - if not isinstance( - hash_algorithm, - ( - hashes.SHA1, - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - ), - ): - raise TypeError( - "hash_algorithm must be one of hashes.SHA1, SHA224, " - "SHA256, SHA384, or SHA512" - ) - if not isinstance(certificate, x509.Certificate): - raise TypeError("certificate must be a x509.Certificate") - - if not isinstance( - private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) - ): - raise TypeError("Only RSA & EC keys are supported at this time.") - - return SMIMESignatureBuilder( - self._data, - self._signers + [(certificate, private_key, hash_algorithm)], - ) - - def sign(self, encoding, options, backend=None): - if len(self._signers) == 0: - raise ValueError("Must have at least one signer") - if self._data is None: - raise ValueError("You must add data to sign") - options = list(options) - if not all(isinstance(x, SMIMEOptions) for x in options): - raise ValueError("options must be from the SMIMEOptions enum") - if ( - encoding is not serialization.Encoding.PEM - and encoding is not serialization.Encoding.DER - ): - raise ValueError("Must be PEM or DER from the Encoding enum") - - # Text is a meaningless option unless it is accompanied by - # DetachedSignature - if ( - SMIMEOptions.Text in options - and SMIMEOptions.DetachedSignature not in options - ): - raise ValueError( - "When passing the Text option you must also pass " - "DetachedSignature" - ) - - if ( - SMIMEOptions.Text in options - and encoding is serialization.Encoding.DER - ): - raise ValueError( - "The Text option does nothing when serializing to DER" - ) - - # No attributes implies no capabilities so we'll error if you try to - # pass both. - if ( - SMIMEOptions.NoAttributes in options - and SMIMEOptions.NoCapabilities in options - ): - raise ValueError( - "NoAttributes is a superset of NoCapabilities. Do not pass " - "both values." - ) - - backend = _get_backend(backend) - return backend.smime_sign(self, encoding, options) - - -class SMIMEOptions(Enum): - Text = "Add text/plain MIME type" - Binary = "Don't translate input data into canonical MIME format" - DetachedSignature = "Don't embed data in the PKCS7 structure" - NoCapabilities = "Don't embed SMIME capabilities" - NoAttributes = "Don't embed authenticatedAttributes" diff --git a/tests/hazmat/primitives/test_pkcs7.py b/tests/hazmat/primitives/test_pkcs7.py index b7afe7512ac2..2a87451d51cc 100644 --- a/tests/hazmat/primitives/test_pkcs7.py +++ b/tests/hazmat/primitives/test_pkcs7.py @@ -10,6 +10,8 @@ from cryptography import x509 from cryptography.exceptions import _Reasons +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ed25519 from cryptography.hazmat.primitives.serialization import pkcs7 from .utils import load_vectors_from_file @@ -77,3 +79,506 @@ def test_load_pkcs7_unsupported_type(self): ), mode="rb", ) + + +# We have no public verification API and won't be adding one until we get +# some requirements from users so this function exists to give us basic +# verification for the signing tests. +def _smime_verify(encoding, sig, msg, certs, options, backend): + sig_bio = backend._bytes_to_bio(sig) + if encoding is serialization.Encoding.DER: + p7 = backend._lib.d2i_PKCS7_bio(sig_bio.bio, backend._ffi.NULL) + else: + p7 = backend._lib.SMIME_read_PKCS7(sig_bio.bio, backend._ffi.NULL) + backend.openssl_assert(p7 != backend._ffi.NULL) + p7 = backend._ffi.gc(p7, backend._lib.PKCS7_free) + flags = 0 + for option in options: + if option is pkcs7.PKCS7Options.Text: + flags |= backend._lib.PKCS7_TEXT + store = backend._lib.X509_STORE_new() + backend.openssl_assert(store != backend._ffi.NULL) + store = backend._ffi.gc(store, backend._lib.X509_STORE_free) + for cert in certs: + res = backend._lib.X509_STORE_add_cert(store, cert._x509) + backend.openssl_assert(res == 1) + if msg is None: + res = backend._lib.PKCS7_verify( + p7, + backend._ffi.NULL, + store, + backend._ffi.NULL, + backend._ffi.NULL, + flags, + ) + else: + msg_bio = backend._bytes_to_bio(msg) + res = backend._lib.PKCS7_verify( + p7, backend._ffi.NULL, store, msg_bio.bio, backend._ffi.NULL, flags + ) + backend.openssl_assert(res == 1) + + +def _load_cert_key(): + key = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "ca_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read(), None + ), + mode="rb", + ) + cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate(pemfile.read()), + mode="rb", + ) + return cert, key + + +class TestPKCS7Builder(object): + def test_invalid_data(self): + builder = pkcs7.PKCS7SignatureBuilder() + with pytest.raises(TypeError): + builder.set_data(u"not bytes") + + def test_set_data_twice(self): + builder = pkcs7.PKCS7SignatureBuilder().set_data(b"test") + with pytest.raises(ValueError): + builder.set_data(b"test") + + def test_sign_no_signer(self): + builder = pkcs7.PKCS7SignatureBuilder().set_data(b"test") + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.PEM, []) + + def test_sign_no_data(self): + cert, key = _load_cert_key() + builder = pkcs7.PKCS7SignatureBuilder().add_signer( + cert, key, hashes.SHA256() + ) + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.PEM, []) + + def test_unsupported_hash_alg(self): + cert, key = _load_cert_key() + with pytest.raises(TypeError): + pkcs7.PKCS7SignatureBuilder().add_signer( + cert, key, hashes.SHA512_256() + ) + + def test_not_a_cert(self): + cert, key = _load_cert_key() + with pytest.raises(TypeError): + pkcs7.PKCS7SignatureBuilder().add_signer( + b"notacert", key, hashes.SHA256() + ) + + @pytest.mark.supported( + only_if=lambda backend: backend.ed25519_supported(), + skip_message="Does not support ed25519.", + ) + def test_unsupported_key_type(self, backend): + cert, _ = _load_cert_key() + key = ed25519.Ed25519PrivateKey.generate() + with pytest.raises(TypeError): + pkcs7.PKCS7SignatureBuilder().add_signer( + cert, key, hashes.SHA256() + ) + + def test_sign_invalid_options(self): + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.PEM, [b"invalid"]) + + def test_sign_invalid_encoding(self): + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.Raw, []) + + def test_sign_invalid_options_text_no_detached(self): + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + options = [pkcs7.PKCS7Options.Text] + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.PEM, options) + + def test_sign_invalid_options_text_der_encoding(self): + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + options = [ + pkcs7.PKCS7Options.Text, + pkcs7.PKCS7Options.DetachedSignature, + ] + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.DER, options) + + def test_sign_invalid_options_no_attrs_and_no_caps(self): + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + options = [ + pkcs7.PKCS7Options.NoAttributes, + pkcs7.PKCS7Options.NoCapabilities, + ] + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.PEM, options) + + def test_smime_sign_detached(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + options = [pkcs7.PKCS7Options.DetachedSignature] + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig = builder.sign(serialization.Encoding.PEM, options) + sig_binary = builder.sign(serialization.Encoding.DER, options) + # We don't have a generic ASN.1 parser available to us so we instead + # will assert on specific byte sequences being present based on the + # parameters chosen above. + assert b"sha-256" in sig + # Detached signature means that the signed data is *not* embedded into + # the PKCS7 structure itself, but is present in the PEM serialization + # as a separate section before the PKCS7 data. So we should expect to + # have data in sig but not in sig_binary + assert data in sig + _smime_verify( + serialization.Encoding.PEM, sig, data, [cert], options, backend + ) + assert data not in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + data, + [cert], + options, + backend, + ) + + def test_sign_byteslike(self): + data = bytearray(b"hello world") + cert, key = _load_cert_key() + options = [pkcs7.PKCS7Options.DetachedSignature] + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig = builder.sign(serialization.Encoding.PEM, options) + assert bytes(data) in sig + + @pytest.mark.parametrize( + ("hash_alg", "expected_value"), + [ + (hashes.SHA1(), b"\x06\x05+\x0e\x03\x02\x1a"), + (hashes.SHA256(), b"\x06\t`\x86H\x01e\x03\x04\x02\x01"), + (hashes.SHA384(), b"\x06\t`\x86H\x01e\x03\x04\x02\x02"), + (hashes.SHA512(), b"\x06\t`\x86H\x01e\x03\x04\x02\x03"), + ], + ) + def test_smime_sign_alternate_digests_der( + self, hash_alg, expected_value, backend + ): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hash_alg) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + assert expected_value in sig + _smime_verify( + serialization.Encoding.DER, sig, None, [cert], options, backend + ) + + @pytest.mark.parametrize( + ("hash_alg", "expected_value"), + [ + (hashes.SHA1(), b"sha1"), + (hashes.SHA256(), b"sha-256"), + (hashes.SHA384(), b"sha-384"), + (hashes.SHA512(), b"sha-512"), + ], + ) + def test_smime_sign_alternate_digests_detached_pem( + self, hash_alg, expected_value + ): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hash_alg) + ) + options = [pkcs7.PKCS7Options.DetachedSignature] + sig = builder.sign(serialization.Encoding.PEM, options) + # When in detached signature mode the hash algorithm is stored as a + # byte string like "sha-384". + assert expected_value in sig + + def test_smime_sign_attached(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + options = [] + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig_binary = builder.sign(serialization.Encoding.DER, options) + # When not passing detached signature the signed data is embedded into + # the PKCS7 structure itself + assert data in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_smime_sign_binary(self, backend): + data = b"hello\nworld" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + options = [] + sig_no_binary = builder.sign(serialization.Encoding.DER, options) + sig_binary = builder.sign( + serialization.Encoding.DER, [pkcs7.PKCS7Options.Binary] + ) + # Binary prevents translation of LF to CR+LF (SMIME canonical form) + # so data should not be present in sig_no_binary, but should be present + # in sig_binary + assert data not in sig_no_binary + _smime_verify( + serialization.Encoding.DER, + sig_no_binary, + None, + [cert], + options, + backend, + ) + assert data in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_smime_sign_smime_canonicalization(self, backend): + data = b"hello\nworld" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [] + sig_binary = builder.sign(serialization.Encoding.DER, options) + # LF gets converted to CR+LF (SMIME canonical form) + # so data should not be present in the sig + assert data not in sig_binary + assert b"hello\r\nworld" in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_smime_sign_text(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [ + pkcs7.PKCS7Options.Text, + pkcs7.PKCS7Options.DetachedSignature, + ] + sig_pem = builder.sign(serialization.Encoding.PEM, options) + # The text option adds text/plain headers to the S/MIME message + # These headers are only relevant in PEM mode, not binary, which is + # just the PKCS7 structure itself. + assert b"text/plain" in sig_pem + # When passing the Text option the header is prepended so the actual + # signed data is this. + signed_data = b"Content-Type: text/plain\r\n\r\nhello world" + _smime_verify( + serialization.Encoding.PEM, + sig_pem, + signed_data, + [cert], + options, + backend, + ) + + def test_smime_sign_no_capabilities(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [pkcs7.PKCS7Options.NoCapabilities] + sig_binary = builder.sign(serialization.Encoding.DER, options) + # NoCapabilities removes the SMIMECapabilities attribute from the + # PKCS7 structure. This is an ASN.1 sequence with the + # OID 1.2.840.113549.1.9.15. It does NOT remove all authenticated + # attributes, so we verify that by looking for the signingTime OID. + + # 1.2.840.113549.1.9.15 SMIMECapabilities as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x0f" not in sig_binary + # 1.2.840.113549.1.9.5 signingTime as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x05" in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_smime_sign_no_attributes(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [pkcs7.PKCS7Options.NoAttributes] + sig_binary = builder.sign(serialization.Encoding.DER, options) + # NoAttributes removes all authenticated attributes, so we shouldn't + # find SMIMECapabilities or signingTime. + + # 1.2.840.113549.1.9.15 SMIMECapabilities as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x0f" not in sig_binary + # 1.2.840.113549.1.9.5 signingTime as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x05" not in sig_binary + _smime_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_multiple_signers(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + rsa_key = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read(), None + ), + mode="rb", + ) + rsa_cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA512()) + .add_signer(rsa_cert, rsa_key, hashes.SHA512()) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + # There should be three SHA512 OIDs in this structure + assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x03") == 3 + _smime_verify( + serialization.Encoding.DER, + sig, + None, + [cert, rsa_cert], + options, + backend, + ) + + def test_multiple_signers_different_hash_algs(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + rsa_key = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read(), None + ), + mode="rb", + ) + rsa_cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA384()) + .add_signer(rsa_cert, rsa_key, hashes.SHA512()) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + # There should be two SHA384 and two SHA512 OIDs in this structure + assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x02") == 2 + assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x03") == 2 + _smime_verify( + serialization.Encoding.DER, + sig, + None, + [cert, rsa_cert], + options, + backend, + ) diff --git a/tests/hazmat/primitives/test_smime.py b/tests/hazmat/primitives/test_smime.py deleted file mode 100644 index c2ff275e0999..000000000000 --- a/tests/hazmat/primitives/test_smime.py +++ /dev/null @@ -1,518 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import absolute_import, division, print_function - -import os - -import pytest - -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization, smime -from cryptography.hazmat.primitives.asymmetric import ed25519 - -from .utils import load_vectors_from_file - - -# We have no public verification API and won't be adding one until we get -# some requirements from users so this function exists to give us basic -# verification for the signing tests. -def _smime_verify(encoding, sig, msg, certs, options, backend): - sig_bio = backend._bytes_to_bio(sig) - if encoding is serialization.Encoding.DER: - p7 = backend._lib.d2i_PKCS7_bio(sig_bio.bio, backend._ffi.NULL) - else: - p7 = backend._lib.SMIME_read_PKCS7(sig_bio.bio, backend._ffi.NULL) - backend.openssl_assert(p7 != backend._ffi.NULL) - p7 = backend._ffi.gc(p7, backend._lib.PKCS7_free) - flags = 0 - for option in options: - if option is smime.SMIMEOptions.Text: - flags |= backend._lib.PKCS7_TEXT - store = backend._lib.X509_STORE_new() - backend.openssl_assert(store != backend._ffi.NULL) - store = backend._ffi.gc(store, backend._lib.X509_STORE_free) - for cert in certs: - res = backend._lib.X509_STORE_add_cert(store, cert._x509) - backend.openssl_assert(res == 1) - if msg is None: - res = backend._lib.PKCS7_verify( - p7, - backend._ffi.NULL, - store, - backend._ffi.NULL, - backend._ffi.NULL, - flags, - ) - else: - msg_bio = backend._bytes_to_bio(msg) - res = backend._lib.PKCS7_verify( - p7, backend._ffi.NULL, store, msg_bio.bio, backend._ffi.NULL, flags - ) - backend.openssl_assert(res == 1) - - -def _load_cert_key(): - key = load_vectors_from_file( - os.path.join("x509", "custom", "ca", "ca_key.pem"), - lambda pemfile: serialization.load_pem_private_key( - pemfile.read(), None - ), - mode="rb", - ) - cert = load_vectors_from_file( - os.path.join("x509", "custom", "ca", "ca.pem"), - loader=lambda pemfile: x509.load_pem_x509_certificate(pemfile.read()), - mode="rb", - ) - return cert, key - - -class TestSMIMEBuilder(object): - def test_invalid_data(self): - builder = smime.SMIMESignatureBuilder() - with pytest.raises(TypeError): - builder.set_data(u"not bytes") - - def test_set_data_twice(self): - builder = smime.SMIMESignatureBuilder().set_data(b"test") - with pytest.raises(ValueError): - builder.set_data(b"test") - - def test_sign_no_signer(self): - builder = smime.SMIMESignatureBuilder().set_data(b"test") - with pytest.raises(ValueError): - builder.sign(serialization.Encoding.PEM, []) - - def test_sign_no_data(self): - cert, key = _load_cert_key() - builder = smime.SMIMESignatureBuilder().add_signer( - cert, key, hashes.SHA256() - ) - with pytest.raises(ValueError): - builder.sign(serialization.Encoding.PEM, []) - - def test_unsupported_hash_alg(self): - cert, key = _load_cert_key() - with pytest.raises(TypeError): - smime.SMIMESignatureBuilder().add_signer( - cert, key, hashes.SHA512_256() - ) - - def test_not_a_cert(self): - cert, key = _load_cert_key() - with pytest.raises(TypeError): - smime.SMIMESignatureBuilder().add_signer( - b"notacert", key, hashes.SHA256() - ) - - @pytest.mark.supported( - only_if=lambda backend: backend.ed25519_supported(), - skip_message="Does not support ed25519.", - ) - def test_unsupported_key_type(self, backend): - cert, _ = _load_cert_key() - key = ed25519.Ed25519PrivateKey.generate() - with pytest.raises(TypeError): - smime.SMIMESignatureBuilder().add_signer( - cert, key, hashes.SHA256() - ) - - def test_sign_invalid_options(self): - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(b"test") - .add_signer(cert, key, hashes.SHA256()) - ) - with pytest.raises(ValueError): - builder.sign(serialization.Encoding.PEM, [b"invalid"]) - - def test_sign_invalid_encoding(self): - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(b"test") - .add_signer(cert, key, hashes.SHA256()) - ) - with pytest.raises(ValueError): - builder.sign(serialization.Encoding.Raw, []) - - def test_sign_invalid_options_text_no_detached(self): - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(b"test") - .add_signer(cert, key, hashes.SHA256()) - ) - options = [smime.SMIMEOptions.Text] - with pytest.raises(ValueError): - builder.sign(serialization.Encoding.PEM, options) - - def test_sign_invalid_options_text_der_encoding(self): - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(b"test") - .add_signer(cert, key, hashes.SHA256()) - ) - options = [ - smime.SMIMEOptions.Text, - smime.SMIMEOptions.DetachedSignature, - ] - with pytest.raises(ValueError): - builder.sign(serialization.Encoding.DER, options) - - def test_sign_invalid_options_no_attrs_and_no_caps(self): - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(b"test") - .add_signer(cert, key, hashes.SHA256()) - ) - options = [ - smime.SMIMEOptions.NoAttributes, - smime.SMIMEOptions.NoCapabilities, - ] - with pytest.raises(ValueError): - builder.sign(serialization.Encoding.PEM, options) - - def test_smime_sign_detached(self, backend): - data = b"hello world" - cert, key = _load_cert_key() - options = [smime.SMIMEOptions.DetachedSignature] - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hashes.SHA256()) - ) - - sig = builder.sign(serialization.Encoding.PEM, options) - sig_binary = builder.sign(serialization.Encoding.DER, options) - # We don't have a generic ASN.1 parser available to us so we instead - # will assert on specific byte sequences being present based on the - # parameters chosen above. - assert b"sha-256" in sig - # Detached signature means that the signed data is *not* embedded into - # the PKCS7 structure itself, but is present in the PEM serialization - # as a separate section before the PKCS7 data. So we should expect to - # have data in sig but not in sig_binary - assert data in sig - _smime_verify( - serialization.Encoding.PEM, sig, data, [cert], options, backend - ) - assert data not in sig_binary - _smime_verify( - serialization.Encoding.DER, - sig_binary, - data, - [cert], - options, - backend, - ) - - def test_sign_byteslike(self): - data = bytearray(b"hello world") - cert, key = _load_cert_key() - options = [smime.SMIMEOptions.DetachedSignature] - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hashes.SHA256()) - ) - - sig = builder.sign(serialization.Encoding.PEM, options) - assert bytes(data) in sig - - @pytest.mark.parametrize( - ("hash_alg", "expected_value"), - [ - (hashes.SHA1(), b"\x06\x05+\x0e\x03\x02\x1a"), - (hashes.SHA256(), b"\x06\t`\x86H\x01e\x03\x04\x02\x01"), - (hashes.SHA384(), b"\x06\t`\x86H\x01e\x03\x04\x02\x02"), - (hashes.SHA512(), b"\x06\t`\x86H\x01e\x03\x04\x02\x03"), - ], - ) - def test_smime_sign_alternate_digests_der( - self, hash_alg, expected_value, backend - ): - data = b"hello world" - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hash_alg) - ) - options = [] - sig = builder.sign(serialization.Encoding.DER, options) - assert expected_value in sig - _smime_verify( - serialization.Encoding.DER, sig, None, [cert], options, backend - ) - - @pytest.mark.parametrize( - ("hash_alg", "expected_value"), - [ - (hashes.SHA1(), b"sha1"), - (hashes.SHA256(), b"sha-256"), - (hashes.SHA384(), b"sha-384"), - (hashes.SHA512(), b"sha-512"), - ], - ) - def test_smime_sign_alternate_digests_detached_pem( - self, hash_alg, expected_value - ): - data = b"hello world" - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hash_alg) - ) - options = [smime.SMIMEOptions.DetachedSignature] - sig = builder.sign(serialization.Encoding.PEM, options) - # When in detached signature mode the hash algorithm is stored as a - # byte string like "sha-384". - assert expected_value in sig - - def test_smime_sign_attached(self, backend): - data = b"hello world" - cert, key = _load_cert_key() - options = [] - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hashes.SHA256()) - ) - - sig_binary = builder.sign(serialization.Encoding.DER, options) - # When not passing detached signature the signed data is embedded into - # the PKCS7 structure itself - assert data in sig_binary - _smime_verify( - serialization.Encoding.DER, - sig_binary, - None, - [cert], - options, - backend, - ) - - def test_smime_sign_binary(self, backend): - data = b"hello\nworld" - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hashes.SHA256()) - ) - options = [] - sig_no_binary = builder.sign(serialization.Encoding.DER, options) - sig_binary = builder.sign( - serialization.Encoding.DER, [smime.SMIMEOptions.Binary] - ) - # Binary prevents translation of LF to CR+LF (SMIME canonical form) - # so data should not be present in sig_no_binary, but should be present - # in sig_binary - assert data not in sig_no_binary - _smime_verify( - serialization.Encoding.DER, - sig_no_binary, - None, - [cert], - options, - backend, - ) - assert data in sig_binary - _smime_verify( - serialization.Encoding.DER, - sig_binary, - None, - [cert], - options, - backend, - ) - - def test_smime_sign_smime_canonicalization(self, backend): - data = b"hello\nworld" - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hashes.SHA256()) - ) - - options = [] - sig_binary = builder.sign(serialization.Encoding.DER, options) - # LF gets converted to CR+LF (SMIME canonical form) - # so data should not be present in the sig - assert data not in sig_binary - assert b"hello\r\nworld" in sig_binary - _smime_verify( - serialization.Encoding.DER, - sig_binary, - None, - [cert], - options, - backend, - ) - - def test_smime_sign_text(self, backend): - data = b"hello world" - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hashes.SHA256()) - ) - - options = [ - smime.SMIMEOptions.Text, - smime.SMIMEOptions.DetachedSignature, - ] - sig_pem = builder.sign(serialization.Encoding.PEM, options) - # The text option adds text/plain headers to the S/MIME message - # These headers are only relevant in PEM mode, not binary, which is - # just the PKCS7 structure itself. - assert b"text/plain" in sig_pem - # When passing the Text option the header is prepended so the actual - # signed data is this. - signed_data = b"Content-Type: text/plain\r\n\r\nhello world" - _smime_verify( - serialization.Encoding.PEM, - sig_pem, - signed_data, - [cert], - options, - backend, - ) - - def test_smime_sign_no_capabilities(self, backend): - data = b"hello world" - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hashes.SHA256()) - ) - - options = [smime.SMIMEOptions.NoCapabilities] - sig_binary = builder.sign(serialization.Encoding.DER, options) - # NoCapabilities removes the SMIMECapabilities attribute from the - # PKCS7 structure. This is an ASN.1 sequence with the - # OID 1.2.840.113549.1.9.15. It does NOT remove all authenticated - # attributes, so we verify that by looking for the signingTime OID. - - # 1.2.840.113549.1.9.15 SMIMECapabilities as an ASN.1 DER encoded OID - assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x0f" not in sig_binary - # 1.2.840.113549.1.9.5 signingTime as an ASN.1 DER encoded OID - assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x05" in sig_binary - _smime_verify( - serialization.Encoding.DER, - sig_binary, - None, - [cert], - options, - backend, - ) - - def test_smime_sign_no_attributes(self, backend): - data = b"hello world" - cert, key = _load_cert_key() - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hashes.SHA256()) - ) - - options = [smime.SMIMEOptions.NoAttributes] - sig_binary = builder.sign(serialization.Encoding.DER, options) - # NoAttributes removes all authenticated attributes, so we shouldn't - # find SMIMECapabilities or signingTime. - - # 1.2.840.113549.1.9.15 SMIMECapabilities as an ASN.1 DER encoded OID - assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x0f" not in sig_binary - # 1.2.840.113549.1.9.5 signingTime as an ASN.1 DER encoded OID - assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x05" not in sig_binary - _smime_verify( - serialization.Encoding.DER, - sig_binary, - None, - [cert], - options, - backend, - ) - - def test_multiple_signers(self, backend): - data = b"hello world" - cert, key = _load_cert_key() - rsa_key = load_vectors_from_file( - os.path.join("x509", "custom", "ca", "rsa_key.pem"), - lambda pemfile: serialization.load_pem_private_key( - pemfile.read(), None - ), - mode="rb", - ) - rsa_cert = load_vectors_from_file( - os.path.join("x509", "custom", "ca", "rsa_ca.pem"), - loader=lambda pemfile: x509.load_pem_x509_certificate( - pemfile.read() - ), - mode="rb", - ) - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hashes.SHA512()) - .add_signer(rsa_cert, rsa_key, hashes.SHA512()) - ) - options = [] - sig = builder.sign(serialization.Encoding.DER, options) - # There should be three SHA512 OIDs in this structure - assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x03") == 3 - _smime_verify( - serialization.Encoding.DER, - sig, - None, - [cert, rsa_cert], - options, - backend, - ) - - def test_multiple_signers_different_hash_algs(self, backend): - data = b"hello world" - cert, key = _load_cert_key() - rsa_key = load_vectors_from_file( - os.path.join("x509", "custom", "ca", "rsa_key.pem"), - lambda pemfile: serialization.load_pem_private_key( - pemfile.read(), None - ), - mode="rb", - ) - rsa_cert = load_vectors_from_file( - os.path.join("x509", "custom", "ca", "rsa_ca.pem"), - loader=lambda pemfile: x509.load_pem_x509_certificate( - pemfile.read() - ), - mode="rb", - ) - builder = ( - smime.SMIMESignatureBuilder() - .set_data(data) - .add_signer(cert, key, hashes.SHA384()) - .add_signer(rsa_cert, rsa_key, hashes.SHA512()) - ) - options = [] - sig = builder.sign(serialization.Encoding.DER, options) - # There should be two SHA384 and two SHA512 OIDs in this structure - assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x02") == 2 - assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x03") == 2 - _smime_verify( - serialization.Encoding.DER, - sig, - None, - [cert, rsa_cert], - options, - backend, - ) From 95c4f68c1b50b8eb84a0f3268bc6fdbd08e20f08 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 24 Oct 2020 18:17:06 -0700 Subject: [PATCH 27/99] PKCS7SignatureBuilder now supports three serializations (#5497) * PKCS7SignatureBuilder now supports three serializations PEM, DER, and SMIME. SMIME embeds the S/MIME headers and has the detached signature concept. * thanks libre --- .../primitives/asymmetric/serialization.rst | 21 ++-- src/_cffi_src/openssl/pkcs7.py | 1 + .../hazmat/backends/openssl/backend.py | 8 +- .../hazmat/primitives/serialization/base.py | 1 + .../hazmat/primitives/serialization/pkcs7.py | 19 ++-- tests/hazmat/primitives/test_pkcs7.py | 97 ++++++++++++------- 6 files changed, 95 insertions(+), 52 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index f3dca471a7f2..cc6d2bae5f75 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -620,7 +620,7 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``, ... ).add_signer( ... cert, key, hashes.SHA256() ... ).sign( - ... serialization.Encoding.PEM, options + ... serialization.Encoding.SMIME, options ... ) b'...' @@ -649,8 +649,9 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``, .. method:: sign(encoding, options, backend=None) - :param encoding: :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` - or :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`. + :param encoding: :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`, + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`, + or :attr:`~cryptography.hazmat.primitives.serialization.Encoding.SMIME`. :param options: A list of :class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. @@ -670,19 +671,19 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``, The text option adds ``text/plain`` headers to an S/MIME message when serializing to - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`. + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.SMIME`. This option is disallowed with ``DER`` serialization. .. attribute:: Binary - S/MIME signing normally converts line endings (LF to CRLF). When + Signing normally converts line endings (LF to CRLF). When passing this option the data will not be converted. .. attribute:: DetachedSignature Don't embed the signed data within the ASN.1. When signing with - :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` this - also results in the data being added as clear text before the + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.SMIME` + this also results in the data being added as clear text before the PEM encoded structure. .. attribute:: NoCapabilities @@ -891,6 +892,12 @@ Serialization Encodings The format used by elliptic curve point encodings. This is a binary format. + .. attribute:: SMIME + + .. versionadded:: 3.2 + + An output format used for PKCS7. This is a text format. + Serialization Encryption Types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/_cffi_src/openssl/pkcs7.py b/src/_cffi_src/openssl/pkcs7.py index 1878ec59dc15..c22263dfe6c1 100644 --- a/src/_cffi_src/openssl/pkcs7.py +++ b/src/_cffi_src/openssl/pkcs7.py @@ -60,6 +60,7 @@ PKCS7 *PKCS7_sign(X509 *, EVP_PKEY *, Cryptography_STACK_OF_X509 *, BIO *, int); int SMIME_write_PKCS7(BIO *, PKCS7 *, BIO *, int); +int PEM_write_bio_PKCS7_stream(BIO *, PKCS7 *, BIO *, int); PKCS7_SIGNER_INFO *PKCS7_sign_add_signer(PKCS7 *, X509 *, EVP_PKEY *, const EVP_MD *, int); int PKCS7_final(PKCS7 *, BIO *, int); diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 3fd87ac5b1e1..81e4289e8b37 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -2735,11 +2735,17 @@ def pkcs7_sign(self, builder, encoding, options): final_flags |= self._lib.PKCS7_BINARY bio_out = self._create_mem_bio_gc() - if encoding is serialization.Encoding.PEM: + if encoding is serialization.Encoding.SMIME: # This finalizes the structure res = self._lib.SMIME_write_PKCS7( bio_out, p7, bio.bio, final_flags ) + elif encoding is serialization.Encoding.PEM: + res = self._lib.PKCS7_final(p7, bio.bio, final_flags) + self.openssl_assert(res == 1) + res = self._lib.PEM_write_bio_PKCS7_stream( + bio_out, p7, bio.bio, final_flags + ) else: assert encoding is serialization.Encoding.DER # We need to call finalize here becauase i2d_PKCS7_bio does not diff --git a/src/cryptography/hazmat/primitives/serialization/base.py b/src/cryptography/hazmat/primitives/serialization/base.py index b2b403470f98..fc27235c5cf2 100644 --- a/src/cryptography/hazmat/primitives/serialization/base.py +++ b/src/cryptography/hazmat/primitives/serialization/base.py @@ -49,6 +49,7 @@ class Encoding(Enum): OpenSSH = "OpenSSH" Raw = "Raw" X962 = "ANSI X9.62" + SMIME = "S/MIME" class PrivateFormat(Enum): diff --git a/src/cryptography/hazmat/primitives/serialization/pkcs7.py b/src/cryptography/hazmat/primitives/serialization/pkcs7.py index 658f7e5d2d18..b33cb7094c63 100644 --- a/src/cryptography/hazmat/primitives/serialization/pkcs7.py +++ b/src/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -71,11 +71,14 @@ def sign(self, encoding, options, backend=None): options = list(options) if not all(isinstance(x, PKCS7Options) for x in options): raise ValueError("options must be from the PKCS7Options enum") - if ( - encoding is not serialization.Encoding.PEM - and encoding is not serialization.Encoding.DER + if encoding not in ( + serialization.Encoding.PEM, + serialization.Encoding.DER, + serialization.Encoding.SMIME, ): - raise ValueError("Must be PEM or DER from the Encoding enum") + raise ValueError( + "Must be PEM, DER, or SMIME from the Encoding enum" + ) # Text is a meaningless option unless it is accompanied by # DetachedSignature @@ -88,12 +91,12 @@ def sign(self, encoding, options, backend=None): "DetachedSignature" ) - if ( - PKCS7Options.Text in options - and encoding is serialization.Encoding.DER + if PKCS7Options.Text in options and encoding in ( + serialization.Encoding.DER, + serialization.Encoding.PEM, ): raise ValueError( - "The Text option does nothing when serializing to DER" + "The Text option is only available for SMIME serialization" ) # No attributes implies no capabilities so we'll error if you try to diff --git a/tests/hazmat/primitives/test_pkcs7.py b/tests/hazmat/primitives/test_pkcs7.py index 2a87451d51cc..f60467c2915d 100644 --- a/tests/hazmat/primitives/test_pkcs7.py +++ b/tests/hazmat/primitives/test_pkcs7.py @@ -84,10 +84,17 @@ def test_load_pkcs7_unsupported_type(self): # We have no public verification API and won't be adding one until we get # some requirements from users so this function exists to give us basic # verification for the signing tests. -def _smime_verify(encoding, sig, msg, certs, options, backend): +def _pkcs7_verify(encoding, sig, msg, certs, options, backend): sig_bio = backend._bytes_to_bio(sig) if encoding is serialization.Encoding.DER: p7 = backend._lib.d2i_PKCS7_bio(sig_bio.bio, backend._ffi.NULL) + elif encoding is serialization.Encoding.PEM: + p7 = backend._lib.PEM_read_bio_PKCS7( + sig_bio.bio, + backend._ffi.NULL, + backend._ffi.NULL, + backend._ffi.NULL, + ) else: p7 = backend._lib.SMIME_read_PKCS7(sig_bio.bio, backend._ffi.NULL) backend.openssl_assert(p7 != backend._ffi.NULL) @@ -149,7 +156,7 @@ def test_set_data_twice(self): def test_sign_no_signer(self): builder = pkcs7.PKCS7SignatureBuilder().set_data(b"test") with pytest.raises(ValueError): - builder.sign(serialization.Encoding.PEM, []) + builder.sign(serialization.Encoding.SMIME, []) def test_sign_no_data(self): cert, key = _load_cert_key() @@ -157,7 +164,7 @@ def test_sign_no_data(self): cert, key, hashes.SHA256() ) with pytest.raises(ValueError): - builder.sign(serialization.Encoding.PEM, []) + builder.sign(serialization.Encoding.SMIME, []) def test_unsupported_hash_alg(self): cert, key = _load_cert_key() @@ -193,7 +200,7 @@ def test_sign_invalid_options(self): .add_signer(cert, key, hashes.SHA256()) ) with pytest.raises(ValueError): - builder.sign(serialization.Encoding.PEM, [b"invalid"]) + builder.sign(serialization.Encoding.SMIME, [b"invalid"]) def test_sign_invalid_encoding(self): cert, key = _load_cert_key() @@ -214,7 +221,7 @@ def test_sign_invalid_options_text_no_detached(self): ) options = [pkcs7.PKCS7Options.Text] with pytest.raises(ValueError): - builder.sign(serialization.Encoding.PEM, options) + builder.sign(serialization.Encoding.SMIME, options) def test_sign_invalid_options_text_der_encoding(self): cert, key = _load_cert_key() @@ -242,7 +249,7 @@ def test_sign_invalid_options_no_attrs_and_no_caps(self): pkcs7.PKCS7Options.NoCapabilities, ] with pytest.raises(ValueError): - builder.sign(serialization.Encoding.PEM, options) + builder.sign(serialization.Encoding.SMIME, options) def test_smime_sign_detached(self, backend): data = b"hello world" @@ -254,22 +261,22 @@ def test_smime_sign_detached(self, backend): .add_signer(cert, key, hashes.SHA256()) ) - sig = builder.sign(serialization.Encoding.PEM, options) + sig = builder.sign(serialization.Encoding.SMIME, options) sig_binary = builder.sign(serialization.Encoding.DER, options) # We don't have a generic ASN.1 parser available to us so we instead # will assert on specific byte sequences being present based on the # parameters chosen above. assert b"sha-256" in sig # Detached signature means that the signed data is *not* embedded into - # the PKCS7 structure itself, but is present in the PEM serialization + # the PKCS7 structure itself, but is present in the SMIME serialization # as a separate section before the PKCS7 data. So we should expect to # have data in sig but not in sig_binary assert data in sig - _smime_verify( - serialization.Encoding.PEM, sig, data, [cert], options, backend + _pkcs7_verify( + serialization.Encoding.SMIME, sig, data, [cert], options, backend ) assert data not in sig_binary - _smime_verify( + _pkcs7_verify( serialization.Encoding.DER, sig_binary, data, @@ -288,9 +295,29 @@ def test_sign_byteslike(self): .add_signer(cert, key, hashes.SHA256()) ) - sig = builder.sign(serialization.Encoding.PEM, options) + sig = builder.sign(serialization.Encoding.SMIME, options) assert bytes(data) in sig + def test_sign_pem(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + options = [] + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig = builder.sign(serialization.Encoding.PEM, options) + _pkcs7_verify( + serialization.Encoding.PEM, + sig, + None, + [cert], + options, + backend, + ) + @pytest.mark.parametrize( ("hash_alg", "expected_value"), [ @@ -300,7 +327,7 @@ def test_sign_byteslike(self): (hashes.SHA512(), b"\x06\t`\x86H\x01e\x03\x04\x02\x03"), ], ) - def test_smime_sign_alternate_digests_der( + def test_sign_alternate_digests_der( self, hash_alg, expected_value, backend ): data = b"hello world" @@ -313,7 +340,7 @@ def test_smime_sign_alternate_digests_der( options = [] sig = builder.sign(serialization.Encoding.DER, options) assert expected_value in sig - _smime_verify( + _pkcs7_verify( serialization.Encoding.DER, sig, None, [cert], options, backend ) @@ -326,9 +353,7 @@ def test_smime_sign_alternate_digests_der( (hashes.SHA512(), b"sha-512"), ], ) - def test_smime_sign_alternate_digests_detached_pem( - self, hash_alg, expected_value - ): + def test_sign_alternate_digests_detached(self, hash_alg, expected_value): data = b"hello world" cert, key = _load_cert_key() builder = ( @@ -337,12 +362,12 @@ def test_smime_sign_alternate_digests_detached_pem( .add_signer(cert, key, hash_alg) ) options = [pkcs7.PKCS7Options.DetachedSignature] - sig = builder.sign(serialization.Encoding.PEM, options) + sig = builder.sign(serialization.Encoding.SMIME, options) # When in detached signature mode the hash algorithm is stored as a # byte string like "sha-384". assert expected_value in sig - def test_smime_sign_attached(self, backend): + def test_sign_attached(self, backend): data = b"hello world" cert, key = _load_cert_key() options = [] @@ -356,7 +381,7 @@ def test_smime_sign_attached(self, backend): # When not passing detached signature the signed data is embedded into # the PKCS7 structure itself assert data in sig_binary - _smime_verify( + _pkcs7_verify( serialization.Encoding.DER, sig_binary, None, @@ -365,7 +390,7 @@ def test_smime_sign_attached(self, backend): backend, ) - def test_smime_sign_binary(self, backend): + def test_sign_binary(self, backend): data = b"hello\nworld" cert, key = _load_cert_key() builder = ( @@ -382,7 +407,7 @@ def test_smime_sign_binary(self, backend): # so data should not be present in sig_no_binary, but should be present # in sig_binary assert data not in sig_no_binary - _smime_verify( + _pkcs7_verify( serialization.Encoding.DER, sig_no_binary, None, @@ -391,7 +416,7 @@ def test_smime_sign_binary(self, backend): backend, ) assert data in sig_binary - _smime_verify( + _pkcs7_verify( serialization.Encoding.DER, sig_binary, None, @@ -400,7 +425,7 @@ def test_smime_sign_binary(self, backend): backend, ) - def test_smime_sign_smime_canonicalization(self, backend): + def test_sign_smime_canonicalization(self, backend): data = b"hello\nworld" cert, key = _load_cert_key() builder = ( @@ -415,7 +440,7 @@ def test_smime_sign_smime_canonicalization(self, backend): # so data should not be present in the sig assert data not in sig_binary assert b"hello\r\nworld" in sig_binary - _smime_verify( + _pkcs7_verify( serialization.Encoding.DER, sig_binary, None, @@ -424,7 +449,7 @@ def test_smime_sign_smime_canonicalization(self, backend): backend, ) - def test_smime_sign_text(self, backend): + def test_sign_text(self, backend): data = b"hello world" cert, key = _load_cert_key() builder = ( @@ -437,16 +462,16 @@ def test_smime_sign_text(self, backend): pkcs7.PKCS7Options.Text, pkcs7.PKCS7Options.DetachedSignature, ] - sig_pem = builder.sign(serialization.Encoding.PEM, options) + sig_pem = builder.sign(serialization.Encoding.SMIME, options) # The text option adds text/plain headers to the S/MIME message - # These headers are only relevant in PEM mode, not binary, which is + # These headers are only relevant in SMIME mode, not binary, which is # just the PKCS7 structure itself. assert b"text/plain" in sig_pem # When passing the Text option the header is prepended so the actual # signed data is this. signed_data = b"Content-Type: text/plain\r\n\r\nhello world" - _smime_verify( - serialization.Encoding.PEM, + _pkcs7_verify( + serialization.Encoding.SMIME, sig_pem, signed_data, [cert], @@ -454,7 +479,7 @@ def test_smime_sign_text(self, backend): backend, ) - def test_smime_sign_no_capabilities(self, backend): + def test_sign_no_capabilities(self, backend): data = b"hello world" cert, key = _load_cert_key() builder = ( @@ -474,7 +499,7 @@ def test_smime_sign_no_capabilities(self, backend): assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x0f" not in sig_binary # 1.2.840.113549.1.9.5 signingTime as an ASN.1 DER encoded OID assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x05" in sig_binary - _smime_verify( + _pkcs7_verify( serialization.Encoding.DER, sig_binary, None, @@ -483,7 +508,7 @@ def test_smime_sign_no_capabilities(self, backend): backend, ) - def test_smime_sign_no_attributes(self, backend): + def test_sign_no_attributes(self, backend): data = b"hello world" cert, key = _load_cert_key() builder = ( @@ -501,7 +526,7 @@ def test_smime_sign_no_attributes(self, backend): assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x0f" not in sig_binary # 1.2.840.113549.1.9.5 signingTime as an ASN.1 DER encoded OID assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x05" not in sig_binary - _smime_verify( + _pkcs7_verify( serialization.Encoding.DER, sig_binary, None, @@ -537,7 +562,7 @@ def test_multiple_signers(self, backend): sig = builder.sign(serialization.Encoding.DER, options) # There should be three SHA512 OIDs in this structure assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x03") == 3 - _smime_verify( + _pkcs7_verify( serialization.Encoding.DER, sig, None, @@ -574,7 +599,7 @@ def test_multiple_signers_different_hash_algs(self, backend): # There should be two SHA384 and two SHA512 OIDs in this structure assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x02") == 2 assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x03") == 2 - _smime_verify( + _pkcs7_verify( serialization.Encoding.DER, sig, None, From 085d1e44c6fe0480a4f67c34caf747943f4a2f20 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 25 Oct 2020 06:11:48 -0700 Subject: [PATCH 28/99] allow additional certificates to be added to a pkcs7 (#5498) * allow additional certificates to be added to a pkcs7 * be more verbose about what these additional certs might be used for * missing test --- .../primitives/asymmetric/serialization.rst | 8 +++ .../hazmat/backends/openssl/backend.py | 11 ++++- .../hazmat/primitives/serialization/pkcs7.py | 11 ++++- tests/hazmat/primitives/test_pkcs7.py | 49 +++++++++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index cc6d2bae5f75..bb278d8e11ad 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -647,6 +647,14 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``, :class:`~cryptography.hazmat.primitives.hashes.SHA384`, or :class:`~cryptography.hazmat.primitives.hashes.SHA512`. + .. method:: add_certificate(certificate) + + Add an additional certificate (typically used to help build a + verification chain) to the PKCS7 structure. This method may + be called multiple times to add as many certificates as desired. + + :param certificate: The :class:`~cryptography.x509.Certificate` to add. + .. method:: sign(encoding, options, backend=None) :param encoding: :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`, diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 81e4289e8b37..76600dc08f9d 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -2695,6 +2695,15 @@ def pkcs7_sign(self, builder, encoding, options): init_flags = self._lib.PKCS7_PARTIAL final_flags = 0 + if len(builder._additional_certs) == 0: + certs = self._ffi.NULL + else: + certs = self._lib.sk_X509_new_null() + certs = self._ffi.gc(certs, self._lib.sk_X509_free) + for cert in builder._additional_certs: + res = self._lib.sk_X509_push(certs, cert._x509) + self.openssl_assert(res >= 1) + if pkcs7.PKCS7Options.DetachedSignature in options: # Don't embed the data in the PKCS7 structure init_flags |= self._lib.PKCS7_DETACHED @@ -2705,7 +2714,7 @@ def pkcs7_sign(self, builder, encoding, options): p7 = self._lib.PKCS7_sign( self._ffi.NULL, self._ffi.NULL, - self._ffi.NULL, + certs, self._ffi.NULL, init_flags, ) diff --git a/src/cryptography/hazmat/primitives/serialization/pkcs7.py b/src/cryptography/hazmat/primitives/serialization/pkcs7.py index b33cb7094c63..275d7708348f 100644 --- a/src/cryptography/hazmat/primitives/serialization/pkcs7.py +++ b/src/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -24,9 +24,10 @@ def load_der_pkcs7_certificates(data): class PKCS7SignatureBuilder(object): - def __init__(self, data=None, signers=[]): + def __init__(self, data=None, signers=[], additional_certs=[]): self._data = data self._signers = signers + self._additional_certs = additional_certs def set_data(self, data): _check_byteslike("data", data) @@ -63,6 +64,14 @@ def add_signer(self, certificate, private_key, hash_algorithm): self._signers + [(certificate, private_key, hash_algorithm)], ) + def add_certificate(self, certificate): + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + return PKCS7SignatureBuilder( + self._data, self._signers, self._additional_certs + [certificate] + ) + def sign(self, encoding, options, backend=None): if len(self._signers) == 0: raise ValueError("Must have at least one signer") diff --git a/tests/hazmat/primitives/test_pkcs7.py b/tests/hazmat/primitives/test_pkcs7.py index f60467c2915d..03a928352c9f 100644 --- a/tests/hazmat/primitives/test_pkcs7.py +++ b/tests/hazmat/primitives/test_pkcs7.py @@ -607,3 +607,52 @@ def test_multiple_signers_different_hash_algs(self, backend): options, backend, ) + + def test_add_additional_cert_not_a_cert(self, backend): + with pytest.raises(TypeError): + pkcs7.PKCS7SignatureBuilder().add_certificate(b"notacert") + + def test_add_additional_cert(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + rsa_cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA384()) + .add_certificate(rsa_cert) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + assert ( + sig.count(rsa_cert.public_bytes(serialization.Encoding.DER)) == 1 + ) + + def test_add_multiple_additional_certs(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + rsa_cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA384()) + .add_certificate(rsa_cert) + .add_certificate(rsa_cert) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + assert ( + sig.count(rsa_cert.public_bytes(serialization.Encoding.DER)) == 2 + ) From 836a92a28fbe9df8c37121e340b91ed9cd519ddd Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 25 Oct 2020 06:15:18 -0700 Subject: [PATCH 29/99] chunking didn't actually work (#5499) --- src/cryptography/hazmat/backends/openssl/ciphers.py | 2 +- tests/hazmat/primitives/test_ciphers.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py index 171605a683de..1e805d235aa2 100644 --- a/src/cryptography/hazmat/backends/openssl/ciphers.py +++ b/src/cryptography/hazmat/backends/openssl/ciphers.py @@ -17,7 +17,7 @@ class _CipherContext(object): _ENCRYPT = 1 _DECRYPT = 0 - _MAX_CHUNK_SIZE = 2 ** 31 + _MAX_CHUNK_SIZE = 2 ** 31 - 1 def __init__(self, backend, cipher, mode, operation): self._backend = backend diff --git a/tests/hazmat/primitives/test_ciphers.py b/tests/hazmat/primitives/test_ciphers.py index 104e679e54a6..4d82f0c13f42 100644 --- a/tests/hazmat/primitives/test_ciphers.py +++ b/tests/hazmat/primitives/test_ciphers.py @@ -333,3 +333,12 @@ def test_update_into_auto_chunking(self, backend, monkeypatch): decbuf = bytearray(527) decprocessed = decryptor.update_into(buf[:processed], decbuf) assert decbuf[:decprocessed] == pt + + def test_max_chunk_size_fits_in_int32(self, backend): + # max chunk must fit in signed int32 or else a call large enough to + # cause chunking will result in the very OverflowError we want to + # avoid with chunking. + key = b"\x00" * 16 + c = ciphers.Cipher(AES(key), modes.ECB(), backend) + encryptor = c.encryptor() + backend._ffi.new("int *", encryptor._ctx._MAX_CHUNK_SIZE) From 611c4a340f6c53a7e28a9695a3248bd4e9f8558d Mon Sep 17 00:00:00 2001 From: frennkie Date: Sun, 25 Oct 2020 15:50:18 +0100 Subject: [PATCH 30/99] PKCS7SignatureBuilder now supports new option NoCerts when signing (#5500) --- .../primitives/asymmetric/serialization.rst | 7 +++++++ .../hazmat/backends/openssl/backend.py | 4 ++++ .../hazmat/primitives/serialization/pkcs7.py | 1 + tests/hazmat/primitives/test_pkcs7.py | 17 +++++++++++++++++ 4 files changed, 29 insertions(+) diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index bb278d8e11ad..6b2e858db9af 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -707,6 +707,13 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``, pass ``NoAttributes`` you can't pass ``NoCapabilities`` since ``NoAttributes`` removes ``MIMECapabilities`` and more. + .. attribute:: NoCerts + + Don't include the signer's certificate in the PKCS7 structure. This can + reduce the size of the signature but requires that the recipient can + obtain the signer's certificate by other means (for example from a + previously signed message). + Serialization Formats ~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 76600dc08f9d..d948de22896f 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -2728,6 +2728,10 @@ def pkcs7_sign(self, builder, encoding, options): signer_flags |= self._lib.PKCS7_NOSMIMECAP elif pkcs7.PKCS7Options.NoAttributes in options: signer_flags |= self._lib.PKCS7_NOATTR + + if pkcs7.PKCS7Options.NoCerts in options: + signer_flags |= self._lib.PKCS7_NOCERTS + for certificate, private_key, hash_algorithm in builder._signers: md = self._evp_md_non_null_from_algorithm(hash_algorithm) p7signerinfo = self._lib.PKCS7_sign_add_signer( diff --git a/src/cryptography/hazmat/primitives/serialization/pkcs7.py b/src/cryptography/hazmat/primitives/serialization/pkcs7.py index 275d7708348f..1e11e28ef5b3 100644 --- a/src/cryptography/hazmat/primitives/serialization/pkcs7.py +++ b/src/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -129,3 +129,4 @@ class PKCS7Options(Enum): DetachedSignature = "Don't embed data in the PKCS7 structure" NoCapabilities = "Don't embed SMIME capabilities" NoAttributes = "Don't embed authenticatedAttributes" + NoCerts = "Don't embed signer certificate" diff --git a/tests/hazmat/primitives/test_pkcs7.py b/tests/hazmat/primitives/test_pkcs7.py index 03a928352c9f..8b93cb6334ba 100644 --- a/tests/hazmat/primitives/test_pkcs7.py +++ b/tests/hazmat/primitives/test_pkcs7.py @@ -535,6 +535,23 @@ def test_sign_no_attributes(self, backend): backend, ) + def test_sign_no_certs(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + assert sig.count(cert.public_bytes(serialization.Encoding.DER)) == 1 + + options = [pkcs7.PKCS7Options.NoCerts] + sig_no = builder.sign(serialization.Encoding.DER, options) + assert sig_no.count(cert.public_bytes(serialization.Encoding.DER)) == 0 + def test_multiple_signers(self, backend): data = b"hello world" cert, key = _load_cert_key() From 52a0e44e97dd6e150509b14c9b1f76a261f12786 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 25 Oct 2020 10:55:27 -0400 Subject: [PATCH 31/99] Add a dependabot configuration to bump our github actions (#5501) --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..123014908beb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" From 08a97cca715ca0842d6792d0079e351efbb48ec9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Oct 2020 11:46:21 -0400 Subject: [PATCH 32/99] Bump actions/upload-artifact from v1 to v2.2.0 (#5502) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from v1 to v2.2.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v1...27bce4eee761b5bc643f46a8dfb41b430c8d05f6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/wheel-builder.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wheel-builder.yml b/.github/workflows/wheel-builder.yml index ba15dd656a93..7fcbec44c188 100644 --- a/.github/workflows/wheel-builder.yml +++ b/.github/workflows/wheel-builder.yml @@ -47,7 +47,7 @@ jobs: .venv/bin/python -c "from cryptography.hazmat.backends.openssl.backend import backend;print('Loaded: ' + backend.openssl_version_text());print('Linked Against: ' + backend._ffi.string(backend._lib.OPENSSL_VERSION_TEXT).decode('ascii'))" - run: mkdir cryptography-wheelhouse - run: mv wheelhouse/cryptography*.whl cryptography-wheelhouse/ - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v2.2.0 with: name: "cryptography-${{ github.event.inputs.version }}-${{ matrix.MANYLINUX.NAME }}-${{ matrix.PYTHON }}" path: cryptography-wheelhouse/ @@ -101,7 +101,7 @@ jobs: - run: mkdir cryptography-wheelhouse - run: mv wheelhouse/cryptography*.whl cryptography-wheelhouse/ - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v2.2.0 with: name: "cryptography-${{ github.event.inputs.version }}-macOS-${{ matrix.PYTHON.ABI_VERSION }}" path: cryptography-wheelhouse/ @@ -159,7 +159,7 @@ jobs: - run: mkdir cryptography-wheelhouse - run: move wheelhouse\cryptography*.whl cryptography-wheelhouse\ - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v2.2.0 with: name: "cryptography-${{ github.event.inputs.version }}-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.VERSION }}-${{ matrix.PYTHON.ABI_VERSION}}" path: cryptography-wheelhouse\ From 8be1d4b1113eabea306dd60ab64e7f00815d6a52 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 25 Oct 2020 11:57:55 -0400 Subject: [PATCH 33/99] Stop using @master for GH actions (#5503) --- .github/workflows/ci.yml | 4 ++-- .github/workflows/wheel-builder.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 717504a61229..b55266721e95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - {VERSION: "3.9", TOXENV: "py39", EXTRA_CFLAGS: "-DUSE_OSRANDOM_RNG_FOR_TESTING"} name: "Python ${{ matrix.PYTHON.VERSION }} on macOS" steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 - name: Setup python uses: actions/setup-python@v2 with: @@ -66,7 +66,7 @@ jobs: - {VERSION: "3.9", TOXENV: "py39", MSVC_VERSION: "2019", CL_FLAGS: "/D USE_OSRANDOM_RNG_FOR_TESTING"} name: "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}" steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 - name: Setup python uses: actions/setup-python@v2 with: diff --git a/.github/workflows/wheel-builder.yml b/.github/workflows/wheel-builder.yml index 7fcbec44c188..74012f09b53a 100644 --- a/.github/workflows/wheel-builder.yml +++ b/.github/workflows/wheel-builder.yml @@ -67,7 +67,7 @@ jobs: BIN_PATH: '/Library/Frameworks/Python.framework/Versions/3.8/bin/python3' name: "${{ matrix.PYTHON.VERSION }} ABI ${{ matrix.PYTHON.ABI_VERSION }} macOS" steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 - run: | curl "$PYTHON_DOWNLOAD_URL" -o python.pkg sudo installer -pkg python.pkg -target / @@ -122,7 +122,7 @@ jobs: - {VERSION: "3.8", MSVC_VERSION: "2019", "USE_ABI3": "true", "ABI_VERSION": "cp36"} name: "${{ matrix.PYTHON.VERSION }} ${{ matrix.WINDOWS.WINDOWS }} ${{ matrix.PYTHON.ABI_VERSION }}" steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 - name: Setup python uses: actions/setup-python@v2 with: From ada53e7ca0f04a33711c330a130d34376e5ecc2b Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 25 Oct 2020 09:13:43 -0700 Subject: [PATCH 34/99] make the regexes for branches more strict (#5504) We don't want them to build when dependabot makes a branch like dependabot/github_actions/actions/upload-artifact-v2.2.0. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 29349ed87109..b0f0fe78d6e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,8 +13,8 @@ cache: branches: only: - master - - /\d+\.\d+\.x/ - - /\d+\.\d+(\.\d+)?/ + - /^\d+\.\d+\.x$/ + - /^\d+\.\d+(\.\d+)?$/ matrix: include: From bf4b962f4b92a1633835b2d17974f18de2d61620 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 25 Oct 2020 16:12:38 -0700 Subject: [PATCH 35/99] be more verbose in the 102 deprecation notice (#5505) --- src/cryptography/hazmat/bindings/openssl/binding.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index bf6f12029b5b..f6bf93729118 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -181,8 +181,11 @@ def _verify_openssl_version(lib): else: raise RuntimeError( "You are linking against OpenSSL 1.0.2, which is no longer " - "supported by the OpenSSL project. You need to upgrade to a " - "newer version of OpenSSL." + "supported by the OpenSSL project. To use this version of " + "cryptography you need to upgrade to a newer version of " + "OpenSSL. For this version only you can also set the " + "environment variable CRYPTOGRAPHY_ALLOW_OPENSSL_102 to " + "allow OpenSSL 1.0.2." ) From cf9bd6a36bc7b05abca114b76e216598d9ad9b16 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 25 Oct 2020 17:15:11 -0700 Subject: [PATCH 36/99] move blinding to __init__ on both RSA public and private (#5506) * move blinding to __init__ on both RSA public and private * change signature to guarantee this test is testing what we think --- .../hazmat/backends/openssl/backend.py | 2 -- .../hazmat/backends/openssl/rsa.py | 12 ++++++++-- tests/hazmat/primitives/test_rsa.py | 24 +++++++++---------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index d948de22896f..b7757e333d9e 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -623,8 +623,6 @@ def load_rsa_private_numbers(self, numbers): self.openssl_assert(res == 1) res = self._lib.RSA_set0_crt_params(rsa_cdata, dmp1, dmq1, iqmp) self.openssl_assert(res == 1) - res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL) - self.openssl_assert(res == 1) evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) return _RSAPrivateKey(self, rsa_cdata, evp_pkey) diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 423f6878c124..69926c8f3723 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -319,6 +319,11 @@ def __init__(self, backend, rsa_cdata, evp_pkey): errors = backend._consume_errors_with_text() raise ValueError("Invalid private key", errors) + # Blinding is on by default in many versions of OpenSSL, but let's + # just be conservative here. + res = backend._lib.RSA_blinding_on(rsa_cdata, backend._ffi.NULL) + backend.openssl_assert(res == 1) + self._backend = backend self._rsa_cdata = rsa_cdata self._evp_pkey = evp_pkey @@ -351,8 +356,6 @@ def public_key(self): ctx = self._backend._lib.RSAPublicKey_dup(self._rsa_cdata) self._backend.openssl_assert(ctx != self._backend._ffi.NULL) ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free) - res = self._backend._lib.RSA_blinding_on(ctx, self._backend._ffi.NULL) - self._backend.openssl_assert(res == 1) evp_pkey = self._backend._rsa_cdata_to_evp_pkey(ctx) return _RSAPublicKey(self._backend, ctx, evp_pkey) @@ -411,6 +414,11 @@ def sign(self, data, padding, algorithm): @utils.register_interface(RSAPublicKeyWithSerialization) class _RSAPublicKey(object): def __init__(self, backend, rsa_cdata, evp_pkey): + # Blinding is on by default in many versions of OpenSSL, but let's + # just be conservative here. + res = backend._lib.RSA_blinding_on(rsa_cdata, backend._ffi.NULL) + backend.openssl_assert(res == 1) + self._backend = backend self._rsa_cdata = rsa_cdata self._evp_pkey = evp_pkey diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index bfb946ee5de4..d7fa7744f493 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -946,21 +946,19 @@ def test_invalid_pss_signature_wrong_key(self, backend): skip_message="Does not support PSS.", ) def test_invalid_pss_signature_data_too_large_for_modulus(self, backend): + # 2048 bit PSS signature signature = binascii.unhexlify( - b"cb43bde4f7ab89eb4a79c6e8dd67e0d1af60715da64429d90c716a490b799c29" - b"194cf8046509c6ed851052367a74e2e92d9b38947ed74332acb115a03fcc0222" + b"58750fc3d2f560d1f3e37c8e28bc8da6d3e93f5d58f8becd25b1c931eea30fea" + b"54cb17d44b90104a0aacb7fe9ffa2a59c5788435911d63de78178d21eb875ccd" + b"0b07121b641ed4fe6bcb1ca5060322765507b4f24bdba8a698a8e4e07e6bf2c4" + b"7a736abe5a912e85cd32f648f3e043b4385e8b612dcce342c5fddf18c524deb5" + b"6295b95f6dfa759b2896b793628a90f133e74c1ff7d3af43e3f7ee792df2e5b6" + b"a19e996ac3676884354899a437b3ae4e3ac91976c336c332a3b1db0d172b19cb" + b"40ad3d871296cfffb3c889ce74a179a3e290852c35d59525afe4b39dc907fad2" + b"ac462c50a488dca486031a3dc8c4cdbbc53e9f71d64732e1533a5d1249b833ce" ) - public_key = rsa.RSAPublicNumbers( - n=int( - b"381201f4905d67dfeb3dec131a0fbea773489227ec7a1448c3109189ac68" - b"5a95441be90866a14c4d2e139cd16db540ec6c7abab13ffff91443fd46a8" - b"960cbb7658ded26a5c95c86f6e40384e1c1239c63e541ba221191c4dd303" - b"231b42e33c6dbddf5ec9a746f09bf0c25d0f8d27f93ee0ae5c0d723348f4" - b"030d3581e13522", - 16, - ), - e=65537, - ).public_key(backend) + # 1024 bit key + public_key = RSA_KEY_1024.private_key(backend).public_key() with pytest.raises(InvalidSignature): public_key.verify( signature, From 58494b41d6ecb0f56b7c5f05d5f5e3ca0320d494 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 25 Oct 2020 21:16:42 -0400 Subject: [PATCH 37/99] Attempt to mitigate Bleichenbacher attacks on RSA decryption (#5507) --- CHANGELOG.rst | 6 +++++ docs/spelling_wordlist.txt | 1 + .../hazmat/backends/openssl/rsa.py | 26 ++++++++----------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 32a6c8522547..f105465e2eaa 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,12 @@ Changelog .. note:: This version is not yet released and is under active development. +* **SECURITY ISSUE:** Attempted to make RSA PKCS#1v1.5 decryption more constant + time, to protect against Bleichenbacher vulnerabilities. Due to limitations + imposed by our API, we cannot completely mitigate this vulnerability and a + future release will contain a new API which is designed to be resilient to + these for contexts where it is required. Credit to **Hubert Kario** for + reporting the issue. *CVE-2020-25659* * Support for OpenSSL 1.0.2 has been removed. Users on older version of OpenSSL will need to upgrade. * Added basic support for PKCS7 signing (including SMIME) via diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 9ec971b36ad2..c8c275142ff7 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -7,6 +7,7 @@ backend Backends backends bcrypt +Bleichenbacher Blowfish boolean Botan diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 69926c8f3723..66b37224e443 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -119,23 +119,19 @@ def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding): outlen = backend._ffi.new("size_t *", buf_size) buf = backend._ffi.new("unsigned char[]", buf_size) + # Everything from this line onwards is written with the goal of being as + # constant-time as is practical given the constraints of Python and our + # API. See Bleichenbacher's '98 attack on RSA, and its many many variants. + # As such, you should not attempt to change this (particularly to "clean it + # up") without understanding why it was written this way (see + # Chesterton's Fence), and without measuring to verify you have not + # introduced observable time differences. res = crypt(pkey_ctx, buf, outlen, data, len(data)) + resbuf = backend._ffi.buffer(buf)[: outlen[0]] + backend._lib.ERR_clear_error() if res <= 0: - _handle_rsa_enc_dec_error(backend, key) - - return backend._ffi.buffer(buf)[: outlen[0]] - - -def _handle_rsa_enc_dec_error(backend, key): - errors = backend._consume_errors_with_text() - if isinstance(key, _RSAPublicKey): - raise ValueError( - "Data too long for key size. Encrypt less data or use a " - "larger key size.", - errors, - ) - else: - raise ValueError("Decryption failed.", errors) + raise ValueError("Encryption/decryption failed.") + return resbuf def _rsa_sig_determine_padding(backend, key, padding, algorithm): From c9e65222c91df8b6f61650a3460e30232962c1e0 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 25 Oct 2020 18:53:53 -0700 Subject: [PATCH 38/99] 3.2 release (#5508) --- CHANGELOG.rst | 6 ++---- src/cryptography/__about__.py | 2 +- vectors/cryptography_vectors/__about__.py | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f105465e2eaa..007f802006d1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,10 +3,8 @@ Changelog .. _v3-2: -3.2 - `master`_ -~~~~~~~~~~~~~~~ - -.. note:: This version is not yet released and is under active development. +3.2 - 2020-10-25 +~~~~~~~~~~~~~~~~ * **SECURITY ISSUE:** Attempted to make RSA PKCS#1v1.5 decryption more constant time, to protect against Bleichenbacher vulnerabilities. Due to limitations diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py index 57905d3ef4e9..b960f7ed1af5 100644 --- a/src/cryptography/__about__.py +++ b/src/cryptography/__about__.py @@ -22,7 +22,7 @@ ) __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.2.dev1" +__version__ = "3.2" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" diff --git a/vectors/cryptography_vectors/__about__.py b/vectors/cryptography_vectors/__about__.py index e1a01178cdc4..3b41b82e0ab9 100644 --- a/vectors/cryptography_vectors/__about__.py +++ b/vectors/cryptography_vectors/__about__.py @@ -20,7 +20,7 @@ __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.2.dev1" +__version__ = "3.2" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" From 558cf91f4760193e73ad6dc42b2ad6e87a06909b Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 25 Oct 2020 23:50:35 -0400 Subject: [PATCH 39/99] Reopen master for 3.3 (#5509) * Reopen master for 3.3 * its how you know its authentic alex gaynor code --- CHANGELOG.rst | 7 +++++++ src/cryptography/__about__.py | 4 ++-- vectors/cryptography_vectors/__about__.py | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 007f802006d1..cf3d3779af7c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,13 @@ Changelog ========= +.. _v3-3: + +3.3 - `master`_ +~~~~~~~~~~~~~~~ + +.. note:: This version is not yet released and is under active development. + .. _v3-2: 3.2 - 2020-10-25 diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py index b960f7ed1af5..758b17899647 100644 --- a/src/cryptography/__about__.py +++ b/src/cryptography/__about__.py @@ -22,10 +22,10 @@ ) __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.2" +__version__ = "3.3.dev1" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" __license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2013-2019 {}".format(__author__) +__copyright__ = "Copyright 2013-2020 {}".format(__author__) diff --git a/vectors/cryptography_vectors/__about__.py b/vectors/cryptography_vectors/__about__.py index 3b41b82e0ab9..9252c0f0f0d0 100644 --- a/vectors/cryptography_vectors/__about__.py +++ b/vectors/cryptography_vectors/__about__.py @@ -20,10 +20,10 @@ __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.2" +__version__ = "3.3.dev1" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" __license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2013-2019 %s" % __author__ +__copyright__ = "Copyright 2013-2020 %s" % __author__ From d3eae8d7dbcd7ca491531424a4ac8b4838acf199 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 26 Oct 2020 01:41:40 -0400 Subject: [PATCH 40/99] Delete all the 1.0.2 code (#5511) --- .readthedocs.yml | 5 + .travis.yml | 5 - MANIFEST.in | 2 +- docs/development/c-bindings.rst | 4 +- docs/faq.rst | 29 ++--- docs/hazmat/backends/openssl.rst | 2 +- docs/installation.rst | 5 - src/_cffi_src/build_openssl.py | 20 ++-- src/_cffi_src/openssl/bio.py | 6 - src/_cffi_src/openssl/callbacks.py | 2 +- src/_cffi_src/openssl/crypto.py | 4 +- src/_cffi_src/openssl/cryptography.py | 15 --- src/_cffi_src/openssl/dh.py | 74 +----------- src/_cffi_src/openssl/dsa.py | 66 ----------- src/_cffi_src/openssl/evp.py | 8 +- src/_cffi_src/openssl/hmac.py | 26 +---- src/_cffi_src/openssl/nid.py | 14 --- src/_cffi_src/openssl/ocsp.py | 8 +- src/_cffi_src/openssl/rsa.py | 107 ------------------ src/_cffi_src/openssl/ssl.py | 70 +----------- src/_cffi_src/openssl/x509.py | 44 +------ src/_cffi_src/openssl/x509_vfy.py | 26 +---- src/_cffi_src/openssl/x509name.py | 2 +- .../hazmat/backends/openssl/backend.py | 10 +- .../hazmat/backends/openssl/ec.py | 6 +- .../hazmat/backends/openssl/hmac.py | 10 +- .../hazmat/bindings/openssl/_conditional.py | 27 ----- .../hazmat/bindings/openssl/binding.py | 27 ----- tests/hazmat/backends/test_openssl.py | 2 +- tests/hazmat/bindings/test_openssl.py | 16 +-- tests/wycheproof/test_rsa.py | 17 +-- tests/x509/test_x509.py | 4 - tests/x509/test_x509_ext.py | 6 +- tox.ini | 2 - 34 files changed, 61 insertions(+), 610 deletions(-) create mode 100644 .readthedocs.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000000..728bb390c30c --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,5 @@ +version: 2 + +build: + image: "7.0" + diff --git a/.travis.yml b/.travis.yml index b0f0fe78d6e6..a13a977f16f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,8 +31,6 @@ matrix: env: TOXENV=pypy-nocoverage - python: pypy3.6-7.3.1 env: TOXENV=pypy3-nocoverage - - python: 3.8 - env: TOXENV=py38 OPENSSL=1.0.2u - python: 2.7 env: TOXENV=py27 OPENSSL=1.1.0l - python: 2.7 @@ -56,9 +54,6 @@ matrix: - python: 3.8 env: TOXENV=py38 LIBRESSL=3.2.2 - - python: 2.7 - services: docker - env: TOXENV=py27 DOCKER=pyca/cryptography-runner-centos7 - python: 2.7 services: docker env: TOXENV=py27 DOCKER=pyca/cryptography-runner-centos8 diff --git a/MANIFEST.in b/MANIFEST.in index 7e97167a1b63..2b90d2465543 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -20,7 +20,7 @@ exclude .travis.yml .travis recursive-exclude .travis * recursive-exclude .github * -exclude release.py .coveragerc codecov.yml dev-requirements.txt rtd-requirements.txt tox.ini +exclude release.py .coveragerc codecov.yml .readthedocs.yml dev-requirements.txt rtd-requirements.txt tox.ini recursive-exclude .zuul.d * recursive-exclude .zuul.playbooks * diff --git a/docs/development/c-bindings.rst b/docs/development/c-bindings.rst index efc21ca0a8f5..e53e0bae7f65 100644 --- a/docs/development/c-bindings.rst +++ b/docs/development/c-bindings.rst @@ -189,9 +189,9 @@ Caveats Sometimes, a set of loosely related features are added in the same version, and it's impractical to create ``#ifdef`` statements for each one. In that case, it may make sense to either check for a particular -version. For example, to check for OpenSSL 1.1.0 or newer:: +version. For example, to check for OpenSSL 1.1.1 or newer:: - #if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER + #if CRYPTOGRAPHY_OPENSSL_111_OR_GREATER Sometimes, the version of a library on a particular platform will have features that you thought it wouldn't, based on its version. diff --git a/docs/faq.rst b/docs/faq.rst index 33c5417d12db..563a241e4f1b 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -109,27 +109,14 @@ Your ``pip`` and/or ``setuptools`` are outdated. Please upgrade to the latest versions with ``pip install -U pip setuptools`` (or on Windows ``python -m pip install -U pip setuptools``). -Importing cryptography causes a ``RuntimeError`` about OpenSSL 1.0.2 --------------------------------------------------------------------- - -The OpenSSL project has dropped support for the 1.0.2 release series. Since it -is no longer receiving security patches from upstream, ``cryptography`` is also -dropping support for it. To fix this issue you should upgrade to a newer -version of OpenSSL (1.1.0 or later). This may require you to upgrade to a newer -operating system. - -For the 3.2 release, you can set the ``CRYPTOGRAPHY_ALLOW_OPENSSL_102`` -environment variable. Please note that this is *temporary* and will be removed -in ``cryptography`` 3.3. - -Installing cryptography with OpenSSL 0.9.8, 1.0.0, 1.0.1 fails --------------------------------------------------------------- - -The OpenSSL project has dropped support for the 0.9.8, 1.0.0, and 1.0.1 release -series. Since they are no longer receiving security patches from upstream, -``cryptography`` is also dropping support for them. To fix this issue you -should upgrade to a newer version of OpenSSL (1.0.2 or later). This may require -you to upgrade to a newer operating system. +Installing cryptography with OpenSSL 0.9.8, 1.0.0, 1.0.1, 1.0.2 fails +--------------------------------------------------------------------- + +The OpenSSL project has dropped support for the 0.9.8, 1.0.0, 1.0.1, and 1.0.2 +release series. Since they are no longer receiving security patches from +upstream, ``cryptography`` is also dropping support for them. To fix this issue +you should upgrade to a newer version of OpenSSL (1.1.0 or later). This may +require you to upgrade to a newer operating system. Why are there no wheels for Python 3.6+ on Linux or macOS? ---------------------------------------------------------- diff --git a/docs/hazmat/backends/openssl.rst b/docs/hazmat/backends/openssl.rst index 0e695279dbe4..dd85d869a635 100644 --- a/docs/hazmat/backends/openssl.rst +++ b/docs/hazmat/backends/openssl.rst @@ -3,7 +3,7 @@ OpenSSL backend =============== -The `OpenSSL`_ C library. Cryptography supports OpenSSL version 1.0.2 and +The `OpenSSL`_ C library. Cryptography supports OpenSSL version 1.1.0 and greater. .. data:: cryptography.hazmat.backends.openssl.backend diff --git a/docs/installation.rst b/docs/installation.rst index c773fdce5d69..abfbb9ba58dd 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -27,14 +27,9 @@ PyPy 7.3.1, and PyPy3 7.3.1 on these operating systems. We test compiling with ``clang`` as well as ``gcc`` and use the following OpenSSL releases: -* ``OpenSSL 1.0.2-latest`` * ``OpenSSL 1.1.0-latest`` * ``OpenSSL 1.1.1-latest`` -.. warning:: - - Cryptography 3.2 has dropped support for OpenSSL 1.0.2, see the - :doc:`FAQ ` for more details Building cryptography on Windows -------------------------------- diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py index 35ccd6b9be0a..f8f41847926d 100644 --- a/src/_cffi_src/build_openssl.py +++ b/src/_cffi_src/build_openssl.py @@ -22,17 +22,15 @@ def _get_openssl_libraries(platform): return [] # OpenSSL goes by a different library name on different operating systems. if platform == "win32" and compiler_type() == "msvc": - windows_link_legacy_openssl = os.environ.get( - "CRYPTOGRAPHY_WINDOWS_LINK_LEGACY_OPENSSL", None - ) - if windows_link_legacy_openssl is None: - # Link against the 1.1.0 names - # CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - libs = ["libssl", "libcrypto"] - else: - # Link against the 1.0.2 and lower names - libs = ["libeay32", "ssleay32"] - return libs + ["advapi32", "crypt32", "gdi32", "user32", "ws2_32"] + return [ + "libssl", + "libcrypto", + "advapi32", + "crypt32", + "gdi32", + "user32", + "ws2_32", + ] else: # darwin, linux, mingw all use this path # In some circumstances, the order in which these libs are diff --git a/src/_cffi_src/openssl/bio.py b/src/_cffi_src/openssl/bio.py index 8f5a3e6a2b6f..52d57c6228d1 100644 --- a/src/_cffi_src/openssl/bio.py +++ b/src/_cffi_src/openssl/bio.py @@ -41,10 +41,4 @@ """ CUSTOMIZATIONS = """ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -int BIO_up_ref(BIO *b) { - CRYPTO_add(&b->references, 1, CRYPTO_LOCK_BIO); - return 1; -} -#endif """ diff --git a/src/_cffi_src/openssl/callbacks.py b/src/_cffi_src/openssl/callbacks.py index 33ebf4df400f..5d5da1b7ec48 100644 --- a/src/_cffi_src/openssl/callbacks.py +++ b/src/_cffi_src/openssl/callbacks.py @@ -51,7 +51,7 @@ using CPython APIs by Armin Rigo of the PyPy project. */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL #ifdef _WIN32 typedef CRITICAL_SECTION Cryptography_mutex; static __inline void cryptography_mutex_init(Cryptography_mutex *mutex) { diff --git a/src/_cffi_src/openssl/crypto.py b/src/_cffi_src/openssl/crypto.py index f3623b21f146..a5db642e89b3 100644 --- a/src/_cffi_src/openssl/crypto.py +++ b/src/_cffi_src/openssl/crypto.py @@ -79,13 +79,13 @@ # define OPENSSL_PLATFORM SSLEAY_PLATFORM # define OPENSSL_DIR SSLEAY_DIR #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_LOCKING_CALLBACKS = 1; #else static const long Cryptography_HAS_LOCKING_CALLBACKS = 0; #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_OPENSSL_CLEANUP = 0; void (*OPENSSL_cleanup)(void) = NULL; diff --git a/src/_cffi_src/openssl/cryptography.py b/src/_cffi_src/openssl/cryptography.py index d9d4a9ea0f41..28d659b1194a 100644 --- a/src/_cffi_src/openssl/cryptography.py +++ b/src/_cffi_src/openssl/cryptography.py @@ -33,19 +33,9 @@ #include #endif -#define CRYPTOGRAPHY_OPENSSL_102L_OR_GREATER \ - (OPENSSL_VERSION_NUMBER >= 0x100020cf && !CRYPTOGRAPHY_IS_LIBRESSL) -#define CRYPTOGRAPHY_OPENSSL_102U_OR_GREATER \ - (OPENSSL_VERSION_NUMBER >= 0x1000215fL && !CRYPTOGRAPHY_IS_LIBRESSL) -#define CRYPTOGRAPHY_OPENSSL_110_OR_GREATER \ - (OPENSSL_VERSION_NUMBER >= 0x10100000 && !CRYPTOGRAPHY_IS_LIBRESSL) #define CRYPTOGRAPHY_OPENSSL_110F_OR_GREATER \ (OPENSSL_VERSION_NUMBER >= 0x1010006f && !CRYPTOGRAPHY_IS_LIBRESSL) -#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I \ - (OPENSSL_VERSION_NUMBER < 0x1000209f || CRYPTOGRAPHY_IS_LIBRESSL) -#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 \ - (OPENSSL_VERSION_NUMBER < 0x10100000 || CRYPTOGRAPHY_IS_LIBRESSL) #define CRYPTOGRAPHY_OPENSSL_LESS_THAN_110J \ (OPENSSL_VERSION_NUMBER < 0x101000af || CRYPTOGRAPHY_IS_LIBRESSL) #define CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 \ @@ -63,13 +53,8 @@ """ TYPES = """ -static const int CRYPTOGRAPHY_OPENSSL_102L_OR_GREATER; -static const int CRYPTOGRAPHY_OPENSSL_102U_OR_GREATER; -static const int CRYPTOGRAPHY_OPENSSL_110_OR_GREATER; static const int CRYPTOGRAPHY_OPENSSL_110F_OR_GREATER; -static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I; -static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_110; static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111; static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B; static const int CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE; diff --git a/src/_cffi_src/openssl/dh.py b/src/_cffi_src/openssl/dh.py index 0e1df23a6ac9..947a5a8ee02e 100644 --- a/src/_cffi_src/openssl/dh.py +++ b/src/_cffi_src/openssl/dh.py @@ -38,79 +38,7 @@ """ CUSTOMIZATIONS = """ -/* These functions were added in OpenSSL 1.1.0 */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -void DH_get0_pqg(const DH *dh, - const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -{ - if (p != NULL) - *p = dh->p; - if (q != NULL) - *q = dh->q; - if (g != NULL) - *g = dh->g; -} - -int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - /* If the fields p and g in d are NULL, the corresponding input - * parameters MUST be non-NULL. q may remain NULL. - */ - if ((dh->p == NULL && p == NULL) - || (dh->g == NULL && g == NULL)) - return 0; - - if (p != NULL) { - BN_free(dh->p); - dh->p = p; - } - if (q != NULL) { - BN_free(dh->q); - dh->q = q; - } - if (g != NULL) { - BN_free(dh->g); - dh->g = g; - } - - if (q != NULL) { - dh->length = BN_num_bits(q); - } - - return 1; -} - -void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) -{ - if (pub_key != NULL) - *pub_key = dh->pub_key; - if (priv_key != NULL) - *priv_key = dh->priv_key; -} - -int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) -{ - /* If the field pub_key in dh is NULL, the corresponding input - * parameters MUST be non-NULL. The priv_key field may - * be left NULL. - */ - if (dh->pub_key == NULL && pub_key == NULL) - return 0; - - if (pub_key != NULL) { - BN_free(dh->pub_key); - dh->pub_key = pub_key; - } - if (priv_key != NULL) { - BN_free(dh->priv_key); - dh->priv_key = priv_key; - } - - return 1; -} -#endif - -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL #ifndef DH_CHECK_Q_NOT_PRIME #define DH_CHECK_Q_NOT_PRIME 0x10 #endif diff --git a/src/_cffi_src/openssl/dsa.py b/src/_cffi_src/openssl/dsa.py index 938c18fcf1b1..3a290067bc5b 100644 --- a/src/_cffi_src/openssl/dsa.py +++ b/src/_cffi_src/openssl/dsa.py @@ -34,70 +34,4 @@ """ CUSTOMIZATIONS = """ -/* These functions were added in OpenSSL 1.1.0 */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -void DSA_get0_pqg(const DSA *d, - const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -{ - if (p != NULL) - *p = d->p; - if (q != NULL) - *q = d->q; - if (g != NULL) - *g = d->g; -} -int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - /* If the fields p, q and g in d are NULL, the corresponding input - * parameters MUST be non-NULL. - */ - if ((d->p == NULL && p == NULL) - || (d->q == NULL && q == NULL) - || (d->g == NULL && g == NULL)) - return 0; - - if (p != NULL) { - BN_free(d->p); - d->p = p; - } - if (q != NULL) { - BN_free(d->q); - d->q = q; - } - if (g != NULL) { - BN_free(d->g); - d->g = g; - } - - return 1; -} -void DSA_get0_key(const DSA *d, - const BIGNUM **pub_key, const BIGNUM **priv_key) -{ - if (pub_key != NULL) - *pub_key = d->pub_key; - if (priv_key != NULL) - *priv_key = d->priv_key; -} -int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) -{ - /* If the field pub_key in d is NULL, the corresponding input - * parameters MUST be non-NULL. The priv_key field may - * be left NULL. - */ - if (d->pub_key == NULL && pub_key == NULL) - return 0; - - if (pub_key != NULL) { - BN_free(d->pub_key); - d->pub_key = pub_key; - } - if (priv_key != NULL) { - BN_free(d->priv_key); - d->priv_key = priv_key; - } - - return 1; -} -#endif """ diff --git a/src/_cffi_src/openssl/evp.py b/src/_cffi_src/openssl/evp.py index d7ac93e603fc..bc8ad80ec7ea 100644 --- a/src/_cffi_src/openssl/evp.py +++ b/src/_cffi_src/openssl/evp.py @@ -176,20 +176,20 @@ } EVP_MD_CTX *Cryptography_EVP_MD_CTX_new(void) { -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL return EVP_MD_CTX_create(); #else return EVP_MD_CTX_new(); #endif } void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *ctx) { -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL EVP_MD_CTX_destroy(ctx); #else EVP_MD_CTX_free(ctx); #endif } -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 || defined(OPENSSL_NO_SCRYPT) +#if CRYPTOGRAPHY_IS_LIBRESSL || defined(OPENSSL_NO_SCRYPT) static const long Cryptography_HAS_SCRYPT = 0; int (*EVP_PBE_scrypt)(const char *, size_t, const unsigned char *, size_t, uint64_t, uint64_t, uint64_t, uint64_t, unsigned char *, @@ -198,7 +198,7 @@ static const long Cryptography_HAS_SCRYPT = 1; #endif -#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER +#if !CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint = 1; #else static const long Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint = 0; diff --git a/src/_cffi_src/openssl/hmac.py b/src/_cffi_src/openssl/hmac.py index 2bc70068ed6b..2e0e33ffe3b0 100644 --- a/src/_cffi_src/openssl/hmac.py +++ b/src/_cffi_src/openssl/hmac.py @@ -18,31 +18,9 @@ int HMAC_Final(HMAC_CTX *, unsigned char *, unsigned int *); int HMAC_CTX_copy(HMAC_CTX *, HMAC_CTX *); -HMAC_CTX *Cryptography_HMAC_CTX_new(void); -void Cryptography_HMAC_CTX_free(HMAC_CTX *ctx); +HMAC_CTX *HMAC_CTX_new(void); +void HMAC_CTX_free(HMAC_CTX *ctx); """ CUSTOMIZATIONS = """ -HMAC_CTX *Cryptography_HMAC_CTX_new(void) { -#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - return HMAC_CTX_new(); -#else - /* This uses OPENSSL_zalloc in 1.1.0, which is malloc + memset */ - HMAC_CTX *ctx = (HMAC_CTX *)OPENSSL_malloc(sizeof(HMAC_CTX)); - memset(ctx, 0, sizeof(HMAC_CTX)); - return ctx; -#endif -} - - -void Cryptography_HMAC_CTX_free(HMAC_CTX *ctx) { -#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - HMAC_CTX_free(ctx); -#else - if (ctx != NULL) { - HMAC_CTX_cleanup(ctx); - OPENSSL_free(ctx); - } -#endif -} """ diff --git a/src/_cffi_src/openssl/nid.py b/src/_cffi_src/openssl/nid.py index aecdc9c0f4ed..9ef88cdbbd41 100644 --- a/src/_cffi_src/openssl/nid.py +++ b/src/_cffi_src/openssl/nid.py @@ -9,8 +9,6 @@ """ TYPES = """ -static const int Cryptography_HAS_X25519; -static const int Cryptography_HAS_X448; static const int Cryptography_HAS_ED448; static const int Cryptography_HAS_ED25519; static const int Cryptography_HAS_POLY1305; @@ -33,24 +31,12 @@ """ CUSTOMIZATIONS = """ -#ifndef NID_X25519 -static const long Cryptography_HAS_X25519 = 0; -static const int NID_X25519 = 0; -#else -static const long Cryptography_HAS_X25519 = 1; -#endif #ifndef NID_ED25519 static const long Cryptography_HAS_ED25519 = 0; static const int NID_ED25519 = 0; #else static const long Cryptography_HAS_ED25519 = 1; #endif -#ifndef NID_X448 -static const long Cryptography_HAS_X448 = 0; -static const int NID_X448 = 0; -#else -static const long Cryptography_HAS_X448 = 1; -#endif #ifndef NID_ED448 static const long Cryptography_HAS_ED448 = 0; static const int NID_ED448 = 0; diff --git a/src/_cffi_src/openssl/ocsp.py b/src/_cffi_src/openssl/ocsp.py index f1a8bf617941..c3d034c2c48d 100644 --- a/src/_cffi_src/openssl/ocsp.py +++ b/src/_cffi_src/openssl/ocsp.py @@ -78,7 +78,7 @@ CUSTOMIZATIONS = """ #if ( \ - CRYPTOGRAPHY_OPENSSL_110_OR_GREATER && \ + !CRYPTOGRAPHY_IS_LIBRESSL && \ CRYPTOGRAPHY_OPENSSL_LESS_THAN_110J \ ) /* These structs come from ocsp_lcl.h and are needed to de-opaque the struct @@ -105,7 +105,7 @@ }; #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL /* These functions are all taken from ocsp_cl.c in OpenSSL 1.1.0 */ const OCSP_CERTID *OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *single) { @@ -147,7 +147,7 @@ #if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110J const X509_ALGOR *OCSP_resp_get0_tbs_sigalg(const OCSP_BASICRESP *bs) { -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL return bs->signatureAlgorithm; #else return &bs->signatureAlgorithm; @@ -156,7 +156,7 @@ const OCSP_RESPDATA *OCSP_resp_get0_respdata(const OCSP_BASICRESP *bs) { -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL return bs->tbsResponseData; #else return &bs->tbsResponseData; diff --git a/src/_cffi_src/openssl/rsa.py b/src/_cffi_src/openssl/rsa.py index 765498fbc9ef..d83dda283841 100644 --- a/src/_cffi_src/openssl/rsa.py +++ b/src/_cffi_src/openssl/rsa.py @@ -65,111 +65,4 @@ int (*EVP_PKEY_CTX_set0_rsa_oaep_label)(EVP_PKEY_CTX *, unsigned char *, int) = NULL; #endif - -/* These functions were added in OpenSSL 1.1.0 */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) -{ - /* If the fields n and e in r are NULL, the corresponding input - * parameters MUST be non-NULL for n and e. d may be - * left NULL (in case only the public key is used). - */ - if ((r->n == NULL && n == NULL) - || (r->e == NULL && e == NULL)) - return 0; - - if (n != NULL) { - BN_free(r->n); - r->n = n; - } - if (e != NULL) { - BN_free(r->e); - r->e = e; - } - if (d != NULL) { - BN_free(r->d); - r->d = d; - } - - return 1; -} - -int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) -{ - /* If the fields p and q in r are NULL, the corresponding input - * parameters MUST be non-NULL. - */ - if ((r->p == NULL && p == NULL) - || (r->q == NULL && q == NULL)) - return 0; - - if (p != NULL) { - BN_free(r->p); - r->p = p; - } - if (q != NULL) { - BN_free(r->q); - r->q = q; - } - - return 1; -} - -int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) -{ - /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input - * parameters MUST be non-NULL. - */ - if ((r->dmp1 == NULL && dmp1 == NULL) - || (r->dmq1 == NULL && dmq1 == NULL) - || (r->iqmp == NULL && iqmp == NULL)) - return 0; - - if (dmp1 != NULL) { - BN_free(r->dmp1); - r->dmp1 = dmp1; - } - if (dmq1 != NULL) { - BN_free(r->dmq1); - r->dmq1 = dmq1; - } - if (iqmp != NULL) { - BN_free(r->iqmp); - r->iqmp = iqmp; - } - - return 1; -} - -void RSA_get0_key(const RSA *r, - const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) -{ - if (n != NULL) - *n = r->n; - if (e != NULL) - *e = r->e; - if (d != NULL) - *d = r->d; -} - -void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) -{ - if (p != NULL) - *p = r->p; - if (q != NULL) - *q = r->q; -} - -void RSA_get0_crt_params(const RSA *r, - const BIGNUM **dmp1, const BIGNUM **dmq1, - const BIGNUM **iqmp) -{ - if (dmp1 != NULL) - *dmp1 = r->dmp1; - if (dmq1 != NULL) - *dmq1 = r->dmq1; - if (iqmp != NULL) - *iqmp = r->iqmp; -} -#endif """ diff --git a/src/_cffi_src/openssl/ssl.py b/src/_cffi_src/openssl/ssl.py index c38e309a1835..93b8eac8a25b 100644 --- a/src/_cffi_src/openssl/ssl.py +++ b/src/_cffi_src/openssl/ssl.py @@ -26,7 +26,6 @@ static const long Cryptography_HAS_DTLS; static const long Cryptography_HAS_SIGALGS; static const long Cryptography_HAS_PSK; -static const long Cryptography_HAS_CIPHER_DETAILS; static const long Cryptography_HAS_VERIFIED_CHAIN; static const long Cryptography_HAS_KEYLOG; @@ -501,7 +500,7 @@ """ CUSTOMIZATIONS = """ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_VERIFIED_CHAIN = 0; Cryptography_STACK_OF_X509 *(*SSL_get0_verified_chain)(const SSL *) = NULL; #else @@ -521,58 +520,6 @@ static const long Cryptography_HAS_KEYLOG = 1; #endif -/* Added in 1.1.0 in the great opaquing, but we need to define it for older - OpenSSLs. Such is our burden. */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -/* from ssl/ssl_lib.c */ -size_t SSL_get_client_random(const SSL *ssl, unsigned char *out, size_t outlen) -{ - if (outlen == 0) - return sizeof(ssl->s3->client_random); - if (outlen > sizeof(ssl->s3->client_random)) - outlen = sizeof(ssl->s3->client_random); - memcpy(out, ssl->s3->client_random, outlen); - return outlen; -} -/* Added in 1.1.0 as well */ -/* from ssl/ssl_lib.c */ -size_t SSL_get_server_random(const SSL *ssl, unsigned char *out, size_t outlen) -{ - if (outlen == 0) - return sizeof(ssl->s3->server_random); - if (outlen > sizeof(ssl->s3->server_random)) - outlen = sizeof(ssl->s3->server_random); - memcpy(out, ssl->s3->server_random, outlen); - return outlen; -} -/* Added in 1.1.0 as well */ -/* from ssl/ssl_lib.c */ -size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, - unsigned char *out, size_t outlen) -{ - if (session->master_key_length < 0) { - /* Should never happen */ - return 0; - } - if (outlen == 0) - return session->master_key_length; - if (outlen > (size_t)session->master_key_length) - outlen = session->master_key_length; - memcpy(out, session->master_key, outlen); - return outlen; -} -/* from ssl/ssl_sess.c */ -int SSL_SESSION_has_ticket(const SSL_SESSION *s) -{ - return (s->tlsext_ticklen > 0) ? 1 : 0; -} -/* from ssl/ssl_sess.c */ -unsigned long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s) -{ - return s->tlsext_tick_lifetime_hint; -} -#endif - static const long Cryptography_HAS_SECURE_RENEGOTIATION = 1; /* Cryptography now compiles out all SSLv2 bindings. This exists to allow @@ -616,7 +563,7 @@ /* in OpenSSL 1.1.0 the SSL_ST values were renamed to TLS_ST and several were removed */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_SSL_ST = 1; #else static const long Cryptography_HAS_SSL_ST = 0; @@ -625,7 +572,7 @@ static const long SSL_ST_INIT = 0; static const long SSL_ST_RENEGOTIATE = 0; #endif -#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER +#if !CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_TLS_ST = 1; #else static const long Cryptography_HAS_TLS_ST = 0; @@ -729,17 +676,6 @@ SRTP_PROTECTION_PROFILE * (*SSL_get_selected_srtp_profile)(SSL *) = NULL; #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -int (*SSL_CIPHER_is_aead)(const SSL_CIPHER *) = NULL; -int (*SSL_CIPHER_get_cipher_nid)(const SSL_CIPHER *) = NULL; -int (*SSL_CIPHER_get_digest_nid)(const SSL_CIPHER *) = NULL; -int (*SSL_CIPHER_get_kx_nid)(const SSL_CIPHER *) = NULL; -int (*SSL_CIPHER_get_auth_nid)(const SSL_CIPHER *) = NULL; -static const long Cryptography_HAS_CIPHER_DETAILS = 0; -#else -static const long Cryptography_HAS_CIPHER_DETAILS = 1; -#endif - #if CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 static const long Cryptography_HAS_TLSv1_3 = 0; static const long SSL_OP_NO_TLSv1_3 = 0; diff --git a/src/_cffi_src/openssl/x509.py b/src/_cffi_src/openssl/x509.py index b88daa1f213d..24946ea48d07 100644 --- a/src/_cffi_src/openssl/x509.py +++ b/src/_cffi_src/openssl/x509.py @@ -288,7 +288,7 @@ } /* Added in 1.1.0 but we need it in all versions now due to the great opaquing. */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL int i2d_re_X509_REQ_tbs(X509_REQ *req, unsigned char **pp) { req->req_info->enc.modified = 1; @@ -298,47 +298,5 @@ crl->crl->enc.modified = 1; return i2d_X509_CRL_INFO(crl->crl, pp); } - -#if !CRYPTOGRAPHY_IS_LIBRESSL -int X509_up_ref(X509 *x) { - return CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); -} - -const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x) -{ - return x->cert_info->signature; -} - -/* from x509/x509_req.c */ -void X509_REQ_get0_signature(const X509_REQ *req, const ASN1_BIT_STRING **psig, - const X509_ALGOR **palg) -{ - if (psig != NULL) - *psig = req->signature; - if (palg != NULL) - *palg = req->sig_alg; -} -void X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig, - const X509_ALGOR **palg) -{ - if (psig != NULL) - *psig = crl->signature; - if (palg != NULL) - *palg = crl->sig_alg; -} -const ASN1_TIME *X509_REVOKED_get0_revocationDate(const X509_REVOKED *x) -{ - return x->revocationDate; -} -const ASN1_INTEGER *X509_REVOKED_get0_serialNumber(const X509_REVOKED *x) -{ - return x->serialNumber; -} - -#define X509_set1_notBefore X509_set_notBefore -#define X509_set1_notAfter X509_set_notAfter -#define X509_getm_notAfter X509_get_notAfter -#define X509_getm_notBefore X509_get_notBefore -#endif #endif """ diff --git a/src/_cffi_src/openssl/x509_vfy.py b/src/_cffi_src/openssl/x509_vfy.py index d2bc5f4ec6e1..ba3d3dbb1421 100644 --- a/src/_cffi_src/openssl/x509_vfy.py +++ b/src/_cffi_src/openssl/x509_vfy.py @@ -234,7 +234,7 @@ static const long X509_V_FLAG_SUITEB_128_LOS = 0; #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 || CRYPTOGRAPHY_IS_LIBRESSL +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_110_VERIFICATION_PARAMS = 0; #ifndef X509_CHECK_FLAG_NEVER_CHECK_SUBJECT static const long X509_CHECK_FLAG_NEVER_CHECK_SUBJECT = 0; @@ -243,29 +243,7 @@ static const long Cryptography_HAS_110_VERIFICATION_PARAMS = 1; #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -Cryptography_STACK_OF_X509_OBJECT *X509_STORE_get0_objects(X509_STORE *ctx) { - return ctx->objs; -} -X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *store) { - return store->param; -} -int X509_OBJECT_get_type(const X509_OBJECT *x) { - return x->type; -} - -/* from x509/x509_vfy.c */ -X509 *X509_STORE_CTX_get0_cert(X509_STORE_CTX *ctx) -{ - return ctx->cert; -} - -X509 *X509_OBJECT_get0_X509(X509_OBJECT *x) { - return x->data.x509; -} -#endif - -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_X509_STORE_CTX_GET_ISSUER = 0; typedef void *X509_STORE_CTX_get_issuer_fn; X509_STORE_CTX_get_issuer_fn (*X509_STORE_get_get_issuer)(X509_STORE *) = NULL; diff --git a/src/_cffi_src/openssl/x509name.py b/src/_cffi_src/openssl/x509name.py index f88c8b063b33..c618899953c9 100644 --- a/src/_cffi_src/openssl/x509name.py +++ b/src/_cffi_src/openssl/x509name.py @@ -75,7 +75,7 @@ """ CUSTOMIZATIONS = """ -#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER +#if !CRYPTOGRAPHY_IS_LIBRESSL int Cryptography_X509_NAME_ENTRY_set(X509_NAME_ENTRY *ne) { return X509_NAME_ENTRY_set(ne); } diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index b7757e333d9e..5f5c5795bd33 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1660,14 +1660,6 @@ def _ec_key_new_by_curve(self, curve): def _ec_key_new_by_curve_nid(self, curve_nid): ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) self.openssl_assert(ec_cdata != self._ffi.NULL) - # Setting the ASN.1 flag to OPENSSL_EC_NAMED_CURVE is - # only necessary on OpenSSL 1.0.2t/u. Once we drop support for 1.0.2 - # we can remove this as it's done automatically when getting an EC_KEY - # from new_by_curve_name - # CRYPTOGRAPHY_OPENSSL_102U_OR_GREATER - self._lib.EC_KEY_set_asn1_flag( - ec_cdata, backend._lib.OPENSSL_EC_NAMED_CURVE - ) return self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) def load_der_ocsp_request(self, data): @@ -2334,7 +2326,7 @@ def x25519_generate_key(self): def x25519_supported(self): if self._fips_enabled: return False - return self._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER + return not self._lib.CRYPTOGRAPHY_IS_LIBRESSL def x448_load_public_bytes(self, data): if len(data) != 56: diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py index bf61bcf16b20..05d32baba662 100644 --- a/src/cryptography/hazmat/backends/openssl/ec.py +++ b/src/cryptography/hazmat/backends/openssl/ec.py @@ -40,18 +40,18 @@ def _ec_key_curve_sn(backend, ec_key): # an error for now. if nid == backend._lib.NID_undef: raise NotImplementedError( - "ECDSA keys with unnamed curves are unsupported " "at this time" + "ECDSA keys with unnamed curves are unsupported at this time" ) # This is like the above check, but it also catches the case where you # explicitly encoded a curve with the same parameters as a named curve. # Don't do that. if ( - backend._lib.CRYPTOGRAPHY_OPENSSL_102U_OR_GREATER + not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL and backend._lib.EC_GROUP_get_asn1_flag(group) == 0 ): raise NotImplementedError( - "ECDSA keys with unnamed curves are unsupported " "at this time" + "ECDSA keys with unnamed curves are unsupported at this time" ) curve_name = backend._lib.OBJ_nid2sn(nid) diff --git a/src/cryptography/hazmat/backends/openssl/hmac.py b/src/cryptography/hazmat/backends/openssl/hmac.py index 5024223b219b..1cc9d99fec7b 100644 --- a/src/cryptography/hazmat/backends/openssl/hmac.py +++ b/src/cryptography/hazmat/backends/openssl/hmac.py @@ -21,11 +21,9 @@ def __init__(self, backend, key, algorithm, ctx=None): self._backend = backend if ctx is None: - ctx = self._backend._lib.Cryptography_HMAC_CTX_new() + ctx = self._backend._lib.HMAC_CTX_new() self._backend.openssl_assert(ctx != self._backend._ffi.NULL) - ctx = self._backend._ffi.gc( - ctx, self._backend._lib.Cryptography_HMAC_CTX_free - ) + ctx = self._backend._ffi.gc(ctx, self._backend._lib.HMAC_CTX_free) evp_md = self._backend._evp_md_from_algorithm(algorithm) if evp_md == self._backend._ffi.NULL: raise UnsupportedAlgorithm( @@ -46,10 +44,10 @@ def __init__(self, backend, key, algorithm, ctx=None): algorithm = utils.read_only_property("_algorithm") def copy(self): - copied_ctx = self._backend._lib.Cryptography_HMAC_CTX_new() + copied_ctx = self._backend._lib.HMAC_CTX_new() self._backend.openssl_assert(copied_ctx != self._backend._ffi.NULL) copied_ctx = self._backend._ffi.gc( - copied_ctx, self._backend._lib.Cryptography_HMAC_CTX_free + copied_ctx, self._backend._lib.HMAC_CTX_free ) res = self._backend._lib.HMAC_CTX_copy(copied_ctx, self._ctx) self._backend.openssl_assert(res != 0) diff --git a/src/cryptography/hazmat/bindings/openssl/_conditional.py b/src/cryptography/hazmat/bindings/openssl/_conditional.py index cdc18eab6848..aeca166b2310 100644 --- a/src/cryptography/hazmat/bindings/openssl/_conditional.py +++ b/src/cryptography/hazmat/bindings/openssl/_conditional.py @@ -126,20 +126,6 @@ def cryptography_has_x509_store_ctx_get_issuer(): ] -def cryptography_has_x25519(): - return [ - "EVP_PKEY_X25519", - "NID_X25519", - ] - - -def cryptography_has_x448(): - return [ - "EVP_PKEY_X448", - "NID_X448", - ] - - def cryptography_has_ed448(): return [ "EVP_PKEY_ED448", @@ -217,16 +203,6 @@ def cryptography_has_openssl_cleanup(): ] -def cryptography_has_cipher_details(): - return [ - "SSL_CIPHER_is_aead", - "SSL_CIPHER_get_cipher_nid", - "SSL_CIPHER_get_digest_nid", - "SSL_CIPHER_get_kx_nid", - "SSL_CIPHER_get_auth_nid", - ] - - def cryptography_has_tlsv13(): return [ "SSL_OP_NO_TLSv1_3", @@ -316,8 +292,6 @@ def cryptography_has_srtp(): "Cryptography_HAS_X509_STORE_CTX_GET_ISSUER": ( cryptography_has_x509_store_ctx_get_issuer ), - "Cryptography_HAS_X25519": cryptography_has_x25519, - "Cryptography_HAS_X448": cryptography_has_x448, "Cryptography_HAS_ED448": cryptography_has_ed448, "Cryptography_HAS_ED25519": cryptography_has_ed25519, "Cryptography_HAS_POLY1305": cryptography_has_poly1305, @@ -332,7 +306,6 @@ def cryptography_has_srtp(): "Cryptography_HAS_PSK": cryptography_has_psk, "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext, "Cryptography_HAS_OPENSSL_CLEANUP": cryptography_has_openssl_cleanup, - "Cryptography_HAS_CIPHER_DETAILS": cryptography_has_cipher_details, "Cryptography_HAS_TLSv1_3": cryptography_has_tlsv13, "Cryptography_HAS_KEYLOG": cryptography_has_keylog, "Cryptography_HAS_RAW_KEY": cryptography_has_raw_key, diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index f6bf93729118..5fd3b0de9656 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -5,10 +5,8 @@ from __future__ import absolute_import, division, print_function import collections -import os import threading import types -import warnings import cryptography from cryptography import utils @@ -166,29 +164,6 @@ def init_static_locks(cls): _openssl_assert(cls.lib, res == 1) -def _verify_openssl_version(lib): - if ( - lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 - and not lib.CRYPTOGRAPHY_IS_LIBRESSL - ): - if os.environ.get("CRYPTOGRAPHY_ALLOW_OPENSSL_102"): - warnings.warn( - "OpenSSL version 1.0.2 is no longer supported by the OpenSSL " - "project, please upgrade. The next version of cryptography " - "will completely remove support for it.", - utils.CryptographyDeprecationWarning, - ) - else: - raise RuntimeError( - "You are linking against OpenSSL 1.0.2, which is no longer " - "supported by the OpenSSL project. To use this version of " - "cryptography you need to upgrade to a newer version of " - "OpenSSL. For this version only you can also set the " - "environment variable CRYPTOGRAPHY_ALLOW_OPENSSL_102 to " - "allow OpenSSL 1.0.2." - ) - - def _verify_package_version(version): # Occasionally we run into situations where the version of the Python # package does not match the version of the shared object that is loaded. @@ -218,5 +193,3 @@ def _verify_package_version(version): # condition registering the OpenSSL locks. On Python 3.4+ the import lock # is per module so this approach will not work. Binding.init_static_locks() - -_verify_openssl_version(Binding.lib) diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 2f7e7bebfd0c..e4bf7f97c6f5 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -590,7 +590,7 @@ def test_numeric_string_x509_name_entry(self): x509.load_der_x509_certificate, backend, ) - if backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I: + if backend._lib.CRYPTOGRAPHY_IS_LIBRESSL: with pytest.raises(ValueError) as exc: cert.subject diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py index ecee34091dc7..069452cbb091 100644 --- a/tests/hazmat/bindings/test_openssl.py +++ b/tests/hazmat/bindings/test_openssl.py @@ -4,8 +4,6 @@ from __future__ import absolute_import, division, print_function -import pretend - import pytest from cryptography.exceptions import InternalError @@ -13,7 +11,6 @@ Binding, _consume_errors, _openssl_assert, - _verify_openssl_version, _verify_package_version, ) @@ -30,7 +27,7 @@ def test_crypto_lock_init(self): b.init_static_locks() lock_cb = b.lib.CRYPTO_get_locking_callback() - if b.lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER: + if not b.lib.CRYPTOGRAPHY_IS_LIBRESSL: assert lock_cb == b.ffi.NULL assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 0 else: @@ -88,7 +85,7 @@ def test_ssl_mode(self): def test_conditional_removal(self): b = Binding() - if b.lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER: + if not b.lib.CRYPTOGRAPHY_IS_LIBRESSL: assert b.lib.TLS_ST_OK else: with pytest.raises(AttributeError): @@ -128,12 +125,3 @@ def test_check_startup_errors_are_allowed(self): def test_version_mismatch(self): with pytest.raises(ImportError): _verify_package_version("nottherightversion") - - def test_verify_openssl_version(self, monkeypatch): - monkeypatch.delenv("CRYPTOGRAPHY_ALLOW_OPENSSL_102", raising=False) - lib = pretend.stub( - CRYPTOGRAPHY_OPENSSL_LESS_THAN_110=True, - CRYPTOGRAPHY_IS_LIBRESSL=False, - ) - with pytest.raises(RuntimeError): - _verify_openssl_version(lib) diff --git a/tests/wycheproof/test_rsa.py b/tests/wycheproof/test_rsa.py index 1262b58853d3..926bb44e999f 100644 --- a/tests/wycheproof/test_rsa.py +++ b/tests/wycheproof/test_rsa.py @@ -35,12 +35,7 @@ def should_verify(backend, wycheproof): return True if wycheproof.acceptable: - if ( - backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - or backend._lib.CRYPTOGRAPHY_IS_LIBRESSL - ) and wycheproof.has_flag("MissingNull"): - return False - return True + return not wycheproof.has_flag("MissingNull") return False @@ -165,16 +160,6 @@ def test_rsa_pss_signature(backend, wycheproof): @pytest.mark.requires_backend_interface(interface=RSABackend) -@pytest.mark.supported( - only_if=lambda backend: ( - backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - or backend._lib.CRYPTOGRAPHY_IS_LIBRESSL - ), - skip_message=( - "A handful of these tests fail on OpenSSL 1.0.2 and since upstream " - "isn't maintaining it, they'll never be fixed." - ), -) @pytest.mark.wycheproof_tests( "rsa_oaep_2048_sha1_mgf1sha1_test.json", "rsa_oaep_2048_sha224_mgf1sha1_test.json", diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index 11c80816cff7..f4d93389ac02 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -1820,10 +1820,6 @@ def read_next_rdn_value_tag(reader): if ( # This only works correctly in OpenSSL 1.1.0f+ and 1.0.2l+ backend._lib.CRYPTOGRAPHY_OPENSSL_110F_OR_GREATER - or ( - backend._lib.CRYPTOGRAPHY_OPENSSL_102L_OR_GREATER - and not backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - ) ): assert read_next_rdn_value_tag(subject) == PRINTABLE_STRING assert read_next_rdn_value_tag(issuer) == PRINTABLE_STRING diff --git a/tests/x509/test_x509_ext.py b/tests/x509/test_x509_ext.py index 2cd216fb688a..429169a45ebc 100644 --- a/tests/x509/test_x509_ext.py +++ b/tests/x509/test_x509_ext.py @@ -5679,10 +5679,8 @@ def test_simple(self, backend): ) @pytest.mark.supported( - only_if=lambda backend: ( - not backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - ), - skip_message="Requires OpenSSL < 1.1.0", + only_if=lambda backend: backend._lib.CRYPTOGRAPHY_IS_LIBRESSL, + skip_message="Requires LibreSSL", ) def test_skips_scts_if_unsupported(self, backend): cert = _load_cert( diff --git a/tox.ini b/tox.ini index e94d3c1e0753..bc5de1ad9953 100644 --- a/tox.ini +++ b/tox.ini @@ -13,8 +13,6 @@ deps = ./vectors randomorder: pytest-randomly passenv = ARCHFLAGS LDFLAGS CFLAGS INCLUDE LIB LD_LIBRARY_PATH USERNAME PYTHONIOENCODING OPENSSL_FORCE_FIPS_MODE -setenv = - CRYPTOGRAPHY_ALLOW_OPENSSL_102=1 commands = pip list # We use parallel mode and then combine here so that coverage.py will take From 5a7dfb705f086f373093021add6a3540196abb10 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 26 Oct 2020 06:33:58 -0700 Subject: [PATCH 41/99] Revert to upload-artifact v1 for manylinux (#5514) --- .github/workflows/wheel-builder.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wheel-builder.yml b/.github/workflows/wheel-builder.yml index 74012f09b53a..0534513ac3a8 100644 --- a/.github/workflows/wheel-builder.yml +++ b/.github/workflows/wheel-builder.yml @@ -47,7 +47,7 @@ jobs: .venv/bin/python -c "from cryptography.hazmat.backends.openssl.backend import backend;print('Loaded: ' + backend.openssl_version_text());print('Linked Against: ' + backend._ffi.string(backend._lib.OPENSSL_VERSION_TEXT).decode('ascii'))" - run: mkdir cryptography-wheelhouse - run: mv wheelhouse/cryptography*.whl cryptography-wheelhouse/ - - uses: actions/upload-artifact@v2.2.0 + - uses: actions/upload-artifact@v1 with: name: "cryptography-${{ github.event.inputs.version }}-${{ matrix.MANYLINUX.NAME }}-${{ matrix.PYTHON }}" path: cryptography-wheelhouse/ From b187b80421d535ef53362ec9f42a2aa5422c0add Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 26 Oct 2020 10:55:31 -0400 Subject: [PATCH 42/99] Simplify a number of branches around libressl (#5515) --- src/_cffi_src/openssl/evp.py | 21 +++++++------------ src/_cffi_src/openssl/x509name.py | 11 +--------- .../hazmat/backends/openssl/decode_asn1.py | 2 +- .../hazmat/backends/openssl/ed25519.py | 8 +++---- .../hazmat/backends/openssl/ed448.py | 8 +++---- .../hazmat/backends/openssl/hashes.py | 8 +++---- .../hazmat/backends/openssl/poly1305.py | 4 ++-- 7 files changed, 23 insertions(+), 39 deletions(-) diff --git a/src/_cffi_src/openssl/evp.py b/src/_cffi_src/openssl/evp.py index bc8ad80ec7ea..5b6e35cf9dd6 100644 --- a/src/_cffi_src/openssl/evp.py +++ b/src/_cffi_src/openssl/evp.py @@ -120,11 +120,12 @@ int EVP_PKEY_id(const EVP_PKEY *); int Cryptography_EVP_PKEY_id(const EVP_PKEY *); -/* in 1.1.0 _create and _destroy were renamed to _new and _free. The following - two functions wrap both the old and new functions so we can call them - without worrying about what OpenSSL we're running against. */ +EVP_MD_CTX *EVP_MD_CTX_new(void); +void EVP_MD_CTX_free(EVP_MD_CTX *); +/* Backwards compat aliases for pyOpenSSL */ EVP_MD_CTX *Cryptography_EVP_MD_CTX_new(void); void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *); + /* Added in 1.1.1 */ int EVP_DigestSign(EVP_MD_CTX *, unsigned char *, size_t *, const unsigned char *, size_t); @@ -174,21 +175,13 @@ int Cryptography_EVP_PKEY_id(const EVP_PKEY *key) { return EVP_PKEY_id(key); } - EVP_MD_CTX *Cryptography_EVP_MD_CTX_new(void) { -#if CRYPTOGRAPHY_IS_LIBRESSL - return EVP_MD_CTX_create(); -#else return EVP_MD_CTX_new(); -#endif } -void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *ctx) { -#if CRYPTOGRAPHY_IS_LIBRESSL - EVP_MD_CTX_destroy(ctx); -#else - EVP_MD_CTX_free(ctx); -#endif +void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *md) { + EVP_MD_CTX_free(md); } + #if CRYPTOGRAPHY_IS_LIBRESSL || defined(OPENSSL_NO_SCRYPT) static const long Cryptography_HAS_SCRYPT = 0; int (*EVP_PBE_scrypt)(const char *, size_t, const unsigned char *, size_t, diff --git a/src/_cffi_src/openssl/x509name.py b/src/_cffi_src/openssl/x509name.py index c618899953c9..1fbe26aa7432 100644 --- a/src/_cffi_src/openssl/x509name.py +++ b/src/_cffi_src/openssl/x509name.py @@ -35,7 +35,7 @@ int X509_NAME_get_index_by_NID(X509_NAME *, int, int); int X509_NAME_cmp(const X509_NAME *, const X509_NAME *); X509_NAME *X509_NAME_dup(X509_NAME *); -int Cryptography_X509_NAME_ENTRY_set(X509_NAME_ENTRY *); +int X509_NAME_ENTRY_set(X509_NAME_ENTRY *); /* These became const X509_NAME * in 1.1.0 */ int X509_NAME_entry_count(X509_NAME *); X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *, int); @@ -75,13 +75,4 @@ """ CUSTOMIZATIONS = """ -#if !CRYPTOGRAPHY_IS_LIBRESSL -int Cryptography_X509_NAME_ENTRY_set(X509_NAME_ENTRY *ne) { - return X509_NAME_ENTRY_set(ne); -} -#else -int Cryptography_X509_NAME_ENTRY_set(X509_NAME_ENTRY *ne) { - return ne->set; -} -#endif """ diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py index 279b00ca5c10..cc9b8c0e34d9 100644 --- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py @@ -64,7 +64,7 @@ def _decode_x509_name(backend, x509_name): for x in range(count): entry = backend._lib.X509_NAME_get_entry(x509_name, x) attribute = _decode_x509_name_entry(backend, entry) - set_id = backend._lib.Cryptography_X509_NAME_ENTRY_set(entry) + set_id = backend._lib.X509_NAME_ENTRY_set(entry) if set_id != prev_set_id: attributes.append({attribute}) else: diff --git a/src/cryptography/hazmat/backends/openssl/ed25519.py b/src/cryptography/hazmat/backends/openssl/ed25519.py index 75653373b3cf..13bec3af1094 100644 --- a/src/cryptography/hazmat/backends/openssl/ed25519.py +++ b/src/cryptography/hazmat/backends/openssl/ed25519.py @@ -50,10 +50,10 @@ def _raw_public_bytes(self): return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:] def verify(self, signature, data): - evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_DigestVerifyInit( evp_md_ctx, @@ -89,10 +89,10 @@ def public_key(self): return self._backend.ed25519_load_public_bytes(public_bytes) def sign(self, data): - evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_DigestSignInit( evp_md_ctx, diff --git a/src/cryptography/hazmat/backends/openssl/ed448.py b/src/cryptography/hazmat/backends/openssl/ed448.py index 4a8dab1a8115..6512770e5b76 100644 --- a/src/cryptography/hazmat/backends/openssl/ed448.py +++ b/src/cryptography/hazmat/backends/openssl/ed448.py @@ -51,10 +51,10 @@ def _raw_public_bytes(self): return self._backend._ffi.buffer(buf, _ED448_KEY_SIZE)[:] def verify(self, signature, data): - evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_DigestVerifyInit( evp_md_ctx, @@ -90,10 +90,10 @@ def public_key(self): return self._backend.ed448_load_public_bytes(public_bytes) def sign(self, data): - evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_DigestSignInit( evp_md_ctx, diff --git a/src/cryptography/hazmat/backends/openssl/hashes.py b/src/cryptography/hazmat/backends/openssl/hashes.py index 44033993e166..764dce0ede60 100644 --- a/src/cryptography/hazmat/backends/openssl/hashes.py +++ b/src/cryptography/hazmat/backends/openssl/hashes.py @@ -18,9 +18,9 @@ def __init__(self, backend, algorithm, ctx=None): self._backend = backend if ctx is None: - ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + ctx = self._backend._lib.EVP_MD_CTX_new() ctx = self._backend._ffi.gc( - ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + ctx, self._backend._lib.EVP_MD_CTX_free ) evp_md = self._backend._evp_md_from_algorithm(algorithm) if evp_md == self._backend._ffi.NULL: @@ -40,9 +40,9 @@ def __init__(self, backend, algorithm, ctx=None): algorithm = utils.read_only_property("_algorithm") def copy(self): - copied_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + copied_ctx = self._backend._lib.EVP_MD_CTX_new() copied_ctx = self._backend._ffi.gc( - copied_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + copied_ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx) self._backend.openssl_assert(res != 0) diff --git a/src/cryptography/hazmat/backends/openssl/poly1305.py b/src/cryptography/hazmat/backends/openssl/poly1305.py index 17493ca60ce8..5699918b1726 100644 --- a/src/cryptography/hazmat/backends/openssl/poly1305.py +++ b/src/cryptography/hazmat/backends/openssl/poly1305.py @@ -30,10 +30,10 @@ def __init__(self, backend, key): self._evp_pkey = self._backend._ffi.gc( evp_pkey, self._backend._lib.EVP_PKEY_free ) - ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + ctx = self._backend._lib.EVP_MD_CTX_new() self._backend.openssl_assert(ctx != self._backend._ffi.NULL) self._ctx = self._backend._ffi.gc( - ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_DigestSignInit( self._ctx, From 956e0965fdc59641227eb9a9df2763a60959a398 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 26 Oct 2020 17:58:35 -0400 Subject: [PATCH 43/99] fixes #5513 update comment on why we can't have nice things (#5517) --- src/_cffi_src/build_openssl.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py index f8f41847926d..4380c3396909 100644 --- a/src/_cffi_src/build_openssl.py +++ b/src/_cffi_src/build_openssl.py @@ -51,10 +51,9 @@ def _extra_compile_args(platform): We set -Wconversion args here so that we only do Wconversion checks on the code we're compiling and not on cffi itself (as passing -Wconversion in CFLAGS would do). We set no error on sign conversion because some - function signatures in OpenSSL have changed from long -> unsigned long - in the past. Since that isn't a precision issue we don't care. - When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 we can - revisit this. + function signatures in LibreSSL differ from OpenSSL have changed on long + vs. unsigned long in the past. Since that isn't a precision issue we don't + care. """ # make sure the compiler used supports the flags to be added is_gcc = False @@ -117,13 +116,6 @@ def _extra_compile_args(platform): "callbacks", ], libraries=_get_openssl_libraries(sys.platform), - # These args are passed here so that we only do Wconversion checks on the - # code we're compiling and not on cffi itself (as passing -Wconversion in - # CFLAGS would do). We set no error on sign convesrion because some - # function signatures in OpenSSL have changed from long -> unsigned long - # in the past. Since that isn't a precision issue we don't care. - # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 we can - # revisit this. extra_compile_args=_extra_compile_args(sys.platform), extra_link_args=extra_link_args(compiler_type()), ) From b0a3d89e0f69d6e460a4ae65a57ea2c721f9370b Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 26 Oct 2020 23:43:33 -0400 Subject: [PATCH 44/99] Remove some dead constants (#5518) --- src/_cffi_src/openssl/rand.py | 3 --- src/_cffi_src/openssl/rsa.py | 3 --- src/_cffi_src/openssl/ssl.py | 17 ----------------- 3 files changed, 23 deletions(-) diff --git a/src/_cffi_src/openssl/rand.py b/src/_cffi_src/openssl/rand.py index c0cd68365960..1bc2ec0bc386 100644 --- a/src/_cffi_src/openssl/rand.py +++ b/src/_cffi_src/openssl/rand.py @@ -10,8 +10,6 @@ TYPES = """ typedef ... RAND_METHOD; - -static const long Cryptography_HAS_EGD; """ FUNCTIONS = """ @@ -27,5 +25,4 @@ """ CUSTOMIZATIONS = """ -static const long Cryptography_HAS_EGD = 0; """ diff --git a/src/_cffi_src/openssl/rsa.py b/src/_cffi_src/openssl/rsa.py index d83dda283841..9298226bb223 100644 --- a/src/_cffi_src/openssl/rsa.py +++ b/src/_cffi_src/openssl/rsa.py @@ -17,7 +17,6 @@ static const int RSA_PKCS1_PSS_PADDING; static const int RSA_F4; -static const int Cryptography_HAS_PSS_PADDING; static const int Cryptography_HAS_RSA_OAEP_MD; static const int Cryptography_HAS_RSA_OAEP_LABEL; """ @@ -49,8 +48,6 @@ """ CUSTOMIZATIONS = """ -static const long Cryptography_HAS_PSS_PADDING = 1; - #if defined(EVP_PKEY_CTX_set_rsa_oaep_md) static const long Cryptography_HAS_RSA_OAEP_MD = 1; #else diff --git a/src/_cffi_src/openssl/ssl.py b/src/_cffi_src/openssl/ssl.py index 93b8eac8a25b..ff960f5775ad 100644 --- a/src/_cffi_src/openssl/ssl.py +++ b/src/_cffi_src/openssl/ssl.py @@ -13,15 +13,11 @@ TYPES = """ static const long Cryptography_HAS_SSL_ST; static const long Cryptography_HAS_TLS_ST; -static const long Cryptography_HAS_SSL2; static const long Cryptography_HAS_SSL3_METHOD; static const long Cryptography_HAS_TLSv1_1; static const long Cryptography_HAS_TLSv1_2; static const long Cryptography_HAS_TLSv1_3; static const long Cryptography_HAS_SECURE_RENEGOTIATION; -static const long Cryptography_HAS_TLSEXT_STATUS_REQ_CB; -static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP; -static const long Cryptography_HAS_TLSEXT_STATUS_REQ_TYPE; static const long Cryptography_HAS_SSL_CTX_CLEAR_OPTIONS; static const long Cryptography_HAS_DTLS; static const long Cryptography_HAS_SIGALGS; @@ -29,9 +25,6 @@ static const long Cryptography_HAS_VERIFIED_CHAIN; static const long Cryptography_HAS_KEYLOG; -/* Internally invented symbol to tell us if SNI is supported */ -static const long Cryptography_HAS_TLSEXT_HOSTNAME; - /* Internally invented symbol to tell us if SSL_MODE_RELEASE_BUFFERS is * supported */ @@ -522,12 +515,6 @@ static const long Cryptography_HAS_SECURE_RENEGOTIATION = 1; -/* Cryptography now compiles out all SSLv2 bindings. This exists to allow - * clients that use it to check for SSLv2 support to keep functioning as - * expected. - */ -static const long Cryptography_HAS_SSL2 = 0; - #ifdef OPENSSL_NO_SSL3_METHOD static const long Cryptography_HAS_SSL3_METHOD = 0; SSL_METHOD* (*SSLv3_method)(void) = NULL; @@ -537,10 +524,6 @@ static const long Cryptography_HAS_SSL3_METHOD = 1; #endif -static const long Cryptography_HAS_TLSEXT_HOSTNAME = 1; -static const long Cryptography_HAS_TLSEXT_STATUS_REQ_CB = 1; -static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP = 1; -static const long Cryptography_HAS_TLSEXT_STATUS_REQ_TYPE = 1; static const long Cryptography_HAS_RELEASE_BUFFERS = 1; static const long Cryptography_HAS_OP_NO_COMPRESSION = 1; static const long Cryptography_HAS_TLSv1_1 = 1; From 48211dd71707473c816cc043f80207cd20d2ff0f Mon Sep 17 00:00:00 2001 From: lukpueh Date: Tue, 27 Oct 2020 12:42:10 +0100 Subject: [PATCH 45/99] Fix installation docs link in README.rst (#5520) Signed-off-by: Lukas Puehringer --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index fddde9878581..0175c7f555ac 100644 --- a/README.rst +++ b/README.rst @@ -68,7 +68,7 @@ documentation. .. _`documentation`: https://cryptography.io/ -.. _`the installation documentation`: https://cryptography.io/en/latest/installation/ +.. _`the installation documentation`: https://cryptography.io/en/latest/installation.html .. _`issue tracker`: https://github.com/pyca/cryptography/issues .. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev .. _`security reporting`: https://cryptography.io/en/latest/security/ From b16561670320c65a18cce41d0db0c42ab68350a9 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 27 Oct 2020 18:51:20 -0400 Subject: [PATCH 46/99] Disable blinding for RSA pub keys (#5524) --- src/cryptography/hazmat/backends/openssl/rsa.py | 5 ----- tests/hazmat/primitives/test_rsa.py | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 66b37224e443..de299779d942 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -410,11 +410,6 @@ def sign(self, data, padding, algorithm): @utils.register_interface(RSAPublicKeyWithSerialization) class _RSAPublicKey(object): def __init__(self, backend, rsa_cdata, evp_pkey): - # Blinding is on by default in many versions of OpenSSL, but let's - # just be conservative here. - res = backend._lib.RSA_blinding_on(rsa_cdata, backend._ffi.NULL) - backend.openssl_assert(res == 1) - self._backend = backend self._rsa_cdata = rsa_cdata self._evp_pkey = evp_pkey diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index d7fa7744f493..1a770d3efe50 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -1894,6 +1894,9 @@ def test_rsa_public_numbers_create_key(self, backend): public_key = RSA_KEY_1024.public_numbers.public_key(backend) assert public_key + public_key = rsa.RSAPublicNumbers(n=10, e=3).public_key(backend) + assert public_key + def test_public_numbers_invalid_types(self): with pytest.raises(TypeError): rsa.RSAPublicNumbers(e=None, n=15) From 46d35a835fe9e0c586c6b89db31280dde82280c0 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 27 Oct 2020 21:28:57 -0700 Subject: [PATCH 47/99] port 3.2.1 changelog (#5526) --- CHANGELOG.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cf3d3779af7c..3e3a0dd6435b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,15 @@ Changelog .. note:: This version is not yet released and is under active development. +.. _v3-2-1: + +3.2.1 - 2020-10-27 +~~~~~~~~~~~~~~~~~~ + +* Disable blinding on RSA public keys to address an error with some versions + of OpenSSL. + + .. _v3-2: 3.2 - 2020-10-25 From d9e174d3e16c4dc50789f9897334bcc14d0b05d9 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 28 Oct 2020 10:38:42 -0400 Subject: [PATCH 48/99] Drop python 3.5 (#5527) --- .github/workflows/ci.yml | 3 +-- .github/workflows/wheel-builder.yml | 5 ++--- .travis.yml | 3 --- .zuul.d/jobs.yaml | 6 +++--- CHANGELOG.rst | 4 +++- README.rst | 2 +- docs/installation.rst | 2 +- setup.py | 4 ++-- src/cryptography/__init__.py | 7 ------- tox.ini | 2 +- 10 files changed, 14 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b55266721e95..fc556f06189a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: matrix: PYTHON: - {VERSION: "2.7", TOXENV: "py27", EXTRA_CFLAGS: ""} - - {VERSION: "3.5", TOXENV: "py35", EXTRA_CFLAGS: ""} + - {VERSION: "3.6", TOXENV: "py36", EXTRA_CFLAGS: ""} - {VERSION: "3.9", TOXENV: "py39", EXTRA_CFLAGS: "-DUSE_OSRANDOM_RNG_FOR_TESTING"} name: "Python ${{ matrix.PYTHON.VERSION }} on macOS" steps: @@ -59,7 +59,6 @@ jobs: - {ARCH: 'x64', WINDOWS: 'win64'} PYTHON: - {VERSION: "2.7", TOXENV: "py27", MSVC_VERSION: "2010", CL_FLAGS: ""} - - {VERSION: "3.5", TOXENV: "py35", MSVC_VERSION: "2019", CL_FLAGS: ""} - {VERSION: "3.6", TOXENV: "py36", MSVC_VERSION: "2019", CL_FLAGS: ""} - {VERSION: "3.7", TOXENV: "py37", MSVC_VERSION: "2019", CL_FLAGS: ""} - {VERSION: "3.8", TOXENV: "py38", MSVC_VERSION: "2019", CL_FLAGS: ""} diff --git a/.github/workflows/wheel-builder.yml b/.github/workflows/wheel-builder.yml index 0534513ac3a8..df8a752eaa4f 100644 --- a/.github/workflows/wheel-builder.yml +++ b/.github/workflows/wheel-builder.yml @@ -11,7 +11,7 @@ jobs: container: ${{ matrix.MANYLINUX.CONTAINER }} strategy: matrix: - PYTHON: ["cp27-cp27m", "cp27-cp27mu", "cp35-cp35m"] + PYTHON: ["cp27-cp27m", "cp27-cp27mu", "cp36-cp36m"] MANYLINUX: - NAME: manylinux1_x86_64 CONTAINER: "pyca/cryptography-manylinux1:x86_64" @@ -62,7 +62,7 @@ jobs: DOWNLOAD_URL: 'https://www.python.org/ftp/python/2.7.17/python-2.7.17-macosx10.9.pkg' BIN_PATH: '/Library/Frameworks/Python.framework/Versions/2.7/bin/python' - VERSION: '3.8' - ABI_VERSION: '3.5' + ABI_VERSION: '3.6' DOWNLOAD_URL: 'https://www.python.org/ftp/python/3.8.2/python-3.8.2-macosx10.9.pkg' BIN_PATH: '/Library/Frameworks/Python.framework/Versions/3.8/bin/python3' name: "${{ matrix.PYTHON.VERSION }} ABI ${{ matrix.PYTHON.ABI_VERSION }} macOS" @@ -115,7 +115,6 @@ jobs: - {ARCH: 'x64', WINDOWS: 'win64'} PYTHON: - {VERSION: "2.7", MSVC_VERSION: "2010"} - - {VERSION: "3.5", MSVC_VERSION: "2019"} - {VERSION: "3.6", MSVC_VERSION: "2019"} - {VERSION: "3.7", MSVC_VERSION: "2019"} - {VERSION: "3.8", MSVC_VERSION: "2019"} diff --git a/.travis.yml b/.travis.yml index a13a977f16f3..45b8122a2d8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,9 +66,6 @@ matrix: - python: 2.7 services: docker env: TOXENV=py27 DOCKER=pyca/cryptography-runner-stretch - - python: 3.5 - services: docker - env: TOXENV=py35 DOCKER=pyca/cryptography-runner-stretch - python: 3.7 services: docker env: TOXENV=py37 DOCKER=pyca/cryptography-runner-buster diff --git a/.zuul.d/jobs.yaml b/.zuul.d/jobs.yaml index 1430b0c31510..38cab295060f 100644 --- a/.zuul.d/jobs.yaml +++ b/.zuul.d/jobs.yaml @@ -46,7 +46,7 @@ - platform: manylinux2014_aarch64 image: pyca/cryptography-manylinux2014_aarch64 pythons: - - cp35-cp35m + - cp36-cp36m - job: name: pyca-cryptography-build-wheel-x86_64 @@ -59,10 +59,10 @@ pythons: - cp27-cp27m - cp27-cp27mu - - cp35-cp35m + - cp36-cp36m - platform: manylinux2010_x86_64 image: pyca/cryptography-manylinux2010:x86_64 pythons: - cp27-cp27m - cp27-cp27mu - - cp35-cp35m + - cp36-cp36m diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3e3a0dd6435b..b652d2ba733e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,9 @@ Changelog .. note:: This version is not yet released and is under active development. +* **BACKWARDS INCOMPATIBLE:** Support for Python 3.5 has been removed due to + low usage and maintenance burden. + .. _v3-2-1: 3.2.1 - 2020-10-27 @@ -16,7 +19,6 @@ Changelog * Disable blinding on RSA public keys to address an error with some versions of OpenSSL. - .. _v3-2: 3.2 - 2020-10-25 diff --git a/README.rst b/README.rst index 0175c7f555ac..062e3c75431c 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,7 @@ pyca/cryptography ``cryptography`` is a package which provides cryptographic recipes and primitives to Python developers. Our goal is for it to be your "cryptographic -standard library". It supports Python 2.7, Python 3.5+, and PyPy 5.4+. +standard library". It supports Python 2.7, Python 3.6+, and PyPy 5.4+. ``cryptography`` includes both high level recipes and low level interfaces to common cryptographic algorithms such as symmetric ciphers, message digests, and diff --git a/docs/installation.rst b/docs/installation.rst index abfbb9ba58dd..eb270e62f371 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -10,7 +10,7 @@ You can install ``cryptography`` with ``pip``: Supported platforms ------------------- -Currently we test ``cryptography`` on Python 2.7, 3.5+, +Currently we test ``cryptography`` on Python 2.7, 3.6+, PyPy 7.3.1, and PyPy3 7.3.1 on these operating systems. * x86-64 CentOS 7.x diff --git a/setup.py b/setup.py index 82800a96e59b..23671748f025 100644 --- a/setup.py +++ b/setup.py @@ -76,10 +76,10 @@ "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", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Security :: Cryptography", @@ -87,7 +87,7 @@ package_dir={"": "src"}, packages=find_packages(where="src", exclude=["_cffi_src", "_cffi_src.*"]), include_package_data=True, - 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.*", install_requires=["six >= 1.4.1"] + setup_requirements, setup_requires=setup_requirements, extras_require={ diff --git a/src/cryptography/__init__.py b/src/cryptography/__init__.py index 7211614d7f4a..f128502e2fd5 100644 --- a/src/cryptography/__init__.py +++ b/src/cryptography/__init__.py @@ -39,10 +39,3 @@ CryptographyDeprecationWarning, stacklevel=2, ) -if sys.version_info[:2] == (3, 5): - warnings.warn( - "Python 3.5 support will be dropped in the next release of " - "cryptography. Please upgrade your Python.", - CryptographyDeprecationWarning, - stacklevel=2, - ) diff --git a/tox.ini b/tox.ini index bc5de1ad9953..2ecd30a222bb 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 2.4 -envlist = py27,pypy,py35,py36,py37,py38,docs,pep8,packaging +envlist = py27,pypy,py36,py37,py38,py39,docs,pep8,packaging isolated_build = True [testenv] From 95049c5c5f9ebaae537b8421495a9b911ded967e Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 28 Oct 2020 11:41:39 -0400 Subject: [PATCH 49/99] fedora has python 3.9 now (#5528) --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 45b8122a2d8e..25dccb21c9cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -90,9 +90,9 @@ matrix: - python: 3.8 services: docker env: TOXENV=py38-randomorder DOCKER=pyca/cryptography-runner-ubuntu-rolling - - python: 3.8 + - python: 3.9 services: docker - env: TOXENV=py38 DOCKER=pyca/cryptography-runner-fedora + env: TOXENV=py39 DOCKER=pyca/cryptography-runner-fedora - python: 3.8 services: docker env: TOXENV=py38 DOCKER=pyca/cryptography-runner-alpine:latest From 6c43bbce9a77d96925c9e045ab37c9de56af7acd Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 28 Oct 2020 18:02:17 +0000 Subject: [PATCH 50/99] fix Python 2 CryptographyDeprecationWarning guide (#5529) --- docs/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index 563a241e4f1b..842dd75bbb5d 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -16,7 +16,7 @@ If your pytest setup follows the best practices of failing on emitted warnings (``filterwarnings = error``), you may ignore it by adding the following line at the end of the list:: - ignore:Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in a future release.:UserWarning:cryptography + ignore:Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in a future release.:UserWarning **Note:** Using ``cryptography.utils.CryptographyDeprecationWarning`` is not possible here because specifying it triggers From ec8b0b204fcb9c99239c6b5d36225b46e59eb89d Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 28 Oct 2020 14:02:46 -0400 Subject: [PATCH 51/99] Update .travis.yml for 3.9 (#5482) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 25dccb21c9cd..026977ee22f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: # Setting 'python' is just to make travis's UI a bit prettier - python: 3.6 env: TOXENV=py36 - - python: 3.9-dev + - python: 3.9 env: TOXENV=py39 # Travis lists available Pythons (including PyPy) by arch and distro here: # https://docs.travis-ci.com/user/languages/python/#python-versions From 2ce289fdae9d1a422e11236875b456c224527cda Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 29 Oct 2020 01:01:37 -0400 Subject: [PATCH 52/99] Update installation docs for 3.5 drop (#5531) --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index eb270e62f371..ea4625582a87 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -45,7 +45,7 @@ just run If you prefer to compile it yourself you'll need to have OpenSSL installed. You can compile OpenSSL yourself as well or use `a binary distribution`_. Be sure to download the proper version for your architecture and Python -(VC2010 works for Python 2.7 while VC2015 is required for 3.5 and above). +(VC2010 works for Python 2.7 while VC2015 is required for 3.6 and above). Wherever you place your copy of OpenSSL you'll need to set the ``LIB`` and ``INCLUDE`` environment variables to include the proper locations. For example: From a48bcfba2aff6f40a9f98137cf3a94b62e508e69 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 29 Oct 2020 01:01:59 -0400 Subject: [PATCH 53/99] Clean up travis scripts now that 1.0.2 is dropped (#5530) --- .travis/install.sh | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.travis/install.sh b/.travis/install.sh index 91df42285cdb..c9c90b041a3e 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -25,14 +25,9 @@ if [ -n "${OPENSSL}" ]; then shlib_sed make depend make -j"$(nproc)" - # CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 - if [ "${OPENSSL}" == "1.0.2u" ]; then - make install - else - # avoid installing the docs on versions of OpenSSL that aren't ancient. - # https://github.com/openssl/openssl/issues/6685#issuecomment-403838728 - make install_sw install_ssldirs - fi + # avoid installing the docs on versions of OpenSSL that aren't ancient. + # https://github.com/openssl/openssl/issues/6685#issuecomment-403838728 + make install_sw install_ssldirs popd fi elif [ -n "${LIBRESSL}" ]; then From 851acb5ab4427fefc769b1380592c0a01da7bb06 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 30 Oct 2020 12:55:40 -0400 Subject: [PATCH 54/99] Rephrase abi3 FAQ so it stays accurate (#5534) --- docs/faq.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index 842dd75bbb5d..e4c2af4f4eb6 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -122,9 +122,9 @@ Why are there no wheels for Python 3.6+ on Linux or macOS? ---------------------------------------------------------- Our Python3 wheels, for macOS and Linux, are ``abi3`` wheels. This means they -support multiple versions of Python. The Python 3.5 ``abi3`` wheel can be used -with any version of Python greater than or equal to 3.5. Recent versions of -``pip`` will automatically install ``abi3`` wheels. +support multiple versions of Python. The ``abi3`` wheel can be used with any +version of Python greater than or equal to the version it specifies. Recent +versions of ``pip`` will automatically install ``abi3`` wheels. Why can't I import my PEM file? ------------------------------- From e06e5c6767a07851e08039f1f46750f449ec0c97 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 31 Oct 2020 14:05:21 -0400 Subject: [PATCH 55/99] tense (#5538) --- docs/api-stability.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-stability.rst b/docs/api-stability.rst index 205b18447b05..fd34ced0aba1 100644 --- a/docs/api-stability.rst +++ b/docs/api-stability.rst @@ -1,7 +1,7 @@ API stability ============= -From its first release, ``cryptography`` will have a strong API stability +From its first release, ``cryptography`` has had a strong API stability policy. What does this policy cover? From 752f966c947b8e95781a3ac6247231015d686d86 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 31 Oct 2020 14:05:45 -0400 Subject: [PATCH 56/99] we have abi3 wheels for windows now (#5536) * we have abi3 wheels for windows now * Update faq.rst --- docs/faq.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index e4c2af4f4eb6..e57d9c295ed1 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -118,13 +118,13 @@ upstream, ``cryptography`` is also dropping support for them. To fix this issue you should upgrade to a newer version of OpenSSL (1.1.0 or later). This may require you to upgrade to a newer operating system. -Why are there no wheels for Python 3.6+ on Linux or macOS? ----------------------------------------------------------- +Why are there no wheels for my Python3.x version? +------------------------------------------------- -Our Python3 wheels, for macOS and Linux, are ``abi3`` wheels. This means they -support multiple versions of Python. The ``abi3`` wheel can be used with any -version of Python greater than or equal to the version it specifies. Recent -versions of ``pip`` will automatically install ``abi3`` wheels. +Our Python3 wheels are ``abi3`` wheels. This means they support multiple +versions of Python. The ``abi3`` wheel can be used with any version of Python +greater than or equal to the version it specifies. Recent versions of ``pip`` +will automatically install ``abi3`` wheels. Why can't I import my PEM file? ------------------------------- From 753965c13aeae63001ad370f1e45307b3687f665 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 31 Oct 2020 14:23:44 -0400 Subject: [PATCH 57/99] abi3 only on windows (#5537) --- .github/workflows/wheel-builder.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/wheel-builder.yml b/.github/workflows/wheel-builder.yml index df8a752eaa4f..c4bf9ab057d3 100644 --- a/.github/workflows/wheel-builder.yml +++ b/.github/workflows/wheel-builder.yml @@ -115,9 +115,6 @@ jobs: - {ARCH: 'x64', WINDOWS: 'win64'} PYTHON: - {VERSION: "2.7", MSVC_VERSION: "2010"} - - {VERSION: "3.6", MSVC_VERSION: "2019"} - - {VERSION: "3.7", MSVC_VERSION: "2019"} - - {VERSION: "3.8", MSVC_VERSION: "2019"} - {VERSION: "3.8", MSVC_VERSION: "2019", "USE_ABI3": "true", "ABI_VERSION": "cp36"} name: "${{ matrix.PYTHON.VERSION }} ${{ matrix.WINDOWS.WINDOWS }} ${{ matrix.PYTHON.ABI_VERSION }}" steps: From 81e5de8986abfb50a322fe1f3cccf319c77e5f6f Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 31 Oct 2020 16:09:07 -0700 Subject: [PATCH 58/99] updated faq entry (#5541) the error will be opensslv.h these days and we don't need to talk specifically about macOS --- docs/faq.rst | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index e57d9c295ed1..d6f4ad336ac7 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -70,18 +70,17 @@ legacy libraries: :class:`AES-GCM ` and :class:`~cryptography.hazmat.primitives.kdf.hkdf.HKDF`. -Compiling ``cryptography`` on macOS produces a ``fatal error: 'openssl/aes.h' file not found`` error ----------------------------------------------------------------------------------------------------- - -This happens because macOS 10.11 no longer includes a copy of OpenSSL. -``cryptography`` now provides wheels which include a statically linked copy of -OpenSSL. You're seeing this error because your copy of pip is too old to find -our wheel files. Upgrade your copy of pip with ``pip install -U pip`` and then -try install ``cryptography`` again. - -If you are using PyPy, we do not currently ship ``cryptography`` wheels for -PyPy. You will need to install your own copy of OpenSSL -- we recommend using -Homebrew. +Installing ``cryptography`` produces a ``fatal error: 'openssl/opensslv.h' file not found`` error +------------------------------------------------------------------------------------------------- + +``cryptography`` provides wheels which include a statically linked copy of +OpenSSL. If you see this error it is likely because your copy of ``pip`` is too +old to find our wheel files. Upgrade your ``pip`` with ``pip install -U pip`` +and then try to install ``cryptography`` again. + +Users on PyPy, unusual CPU architectures, or distributions of Linux using +``musl`` (like Alpine) will need to compile ``cryptography`` themselves. Please +view our :doc:`/installation` documentation. ``cryptography`` raised an ``InternalError`` and I'm not sure what to do? ------------------------------------------------------------------------- From 66654d628d1445edf42c66de402c6386b67a4292 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 31 Oct 2020 16:10:10 -0700 Subject: [PATCH 59/99] print some stuff in setup.py to try to educate people (#5539) This is likely futile, but maybe it will slightly help out people who run into compilation errors and actually choose to look at the output. --- setup.py | 171 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 102 insertions(+), 69 deletions(-) diff --git a/setup.py b/setup.py index 23671748f025..c6c3e994625e 100644 --- a/setup.py +++ b/setup.py @@ -51,74 +51,107 @@ long_description = f.read() -setup( - name=about["__title__"], - version=about["__version__"], - description=about["__summary__"], - long_description=long_description, - long_description_content_type="text/x-rst", - license=about["__license__"], - url=about["__uri__"], - author=about["__author__"], - author_email=about["__email__"], - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "License :: OSI Approved :: BSD License", - "Natural Language :: English", - "Operating System :: MacOS :: MacOS X", - "Operating System :: POSIX", - "Operating System :: POSIX :: BSD", - "Operating System :: POSIX :: Linux", - "Operating System :: Microsoft :: Windows", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Topic :: Security :: Cryptography", - ], - package_dir={"": "src"}, - packages=find_packages(where="src", exclude=["_cffi_src", "_cffi_src.*"]), - include_package_data=True, - python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*", - install_requires=["six >= 1.4.1"] + setup_requirements, - setup_requires=setup_requirements, - extras_require={ - ":python_version < '3'": ["enum34", "ipaddress"], - "test": [ - "pytest>=3.6.0,!=3.9.0,!=3.9.1,!=3.9.2", - "pretend", - "iso8601", - "pytz", - "hypothesis>=1.11.4,!=3.79.2", +try: + setup( + name=about["__title__"], + version=about["__version__"], + description=about["__summary__"], + long_description=long_description, + long_description_content_type="text/x-rst", + license=about["__license__"], + url=about["__uri__"], + author=about["__author__"], + author_email=about["__email__"], + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX", + "Operating System :: POSIX :: BSD", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Security :: Cryptography", ], - "docs": [ - "sphinx >= 1.6.5,!=1.8.0,!=3.1.0,!=3.1.1", - "sphinx_rtd_theme", + package_dir={"": "src"}, + packages=find_packages( + where="src", exclude=["_cffi_src", "_cffi_src.*"] + ), + include_package_data=True, + python_requires=( + ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" + ), + install_requires=["six >= 1.4.1"] + setup_requirements, + setup_requires=setup_requirements, + extras_require={ + ":python_version < '3'": ["enum34", "ipaddress"], + "test": [ + "pytest>=3.6.0,!=3.9.0,!=3.9.1,!=3.9.2", + "pretend", + "iso8601", + "pytz", + "hypothesis>=1.11.4,!=3.79.2", + ], + "docs": [ + "sphinx >= 1.6.5,!=1.8.0,!=3.1.0,!=3.1.1", + "sphinx_rtd_theme", + ], + "docstest": [ + "doc8", + "pyenchant >= 1.6.11", + "twine >= 1.12.0", + "sphinxcontrib-spelling >= 4.0.1", + ], + "pep8test": [ + "black", + "flake8", + "flake8-import-order", + "pep8-naming", + ], + # This extra is for OpenSSH private keys that use bcrypt KDF + # Versions: v3.1.3 - ignore_few_rounds, v3.1.5 - abi3 + "ssh": ["bcrypt >= 3.1.5"], + }, + # for cffi + zip_safe=False, + ext_package="cryptography.hazmat.bindings", + cffi_modules=[ + "src/_cffi_src/build_openssl.py:ffi", + "src/_cffi_src/build_padding.py:ffi", ], - "docstest": [ - "doc8", - "pyenchant >= 1.6.11", - "twine >= 1.12.0", - "sphinxcontrib-spelling >= 4.0.1", - ], - "pep8test": ["black", "flake8", "flake8-import-order", "pep8-naming"], - # This extra is for OpenSSH private keys that use bcrypt KDF - # Versions: v3.1.3 - ignore_few_rounds, v3.1.5 - abi3 - "ssh": ["bcrypt >= 3.1.5"], - }, - # for cffi - zip_safe=False, - ext_package="cryptography.hazmat.bindings", - cffi_modules=[ - "src/_cffi_src/build_openssl.py:ffi", - "src/_cffi_src/build_padding.py:ffi", - ], -) + ) +except: # noqa: E722 + # Note: This is a bare exception that re-raises so that we don't interfere + # with anything the installation machinery might want to do. Because we + # print this for any exception this msg can appear (e.g. in verbose logs) + # even if there's no failure. For example, SetupRequirementsError is raised + # during PEP517 building and prints this text. setuptools raises SystemExit + # when compilation fails right now, but it's possible this isn't stable + # or a public API commitment so we'll remain ultra conservative. + print( + """ + =============================DEBUG ASSISTANCE============================= + If you are seeing a compilation error please try the following steps to + successfully install cryptography: + 1) Upgrade to the latest pip and try again. This will fix errors for most + users. See: https://pip.pypa.io/en/stable/installing/#upgrading-pip + 2) Read https://cryptography.io/en/latest/installation.html for specific + instructions for your platform. + 3) Check our frequently asked questions for more information: + https://cryptography.io/en/latest/faq.html + =============================DEBUG ASSISTANCE============================= + """ + ) + raise From d59b7c235b0f0819e3f7e4f01e351d4b2f385026 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 1 Nov 2020 11:55:52 -0500 Subject: [PATCH 60/99] Don't tell people to use PGP, it's not good (#5543) --- docs/security.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/security.rst b/docs/security.rst index 8cdd2d114d9a..d11f2700012a 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -53,10 +53,9 @@ We ask that you do not report security issues to our normal GitHub issue tracker. If you believe you've identified a security issue with ``cryptography``, please -report it to ``alex.gaynor@gmail.com``. Messages may be optionally encrypted -with PGP using key fingerprint -``F7FC 698F AAE2 D2EF BECD E98E D1B3 ADC0 E023 8CA6`` (this public key is -available from most commonly-used key servers). +report it to ``alex.gaynor@gmail.com`` and/or ``paul.l.kehrer@gmail.com``. You +should verify that your MTA uses TLS to ensure the confidentiality of your +message. Once you've submitted an issue via email, you should receive an acknowledgment within 48 hours, and depending on the action to be taken, you may receive From 923de98f040dd280c7b7eae4a8e83488853fbd12 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 3 Nov 2020 10:16:06 -0500 Subject: [PATCH 61/99] Attempt to move docker builds from travis to GHA (#5545) * Attempt to move docker builds from travis to GHA * fix linkcheck --- .github/workflows/ci.yml | 36 +++++++++++++++++++++++++++++ .travis.yml | 46 +------------------------------------- .travis/install.sh | 8 ------- .travis/run.sh | 10 +-------- .travis/upload_coverage.sh | 4 ++-- 5 files changed, 40 insertions(+), 64 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc556f06189a..5580b0ec09d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,42 @@ on: - '*.*.*' jobs: + linux-distros: + runs-on: ubuntu-latest + container: ${{ matrix.IMAGE.IMAGE }} + strategy: + matrix: + IMAGE: + - {IMAGE: "pyca/cryptography-runner-centos8", TOXENV: "py27"} + - {IMAGE: "pyca/cryptography-runner-centos8", TOXENV: "py36"} + - {IMAGE: "pyca/cryptography-runner-centos8-fips", TOXENV: "py36", ENV: "OPENSSL_FORCE_FIPS_MODE=1"} + - {IMAGE: "pyca/cryptography-runner-stretch", TOXENV: "py27"} + - {IMAGE: "pyca/cryptography-runner-buster", TOXENV: "py37"} + - {IMAGE: "pyca/cryptography-runner-bullseye", TOXENV: "py38"} + - {IMAGE: "pyca/cryptography-runner-sid", TOXENV: "py38"} + - {IMAGE: "pyca/cryptography-runner-ubuntu-bionic", TOXENV: "py36"} + - {IMAGE: "pyca/cryptography-runner-ubuntu-focal", TOXENV: "py38"} + - {IMAGE: "pyca/cryptography-runner-ubuntu-rolling", TOXENV: "py27"} + - {IMAGE: "pyca/cryptography-runner-ubuntu-rolling", TOXENV: "py38"} + - {IMAGE: "pyca/cryptography-runner-ubuntu-rolling", TOXENV: "py38-randomorder"} + - {IMAGE: "pyca/cryptography-runner-fedora", TOXENV: "py39"} + - {IMAGE: "pyca/cryptography-runner-alpine", TOXENV: "py38"} + name: "tox -e ${{ matrix.IMAGE.TOXENV }} on ${{ matrix.IMAGE.IMAGE }} ${{ matrix.IMAGE.ENV }}" + steps: + - uses: actions/checkout@v2 + - run: 'git clone --depth=1 https://github.com/google/wycheproof "$HOME/wycheproof"' + - run: 'echo "$ENV_VAR" >> $GITHUB_ENV' + if: matrix.IMAGE.ENV + env: + ENV_VAR: ${{ matrix.IMAGE.ENV }} + - run: 'tox -- --wycheproof-root="$HOME/wycheproof"' + env: + TOXENV: ${{ matrix.IMAGE.TOXENV }} + - 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 + bash codecov.sh -n "tox -e ${{ matrix.IMAGE.TOXENV }} on ${{ matrix.IMAGE.IMAGE }} ${{ matrix.IMAGE.ENV }}" + macos: runs-on: macos-latest strategy: diff --git a/.travis.yml b/.travis.yml index 026977ee22f0..b214aa49421e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,49 +54,6 @@ matrix: - python: 3.8 env: TOXENV=py38 LIBRESSL=3.2.2 - - python: 2.7 - services: docker - env: TOXENV=py27 DOCKER=pyca/cryptography-runner-centos8 - - python: 3.6 - services: docker - env: TOXENV=py36 DOCKER=pyca/cryptography-runner-centos8 - - python: 3.6 - services: docker - env: TOXENV=py36 OPENSSL_FORCE_FIPS_MODE=1 DOCKER=pyca/cryptography-runner-centos8-fips - - python: 2.7 - services: docker - env: TOXENV=py27 DOCKER=pyca/cryptography-runner-stretch - - python: 3.7 - services: docker - env: TOXENV=py37 DOCKER=pyca/cryptography-runner-buster - - python: 3.8 - services: docker - env: TOXENV=py38 DOCKER=pyca/cryptography-runner-bullseye - - python: 3.8 - services: docker - env: TOXENV=py38 DOCKER=pyca/cryptography-runner-sid - - python: 3.6 - services: docker - env: TOXENV=py36 DOCKER=pyca/cryptography-runner-ubuntu-bionic - - python: 3.8 - services: docker - env: TOXENV=py38 DOCKER=pyca/cryptography-runner-ubuntu-focal - - python: 2.7 - services: docker - env: TOXENV=py27 DOCKER=pyca/cryptography-runner-ubuntu-rolling - - python: 3.8 - services: docker - env: TOXENV=py38 DOCKER=pyca/cryptography-runner-ubuntu-rolling - - python: 3.8 - services: docker - env: TOXENV=py38-randomorder DOCKER=pyca/cryptography-runner-ubuntu-rolling - - python: 3.9 - services: docker - env: TOXENV=py39 DOCKER=pyca/cryptography-runner-fedora - - python: 3.8 - services: docker - env: TOXENV=py38 DOCKER=pyca/cryptography-runner-alpine:latest - - python: 3.8 env: TOXENV=docs OPENSSL=1.1.1h addons: @@ -104,8 +61,7 @@ matrix: packages: - libenchant-dev - python: 3.8 - services: docker - env: TOXENV=docs-linkcheck DOCKER=pyca/cryptography-runner-buster + env: TOXENV=docs-linkcheck if: (branch = master AND type != pull_request) OR commit_message =~ /linkcheck/ - python: 3.8 diff --git a/.travis/install.sh b/.travis/install.sh index c9c90b041a3e..ccc7f444dfe8 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -43,14 +43,6 @@ elif [ -n "${LIBRESSL}" ]; then fi fi -if [ -n "${DOCKER}" ]; then - if [ -n "${OPENSSL}" ] || [ -n "${LIBRESSL}" ]; then - echo "OPENSSL and LIBRESSL are not allowed when DOCKER is set." - exit 1 - fi - docker pull "$DOCKER" || docker pull "$DOCKER" || docker pull "$DOCKER" -fi - if [ -z "${DOWNSTREAM}" ]; then git clone --depth=1 https://github.com/google/wycheproof "$HOME/wycheproof" fi diff --git a/.travis/run.sh b/.travis/run.sh index 01605c2717d4..313fb86b6d8d 100755 --- a/.travis/run.sh +++ b/.travis/run.sh @@ -20,15 +20,7 @@ fi source ~/.venv/bin/activate -if [ -n "${DOCKER}" ]; then - docker run --rm \ - -v "${TRAVIS_BUILD_DIR}":"${TRAVIS_BUILD_DIR}" \ - -v "${HOME}/wycheproof":/wycheproof \ - -w "${TRAVIS_BUILD_DIR}" \ - -e OPENSSL_FORCE_FIPS_MODE \ - -e TOXENV "${DOCKER}" \ - /bin/sh -c "tox -- --wycheproof-root='/wycheproof'" -elif [ -n "${TOXENV}" ]; then +if [ -n "${TOXENV}" ]; then tox -- --wycheproof-root="$HOME/wycheproof" else downstream_script="${TRAVIS_BUILD_DIR}/.travis/downstream.d/${DOWNSTREAM}.sh" diff --git a/.travis/upload_coverage.sh b/.travis/upload_coverage.sh index 2999bb7e6b25..28251f8ba010 100755 --- a/.travis/upload_coverage.sh +++ b/.travis/upload_coverage.sh @@ -14,8 +14,8 @@ if [ -n "${TOXENV}" ]; then source ~/.venv/bin/activate 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 - bash codecov.sh -Z -e TRAVIS_OS_NAME,TOXENV,OPENSSL,DOCKER,OPENSSL_FORCE_FIPS_MODE || \ - bash codecov.sh -Z -e TRAVIS_OS_NAME,TOXENV,OPENSSL,DOCKER,OPENSSL_FORCE_FIPS_MODE + bash codecov.sh -Z -e TRAVIS_OS_NAME,TOXENV,OPENSSL || \ + bash codecov.sh -Z -e TRAVIS_OS_NAME,TOXENV,OPENSSL ;; esac fi From 4ba0d6ea4af2fb93826378e6f21744161dda082b Mon Sep 17 00:00:00 2001 From: Mads Jensen Date: Wed, 4 Nov 2020 15:51:17 +0100 Subject: [PATCH 62/99] Fix broken link to security documentation in README.rst (#5551) --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 062e3c75431c..586cb1b0b3bc 100644 --- a/README.rst +++ b/README.rst @@ -71,4 +71,4 @@ documentation. .. _`the installation documentation`: https://cryptography.io/en/latest/installation.html .. _`issue tracker`: https://github.com/pyca/cryptography/issues .. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev -.. _`security reporting`: https://cryptography.io/en/latest/security/ +.. _`security reporting`: https://cryptography.io/en/latest/security.html From 15771e2ec2f94e2a82057d3eae38cb5580fb6727 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 4 Nov 2020 09:49:47 -0600 Subject: [PATCH 63/99] padding: Tip-toe around bytes subclasses. (#5548) This change allows future's newbytes class to be padded again. Fixes https://github.com/pyca/cryptography/issues/5547. --- src/cryptography/hazmat/primitives/padding.py | 10 ++++++-- tests/hazmat/primitives/test_padding.py | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/cryptography/hazmat/primitives/padding.py b/src/cryptography/hazmat/primitives/padding.py index d3dc7093ae51..98abffbc08be 100644 --- a/src/cryptography/hazmat/primitives/padding.py +++ b/src/cryptography/hazmat/primitives/padding.py @@ -42,7 +42,10 @@ def _byte_padding_update(buffer_, data, block_size): utils._check_byteslike("data", data) - buffer_ += bytes(data) + # six.PY2: Only coerce non-bytes objects to avoid triggering bad behavior + # of future's newbytes type. Unconditionally call bytes() after Python 2 + # support is gone. + buffer_ += data if isinstance(data, bytes) else bytes(data) finished_blocks = len(buffer_) // (block_size // 8) @@ -66,7 +69,10 @@ def _byte_unpadding_update(buffer_, data, block_size): utils._check_byteslike("data", data) - buffer_ += bytes(data) + # six.PY2: Only coerce non-bytes objects to avoid triggering bad behavior + # of future's newbytes type. Unconditionally call bytes() after Python 2 + # support is gone. + buffer_ += data if isinstance(data, bytes) else bytes(data) finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0) diff --git a/tests/hazmat/primitives/test_padding.py b/tests/hazmat/primitives/test_padding.py index bf5379730131..b15eb37539c5 100644 --- a/tests/hazmat/primitives/test_padding.py +++ b/tests/hazmat/primitives/test_padding.py @@ -43,6 +43,18 @@ def test_non_bytes(self): with pytest.raises(TypeError): unpadder.update(u"abc") + def test_zany_py2_bytes_subclass(self): + class mybytes(bytes): # noqa: N801 + def __str__(self): + return "broken" + + str(mybytes()) + padder = padding.PKCS7(128).padder() + padder.update(mybytes(b"abc")) + unpadder = padding.PKCS7(128).unpadder() + unpadder.update(mybytes(padder.finalize())) + assert unpadder.finalize() == b"abc" + @pytest.mark.parametrize( ("size", "unpadded", "padded"), [ @@ -154,6 +166,18 @@ def test_non_bytes(self): with pytest.raises(TypeError): unpadder.update(u"abc") + def test_zany_py2_bytes_subclass(self): + class mybytes(bytes): # noqa: N801 + def __str__(self): + return "broken" + + str(mybytes()) + padder = padding.ANSIX923(128).padder() + padder.update(mybytes(b"abc")) + unpadder = padding.ANSIX923(128).unpadder() + unpadder.update(mybytes(padder.finalize())) + assert unpadder.finalize() == b"abc" + @pytest.mark.parametrize( ("size", "unpadded", "padded"), [ From b59d2de9cf0dcf5c36d37007b6e756f18e9b0160 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 9 Nov 2020 12:56:53 -0600 Subject: [PATCH 64/99] GCM IV size limits (#5553) * GCM IV size limits OpenSSL 3.0.0 is going to enforce these size limits so we might as well put them in now. * fix the tests * black * these cases can't happen if we're limiting IV size already --- CHANGELOG.rst | 6 +++++ .../hazmat/primitives/ciphers/aead.py | 4 ++-- .../hazmat/primitives/ciphers/modes.py | 12 +++++----- tests/hazmat/primitives/test_aead.py | 13 +++++++++-- tests/hazmat/primitives/test_aes_gcm.py | 22 +++++++++++++++++++ tests/hazmat/primitives/test_ciphers.py | 7 ++++++ tests/hazmat/primitives/utils.py | 4 ++++ tests/wycheproof/test_aes.py | 17 +++++++++----- 8 files changed, 70 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b652d2ba733e..b001ec266c6a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,12 @@ Changelog * **BACKWARDS INCOMPATIBLE:** Support for Python 3.5 has been removed due to low usage and maintenance burden. +* **BACKWARDS INCOMPATIBLE:** The + :class:`~cryptography.hazmat.primitives.ciphers.modes.GCM` and + :class:`~cryptography.hazmat.primitives.ciphers.aead.AESGCM` now require + 64-bit to 1024-bit (8 byte to 128 byte) initialization vectors. This change + is to conform with an upcoming OpenSSL release that will no longer support + sizes outside this window. .. _v3-2-1: diff --git a/src/cryptography/hazmat/primitives/ciphers/aead.py b/src/cryptography/hazmat/primitives/ciphers/aead.py index 4eddc1ee6e37..c8c93955ce01 100644 --- a/src/cryptography/hazmat/primitives/ciphers/aead.py +++ b/src/cryptography/hazmat/primitives/ciphers/aead.py @@ -170,5 +170,5 @@ def _check_params(self, nonce, data, associated_data): utils._check_byteslike("nonce", nonce) utils._check_bytes("data", data) utils._check_bytes("associated_data", associated_data) - if len(nonce) == 0: - raise ValueError("Nonce must be at least 1 byte") + if len(nonce) < 8 or len(nonce) > 128: + raise ValueError("Nonce must be between 8 and 128 bytes") diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py index dcb24444214f..0ba0f2b5a176 100644 --- a/src/cryptography/hazmat/primitives/ciphers/modes.py +++ b/src/cryptography/hazmat/primitives/ciphers/modes.py @@ -196,12 +196,14 @@ class GCM(object): _MAX_AAD_BYTES = (2 ** 64) // 8 def __init__(self, initialization_vector, tag=None, min_tag_length=16): - # len(initialization_vector) must in [1, 2 ** 64), but it's impossible - # to actually construct a bytes object that large, so we don't check - # for it + # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive + # This is a sane limit anyway so we'll enforce it here. utils._check_byteslike("initialization_vector", initialization_vector) - if len(initialization_vector) == 0: - raise ValueError("initialization_vector must be at least 1 byte") + if len(initialization_vector) < 8 or len(initialization_vector) > 128: + raise ValueError( + "initialization_vector must be between 8 and 128 bytes (64 " + "and 1024 bits)." + ) self._initialization_vector = initialization_vector if tag is not None: utils._check_bytes("tag", tag) diff --git a/tests/hazmat/primitives/test_aead.py b/tests/hazmat/primitives/test_aead.py index 753c7c192bc9..270693182ed2 100644 --- a/tests/hazmat/primitives/test_aead.py +++ b/tests/hazmat/primitives/test_aead.py @@ -380,6 +380,9 @@ def test_data_too_large(self): def test_vectors(self, backend, vector): nonce = binascii.unhexlify(vector["iv"]) + if len(nonce) < 8: + pytest.skip("GCM does not support less than 64-bit IVs") + if backend._fips_enabled and len(nonce) != 12: # Red Hat disables non-96-bit IV support as part of its FIPS # patches. @@ -418,11 +421,17 @@ def test_params_not_bytes(self, nonce, data, associated_data, backend): with pytest.raises(TypeError): aesgcm.decrypt(nonce, data, associated_data) - def test_invalid_nonce_length(self, backend): + @pytest.mark.parametrize("length", [7, 129]) + def test_invalid_nonce_length(self, length, backend): + if backend._fips_enabled: + # Red Hat disables non-96-bit IV support as part of its FIPS + # patches. + pytest.skip("Non-96-bit IVs unsupported in FIPS mode.") + key = AESGCM.generate_key(128) aesgcm = AESGCM(key) with pytest.raises(ValueError): - aesgcm.encrypt(b"", b"hi", None) + aesgcm.encrypt(b"\x00" * length, b"hi", None) def test_bad_key(self, backend): with pytest.raises(TypeError): diff --git a/tests/hazmat/primitives/test_aes_gcm.py b/tests/hazmat/primitives/test_aes_gcm.py index f289f18b11cc..8b71d12300c9 100644 --- a/tests/hazmat/primitives/test_aes_gcm.py +++ b/tests/hazmat/primitives/test_aes_gcm.py @@ -195,3 +195,25 @@ def test_buffer_protocol(self, backend): dec.authenticate_additional_data(bytearray(b"foo")) pt = dec.update(ct) + dec.finalize() assert pt == data + + @pytest.mark.parametrize("size", [8, 128]) + def test_gcm_min_max_iv(self, size, backend): + if backend._fips_enabled: + # Red Hat disables non-96-bit IV support as part of its FIPS + # patches. + pytest.skip("Non-96-bit IVs unsupported in FIPS mode.") + + key = os.urandom(16) + iv = b"\x00" * size + + payload = b"data" + encryptor = base.Cipher(algorithms.AES(key), modes.GCM(iv)).encryptor() + ct = encryptor.update(payload) + encryptor.finalize() + tag = encryptor.tag + + decryptor = base.Cipher(algorithms.AES(key), modes.GCM(iv)).decryptor() + pt = decryptor.update(ct) + + decryptor.finalize_with_tag(tag) + assert pt == payload diff --git a/tests/hazmat/primitives/test_ciphers.py b/tests/hazmat/primitives/test_ciphers.py index 4d82f0c13f42..a9219fe99c15 100644 --- a/tests/hazmat/primitives/test_ciphers.py +++ b/tests/hazmat/primitives/test_ciphers.py @@ -72,6 +72,13 @@ def test_xts_wrong_key_size(self, backend): ciphers.Cipher(AES(b"0" * 16), modes.XTS(b"0" * 16), backend) +class TestGCM(object): + @pytest.mark.parametrize("size", [7, 129]) + def test_gcm_min_max(self, size): + with pytest.raises(ValueError): + modes.GCM(b"0" * size) + + class TestCamellia(object): @pytest.mark.parametrize( ("key", "keysize"), diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py index 741e07d5c3cc..3f1eff66e00f 100644 --- a/tests/hazmat/primitives/utils.py +++ b/tests/hazmat/primitives/utils.py @@ -86,6 +86,10 @@ def test_aead(self, backend, params): def aead_test(backend, cipher_factory, mode_factory, params): + if mode_factory is GCM and len(params["iv"]) < 16: + # 16 because this is hex encoded data + pytest.skip("Less than 64-bit IVs are no longer supported") + if ( mode_factory is GCM and backend._fips_enabled diff --git a/tests/wycheproof/test_aes.py b/tests/wycheproof/test_aes.py index e33c01e99f54..9992095ae8c3 100644 --- a/tests/wycheproof/test_aes.py +++ b/tests/wycheproof/test_aes.py @@ -54,6 +54,11 @@ def test_aes_gcm(backend, wycheproof): msg = binascii.unhexlify(wycheproof.testcase["msg"]) ct = binascii.unhexlify(wycheproof.testcase["ct"]) tag = binascii.unhexlify(wycheproof.testcase["tag"]) + if len(iv) < 8 or len(iv) > 128: + pytest.skip( + "Less than 64-bit IVs (and greater than 1024-bit) are no longer " + "supported" + ) if backend._fips_enabled and len(iv) != 12: # Red Hat disables non-96-bit IV support as part of its FIPS # patches. @@ -73,9 +78,6 @@ def test_aes_gcm(backend, wycheproof): dec.authenticate_additional_data(aad) computed_msg = dec.update(ct) + dec.finalize() assert computed_msg == msg - elif len(iv) == 0: - with pytest.raises(ValueError): - Cipher(algorithms.AES(key), modes.GCM(iv), backend) else: dec = Cipher( algorithms.AES(key), @@ -97,6 +99,12 @@ def test_aes_gcm_aead_api(backend, wycheproof): msg = binascii.unhexlify(wycheproof.testcase["msg"]) ct = binascii.unhexlify(wycheproof.testcase["ct"]) tag = binascii.unhexlify(wycheproof.testcase["tag"]) + if len(iv) < 8 or len(iv) > 128: + pytest.skip( + "Less than 64-bit IVs (and greater than 1024-bit) are no longer " + "supported" + ) + if backend._fips_enabled and len(iv) != 12: # Red Hat disables non-96-bit IV support as part of its FIPS # patches. @@ -107,9 +115,6 @@ def test_aes_gcm_aead_api(backend, wycheproof): assert computed_ct == ct + tag computed_msg = aesgcm.decrypt(iv, ct + tag, aad) assert computed_msg == msg - elif len(iv) == 0: - with pytest.raises(ValueError): - aesgcm.encrypt(iv, msg, aad) else: with pytest.raises(InvalidTag): aesgcm.decrypt(iv, ct + tag, aad) From b9b921aa898d475a080a3f7ff88419f16311a704 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Wed, 11 Nov 2020 04:52:33 +0100 Subject: [PATCH 65/99] Fix broken links (#5552) * Fix broken links. * Shorter lines. --- src/cryptography/hazmat/backends/openssl/backend.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 5f5c5795bd33..f2b7230bbaa7 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1368,8 +1368,9 @@ def load_pem_x509_certificate(self, data): if x509 == self._ffi.NULL: self._consume_errors() raise ValueError( - "Unable to load certificate. See https://cryptography.io/en/la" - "test/faq/#why-can-t-i-import-my-pem-file for more details." + "Unable to load certificate. See https://cryptography.io/en/" + "latest/faq.html#why-can-t-i-import-my-pem-file for more" + " details." ) x509 = self._ffi.gc(x509, self._lib.X509_free) @@ -1394,7 +1395,8 @@ def load_pem_x509_crl(self, data): self._consume_errors() raise ValueError( "Unable to load CRL. See https://cryptography.io/en/la" - "test/faq/#why-can-t-i-import-my-pem-file for more details." + "test/faq.html#why-can-t-i-import-my-pem-file for more" + " details." ) x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) @@ -1418,8 +1420,9 @@ def load_pem_x509_csr(self, data): if x509_req == self._ffi.NULL: self._consume_errors() raise ValueError( - "Unable to load request. See https://cryptography.io/en/la" - "test/faq/#why-can-t-i-import-my-pem-file for more details." + "Unable to load request. See https://cryptography.io/en/" + "latest/faq.html#why-can-t-i-import-my-pem-file for more" + " details." ) x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) From e0b446e2e47a589de480346aaedfa0a495be5ae6 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 11 Nov 2020 10:50:02 -0500 Subject: [PATCH 66/99] Migrate drownstream tests to GHA (#5554) --- .../downstream.d/aws-encryption-sdk.sh | 0 .../downstream.d/certbot-josepy.sh | 0 {.travis => .github}/downstream.d/certbot.sh | 0 .../downstream.d/dynamodb-encryption-sdk.sh | 0 .../downstream.d/pyopenssl.sh | 0 {.travis => .github}/downstream.d/twisted.sh | 0 .github/workflows/ci.yml | 23 +++++++++++++++++++ .travis.yml | 16 ++----------- .travis/downstream.d/README.rst | 13 ----------- 9 files changed, 25 insertions(+), 27 deletions(-) rename {.travis => .github}/downstream.d/aws-encryption-sdk.sh (100%) rename {.travis => .github}/downstream.d/certbot-josepy.sh (100%) rename {.travis => .github}/downstream.d/certbot.sh (100%) rename {.travis => .github}/downstream.d/dynamodb-encryption-sdk.sh (100%) rename {.travis => .github}/downstream.d/pyopenssl.sh (100%) rename {.travis => .github}/downstream.d/twisted.sh (100%) delete mode 100644 .travis/downstream.d/README.rst diff --git a/.travis/downstream.d/aws-encryption-sdk.sh b/.github/downstream.d/aws-encryption-sdk.sh similarity index 100% rename from .travis/downstream.d/aws-encryption-sdk.sh rename to .github/downstream.d/aws-encryption-sdk.sh diff --git a/.travis/downstream.d/certbot-josepy.sh b/.github/downstream.d/certbot-josepy.sh similarity index 100% rename from .travis/downstream.d/certbot-josepy.sh rename to .github/downstream.d/certbot-josepy.sh diff --git a/.travis/downstream.d/certbot.sh b/.github/downstream.d/certbot.sh similarity index 100% rename from .travis/downstream.d/certbot.sh rename to .github/downstream.d/certbot.sh diff --git a/.travis/downstream.d/dynamodb-encryption-sdk.sh b/.github/downstream.d/dynamodb-encryption-sdk.sh similarity index 100% rename from .travis/downstream.d/dynamodb-encryption-sdk.sh rename to .github/downstream.d/dynamodb-encryption-sdk.sh diff --git a/.travis/downstream.d/pyopenssl.sh b/.github/downstream.d/pyopenssl.sh similarity index 100% rename from .travis/downstream.d/pyopenssl.sh rename to .github/downstream.d/pyopenssl.sh diff --git a/.travis/downstream.d/twisted.sh b/.github/downstream.d/twisted.sh similarity index 100% rename from .travis/downstream.d/twisted.sh rename to .github/downstream.d/twisted.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5580b0ec09d7..a1879f83b490 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,3 +135,26 @@ jobs: 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 bash codecov.sh -n "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}" + + linux-downstream: + runs-on: ubuntu-latest + strategy: + matrix: + DOWNSTREAM: + - pyopenssl + - twisted + - aws-encryption-sdk + - dynamodb-encryption-sdk + - certbot + - certbot-josepy + name: "Downstream tests for ${{ matrix.DOWNSTREAM }}" + steps: + - uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - run: python -m pip install -U pip wheel + - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh install + - run: pip install . + - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh run diff --git a/.travis.yml b/.travis.yml index b214aa49421e..ff7647a7a43b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,22 +64,10 @@ matrix: env: TOXENV=docs-linkcheck if: (branch = master AND type != pull_request) OR commit_message =~ /linkcheck/ - - python: 3.8 - env: DOWNSTREAM=pyopenssl - - python: 3.7 - env: DOWNSTREAM=twisted OPENSSL=1.1.1h + # TODO: This needs to be moved to GHA, but it fails there. The + # confusing part is why it passes on Travis! - python: 3.7 env: DOWNSTREAM=paramiko - - python: 3.7 - env: DOWNSTREAM=aws-encryption-sdk - - python: 3.7 - # BOTO_CONFIG works around this boto issue on travis: - # https://github.com/boto/boto/issues/3717 - env: DOWNSTREAM=dynamodb-encryption-sdk BOTO_CONFIG=/dev/null - - python: 3.8 - env: DOWNSTREAM=certbot - - python: 3.8 - env: DOWNSTREAM=certbot-josepy install: - ./.travis/install.sh diff --git a/.travis/downstream.d/README.rst b/.travis/downstream.d/README.rst deleted file mode 100644 index 1553448c327f..000000000000 --- a/.travis/downstream.d/README.rst +++ /dev/null @@ -1,13 +0,0 @@ -To add downstream tests to be run in CI: - -1. Create a test handler for the downstream consumer that you want to test. - - * The test handler should be a single file in the ``.travis/downstream.d/`` directory. - * The file name should be ``{downstream name}.sh`` where ``{downstream name}`` - is the name that you wish to use to identify the consumer. - * The test handler should accept a single argument that can be either ``install`` or ``run``. - These should be used to separate installation of the downstream consumer and - any dependencies from the actual running of the tests. - -2. Add an entry to the test matrix in ``.travis.yml`` that sets the ``DOWNSTREAM`` - environment variable to the downstream name that you selected. From 94f32c14b7c0992b02943cf6fb9ebc5997d9fd7b Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 11 Nov 2020 11:25:29 -0500 Subject: [PATCH 67/99] See if we can remove this now that we're on focal (#5559) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ff7647a7a43b..ef5e2d9038cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,7 +55,7 @@ matrix: env: TOXENV=py38 LIBRESSL=3.2.2 - python: 3.8 - env: TOXENV=docs OPENSSL=1.1.1h + env: TOXENV=docs addons: apt: packages: From d74a477b66983bc3a169d150c3a52de83b9f85e4 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 11 Nov 2020 11:57:32 -0500 Subject: [PATCH 68/99] Tighten up this warning message (#5560) --- src/cryptography/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptography/__init__.py b/src/cryptography/__init__.py index f128502e2fd5..465671eec826 100644 --- a/src/cryptography/__init__.py +++ b/src/cryptography/__init__.py @@ -34,8 +34,8 @@ if sys.version_info[0] == 2: warnings.warn( "Python 2 is no longer supported by the Python core team. Support for " - "it is now deprecated in cryptography, and will be removed in a " - "future release.", + "it is now deprecated in cryptography, and will be removed in the " + "next release.", CryptographyDeprecationWarning, stacklevel=2, ) From 49109ce1a6e870cb6cc87f05d9ae7c03d70a684b Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 11 Nov 2020 14:35:47 -0500 Subject: [PATCH 69/99] Always rely on OpenSSL's builtin locking callbacks (#5561) --- LICENSE | 4 +- src/_cffi_src/openssl/callbacks.py | 134 +----------------- src/_cffi_src/openssl/crypto.py | 6 - .../hazmat/bindings/openssl/_conditional.py | 7 - .../hazmat/bindings/openssl/binding.py | 23 +-- tests/hazmat/bindings/test_openssl.py | 12 -- 6 files changed, 4 insertions(+), 182 deletions(-) diff --git a/LICENSE b/LICENSE index fe5af51408c1..07074259b61a 100644 --- a/LICENSE +++ b/LICENSE @@ -2,5 +2,5 @@ This software is made available under the terms of *either* of the licenses found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made under the terms of *both* these licenses. -The code used in the OpenSSL locking callback and OS random engine is derived -from CPython, and is licensed under the terms of the PSF License Agreement. +The code used in the OS random engine is derived from CPython, and is licensed +under the terms of the PSF License Agreement. diff --git a/src/_cffi_src/openssl/callbacks.py b/src/_cffi_src/openssl/callbacks.py index 5d5da1b7ec48..19301b973a28 100644 --- a/src/_cffi_src/openssl/callbacks.py +++ b/src/_cffi_src/openssl/callbacks.py @@ -5,25 +5,7 @@ from __future__ import absolute_import, division, print_function INCLUDES = """ -#include -#include -#include -#include - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#else -#include -#include -#include -#endif - -#ifdef __MVS__ -#include -#endif +#include """ TYPES = """ @@ -37,124 +19,10 @@ """ FUNCTIONS = """ -int Cryptography_setup_ssl_threads(void); int Cryptography_pem_password_cb(char *, int, int, void *); """ CUSTOMIZATIONS = """ -/* This code is derived from the locking code found in the Python _ssl module's - locking callback for OpenSSL. - - Copyright 2001-2016 Python Software Foundation; All Rights Reserved. - - It has been subsequently modified to use cross platform locking without - using CPython APIs by Armin Rigo of the PyPy project. -*/ - -#if CRYPTOGRAPHY_IS_LIBRESSL -#ifdef _WIN32 -typedef CRITICAL_SECTION Cryptography_mutex; -static __inline void cryptography_mutex_init(Cryptography_mutex *mutex) { - InitializeCriticalSection(mutex); -} -static __inline void cryptography_mutex_lock(Cryptography_mutex *mutex) { - EnterCriticalSection(mutex); -} -static __inline void cryptography_mutex_unlock(Cryptography_mutex *mutex) { - LeaveCriticalSection(mutex); -} -#else -typedef pthread_mutex_t Cryptography_mutex; -#define ASSERT_STATUS(call) \ - if ((call) != 0) { \ - perror("Fatal error in callback initialization: " #call); \ - abort(); \ - } -#ifdef __MVS__ -/* When pthread_mutex_init is called more than once on the same mutex, - on z/OS this throws an EBUSY error. -*/ -#define ASSERT_STATUS_INIT(call) \ - if ((call) != 0 && errno != EBUSY) { \ - perror("Fatal error in callback initialization: " #call); \ - abort(); \ - } -#else -#define ASSERT_STATUS_INIT ASSERT_STATUS -#endif -static inline void cryptography_mutex_init(Cryptography_mutex *mutex) { -#if !defined(pthread_mutexattr_default) -# define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL) -#endif - ASSERT_STATUS_INIT(pthread_mutex_init(mutex, pthread_mutexattr_default)); -} -static inline void cryptography_mutex_lock(Cryptography_mutex *mutex) { - ASSERT_STATUS(pthread_mutex_lock(mutex)); -} -static inline void cryptography_mutex_unlock(Cryptography_mutex *mutex) { - ASSERT_STATUS(pthread_mutex_unlock(mutex)); -} -#endif - - -static int _ssl_locks_count = 0; -static Cryptography_mutex *_ssl_locks = NULL; - -static void _ssl_thread_locking_function(int mode, int n, const char *file, - int line) { - /* this function is needed to perform locking on shared data - structures. (Note that OpenSSL uses a number of global data - structures that will be implicitly shared whenever multiple - threads use OpenSSL.) Multi-threaded applications will - crash at random if it is not set. - - locking_function() must be able to handle up to - CRYPTO_num_locks() different mutex locks. It sets the n-th - lock if mode & CRYPTO_LOCK, and releases it otherwise. - - file and line are the file number of the function setting the - lock. They can be useful for debugging. - */ - - if ((_ssl_locks == NULL) || - (n < 0) || (n >= _ssl_locks_count)) { - return; - } - - if (mode & CRYPTO_LOCK) { - cryptography_mutex_lock(_ssl_locks + n); - } else { - cryptography_mutex_unlock(_ssl_locks + n); - } -} - -static void init_mutexes(void) { - int i; - for (i = 0; i < _ssl_locks_count; i++) { - cryptography_mutex_init(_ssl_locks + i); - } -} - - -int Cryptography_setup_ssl_threads(void) { - if (_ssl_locks == NULL) { - _ssl_locks_count = CRYPTO_num_locks(); - _ssl_locks = calloc(_ssl_locks_count, sizeof(Cryptography_mutex)); - if (_ssl_locks == NULL) { - return 0; - } - init_mutexes(); - CRYPTO_set_locking_callback(_ssl_thread_locking_function); -#ifndef _WIN32 - pthread_atfork(NULL, NULL, &init_mutexes); -#endif - } - return 1; -} -#else -int (*Cryptography_setup_ssl_threads)(void) = NULL; -#endif - typedef struct { char *password; int length; diff --git a/src/_cffi_src/openssl/crypto.py b/src/_cffi_src/openssl/crypto.py index a5db642e89b3..6284f0579582 100644 --- a/src/_cffi_src/openssl/crypto.py +++ b/src/_cffi_src/openssl/crypto.py @@ -9,7 +9,6 @@ """ TYPES = """ -static const long Cryptography_HAS_LOCKING_CALLBACKS; static const long Cryptography_HAS_MEM_FUNCTIONS; static const long Cryptography_HAS_OPENSSL_CLEANUP; @@ -79,11 +78,6 @@ # define OPENSSL_PLATFORM SSLEAY_PLATFORM # define OPENSSL_DIR SSLEAY_DIR #endif -#if CRYPTOGRAPHY_IS_LIBRESSL -static const long Cryptography_HAS_LOCKING_CALLBACKS = 1; -#else -static const long Cryptography_HAS_LOCKING_CALLBACKS = 0; -#endif #if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_OPENSSL_CLEANUP = 0; diff --git a/src/cryptography/hazmat/bindings/openssl/_conditional.py b/src/cryptography/hazmat/bindings/openssl/_conditional.py index aeca166b2310..585c7366454e 100644 --- a/src/cryptography/hazmat/bindings/openssl/_conditional.py +++ b/src/cryptography/hazmat/bindings/openssl/_conditional.py @@ -74,12 +74,6 @@ def cryptography_has_tls_st(): ] -def cryptography_has_locking_callbacks(): - return [ - "Cryptography_setup_ssl_threads", - ] - - def cryptography_has_scrypt(): return [ "EVP_PBE_scrypt", @@ -284,7 +278,6 @@ def cryptography_has_srtp(): "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb, "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st, "Cryptography_HAS_TLS_ST": cryptography_has_tls_st, - "Cryptography_HAS_LOCKING_CALLBACKS": cryptography_has_locking_callbacks, "Cryptography_HAS_SCRYPT": cryptography_has_scrypt, "Cryptography_HAS_EVP_PKEY_DHX": cryptography_has_evp_pkey_dhx, "Cryptography_HAS_MEM_FUNCTIONS": cryptography_has_mem_functions, diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index 5fd3b0de9656..6593bf1dc1b1 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -113,7 +113,6 @@ class Binding(object): ffi = ffi _lib_loaded = False _init_lock = threading.Lock() - _lock_init_lock = threading.Lock() def __init__(self): self._ensure_ffi_initialized() @@ -146,22 +145,7 @@ def _ensure_ffi_initialized(cls): @classmethod def init_static_locks(cls): - with cls._lock_init_lock: - cls._ensure_ffi_initialized() - # Use Python's implementation if available, importing _ssl triggers - # the setup for this. - __import__("_ssl") - - if ( - not cls.lib.Cryptography_HAS_LOCKING_CALLBACKS - or cls.lib.CRYPTO_get_locking_callback() != cls.ffi.NULL - ): - return - - # If nothing else has setup a locking callback already, we set up - # our own - res = lib.Cryptography_setup_ssl_threads() - _openssl_assert(cls.lib, res == 1) + cls._ensure_ffi_initialized() def _verify_package_version(version): @@ -187,9 +171,4 @@ def _verify_package_version(version): _verify_package_version(cryptography.__version__) -# OpenSSL is not thread safe until the locks are initialized. We call this -# method in module scope so that it executes with the import lock. On -# Pythons < 3.4 this import lock is a global lock, which can prevent a race -# condition registering the OpenSSL locks. On Python 3.4+ the import lock -# is per module so this approach will not work. Binding.init_static_locks() diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py index 069452cbb091..4bc046b80fe4 100644 --- a/tests/hazmat/bindings/test_openssl.py +++ b/tests/hazmat/bindings/test_openssl.py @@ -22,18 +22,6 @@ def test_binding_loads(self): assert binding.lib assert binding.ffi - def test_crypto_lock_init(self): - b = Binding() - - b.init_static_locks() - lock_cb = b.lib.CRYPTO_get_locking_callback() - if not b.lib.CRYPTOGRAPHY_IS_LIBRESSL: - assert lock_cb == b.ffi.NULL - assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 0 - else: - assert lock_cb != b.ffi.NULL - assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 1 - def test_add_engine_more_than_once(self): b = Binding() b._register_osrandom_engine() From 23ce5638fdd6cd04ec982e7feec3140c474a9cef Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 11 Nov 2020 14:27:57 -0600 Subject: [PATCH 70/99] migrate more from Travis to GHA (#5555) * migrate more from Travis to GHA * actually upload coverage * use cache checks properly * also do coverage * simplify coverage check * refactor a bit * oops * remove unused things in travis * this needs to be stored to the github env... --- .github/workflows/build_openssl.sh | 36 +++++++++++++++ .github/workflows/ci.yml | 72 ++++++++++++++++++++++++++++++ .travis.yml | 50 --------------------- .travis/install.sh | 40 ----------------- .travis/openssl_config.sh | 13 ------ .travis/run.sh | 18 -------- .travis/upload_coverage.sh | 21 --------- 7 files changed, 108 insertions(+), 142 deletions(-) create mode 100755 .github/workflows/build_openssl.sh delete mode 100755 .travis/openssl_config.sh delete mode 100755 .travis/upload_coverage.sh diff --git a/.github/workflows/build_openssl.sh b/.github/workflows/build_openssl.sh new file mode 100755 index 000000000000..d5f23f61aa3a --- /dev/null +++ b/.github/workflows/build_openssl.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e +set -x + +shlib_sed() { + # modify the shlib version to a unique one to make sure the dynamic + # linker doesn't load the system one. + sed -i "s/^SHLIB_MAJOR=.*/SHLIB_MAJOR=100/" Makefile + sed -i "s/^SHLIB_MINOR=.*/SHLIB_MINOR=0.0/" Makefile + sed -i "s/^SHLIB_VERSION_NUMBER=.*/SHLIB_VERSION_NUMBER=100.0.0/" Makefile +} + +# CONFIG_HASH is a global coming from a previous step +OPENSSL_DIR="${GITHUB_WORKSPACE}/osslcache/${TYPE}-${VERSION}-${CONFIG_HASH}" +if [[ "${TYPE}" == "openssl" ]]; then + curl -O "https://www.openssl.org/source/openssl-${VERSION}.tar.gz" + tar zxf "openssl-${VERSION}.tar.gz" + pushd "openssl-${VERSION}" + # CONFIG_FLAGS is a global coming from a previous step + ./config ${CONFIG_FLAGS} -fPIC --prefix="${OPENSSL_DIR}" + shlib_sed + make depend + make -j"$(nproc)" + # avoid installing the docs on versions of OpenSSL that aren't ancient. + # https://github.com/openssl/openssl/issues/6685#issuecomment-403838728 + make install_sw install_ssldirs + popd +elif [[ "${TYPE}" == "libressl" ]]; then + curl -O "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${VERSION}.tar.gz" + tar zxf "libressl-${VERSION}.tar.gz" + pushd "libressl-${VERSION}" + ./config -Wl -Wl,-Bsymbolic-functions -fPIC shared --prefix="${OPENSSL_DIR}" + shlib_sed + make -j"$(nproc)" install + popd +fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1879f83b490..d4996232b1f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,78 @@ on: - '*.*.*' jobs: + linux: + runs-on: ubuntu-latest + strategy: + matrix: + PYTHON: + - {VERSION: "3.9", TOXENV: "pep8,packaging", COVERAGE: "false"} + - {VERSION: "pypy2", TOXENV: "pypy-nocoverage", COVERAGE: "false"} + - {VERSION: "pypy3", TOXENV: "pypy3-nocoverage", COVERAGE: "false"} + - {VERSION: "2.7", TOXENV: "py27", OPENSSL: {TYPE: "openssl", VERSION: "1.1.0l"}} + - {VERSION: "2.7", TOXENV: "py27-ssh", OPENSSL: {TYPE: "openssl", VERSION: "1.1.0l"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.0l"}} + - {VERSION: "2.7", TOXENV: "py27", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1h"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1h"}} + - {VERSION: "3.9", TOXENV: "py39-ssh", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1h"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1h", CONFIG_FLAGS: "no-engine no-rc2 no-srtp no-ct"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "2.9.2"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "3.0.2"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "3.1.4"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "3.2.2"}} + name: "${{ matrix.PYTHON.TOXENV }} ${{ matrix.PYTHON.OPENSSL.TYPE }} ${{ matrix.PYTHON.OPENSSL.VERSION }} ${{ matrix.PYTHON.OPENSSL.CONFIG_FLAGS }}" + steps: + - uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.PYTHON.VERSION }} + - run: git clone --depth=1 https://github.com/google/wycheproof + - run: python -m pip install tox requests coverage + - name: Compute config hash and set config vars + run: | + DEFAULT_CONFIG_FLAGS="shared no-ssl2 no-ssl3" + CONFIG_FLAGS="$DEFAULT_CONFIG_FLAGS $CONFIG_FLAGS" + CONFIG_HASH=$(echo "$CONFIG_FLAGS" | sha1sum | sed 's/ .*$//') + echo "CONFIG_FLAGS=${CONFIG_FLAGS}" >> $GITHUB_ENV + echo "CONFIG_HASH=${CONFIG_HASH}" >> $GITHUB_ENV + echo "OSSL_INFO=${{ matrix.PYTHON.OPENSSL.TYPE }}-${{ matrix.PYTHON.OPENSSL.VERSION }}-${CONFIG_FLAGS}" >> $GITHUB_ENV + echo "OSSL_PATH=${{ github.workspace }}/osslcache/${{ matrix.PYTHON.OPENSSL.TYPE }}-${{ matrix.PYTHON.OPENSSL.VERSION }}-${CONFIG_HASH}" >> $GITHUB_ENV + env: + CONFIG_FLAGS: ${{ matrix.PYTHON.OPENSSL.CONFIG_FLAGS }} + if: matrix.PYTHON.OPENSSL + - name: Load cache + uses: actions/cache@v2 + id: ossl-cache + with: + path: ${{ github.workspace }}/osslcache + # When altering the openssl build process you may need to increment the value on the end of this cache key + # so that you can prevent it from fetching the cache and skipping the build step. + key: ${{ matrix.PYTHON.OPENSSL.TYPE }}-${{ matrix.PYTHON.OPENSSL.VERSION }}-${{ env.CONFIG_HASH }}-1 + if: matrix.PYTHON.OPENSSL + - name: Build custom OpenSSL/LibreSSL + run: .github/workflows/build_openssl.sh + env: + TYPE: ${{ matrix.PYTHON.OPENSSL.TYPE }} + VERSION: ${{ matrix.PYTHON.OPENSSL.VERSION }} + GITHUB_WORKSPACE: ${{ github.workspace }} + if: matrix.PYTHON.OPENSSL && steps.ossl-cache.outputs.cache-hit != 'true' + - name: Set CFLAGS/LDFLAGS + run: | + echo "CFLAGS=${CFLAGS} -I${OSSL_PATH}/include" >> $GITHUB_ENV + echo "LDFLAGS=${LDFLAGS} -L${OSSL_PATH}/lib -Wl,-rpath=${OSSL_PATH}/lib" >> $GITHUB_ENV + if: matrix.PYTHON.OPENSSL + - name: Tests + run: | + tox -r -- --color=yes --wycheproof-root=wycheproof + env: + TOXENV: ${{ matrix.PYTHON.TOXENV }} + - 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 + bash codecov.sh -n "tox -e ${{ matrix.PYTHON.TOXENV }} ${{ env.OSSL_INFO }}" + if: matrix.PYTHON.COVERAGE != 'false' + linux-distros: runs-on: ubuntu-latest container: ${{ matrix.IMAGE.IMAGE }} diff --git a/.travis.yml b/.travis.yml index ef5e2d9038cb..03716a386594 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,42 +18,7 @@ branches: matrix: include: - - python: 3.8 - env: TOXENV=pep8,packaging # Setting 'python' is just to make travis's UI a bit prettier - - python: 3.6 - env: TOXENV=py36 - - python: 3.9 - env: TOXENV=py39 - # Travis lists available Pythons (including PyPy) by arch and distro here: - # https://docs.travis-ci.com/user/languages/python/#python-versions - - python: pypy2.7-7.3.1 - env: TOXENV=pypy-nocoverage - - python: pypy3.6-7.3.1 - env: TOXENV=pypy3-nocoverage - - python: 2.7 - env: TOXENV=py27 OPENSSL=1.1.0l - - python: 2.7 - env: TOXENV=py27-ssh OPENSSL=1.1.0l - - python: 3.8 - env: TOXENV=py38 OPENSSL=1.1.0l - - python: 2.7 - env: TOXENV=py27 OPENSSL=1.1.1h - - python: 3.8 - env: TOXENV=py38 OPENSSL=1.1.1h - - python: 3.8 - env: TOXENV=py38 OPENSSL=1.1.1h OPENSSL_CONFIG_FLAGS="no-engine no-rc2 no-srtp no-ct" - - python: 3.8 - env: TOXENV=py38-ssh OPENSSL=1.1.1h - - python: 3.8 - env: TOXENV=py38 LIBRESSL=2.9.2 - - python: 3.8 - env: TOXENV=py38 LIBRESSL=3.0.2 - - python: 3.8 - env: TOXENV=py38 LIBRESSL=3.1.4 - - python: 3.8 - env: TOXENV=py38 LIBRESSL=3.2.2 - - python: 3.8 env: TOXENV=docs addons: @@ -74,18 +39,3 @@ install: script: - ./.travis/run.sh - -after_success: - - ./.travis/upload_coverage.sh - -notifications: - irc: - channels: - # This is set to a secure variable to prevent forks from notifying the - # IRC channel whenever they fail a build. This can be removed when travis - # implements https://github.com/travis-ci/travis-ci/issues/1094. - # The value encrypted here was created via - # travis encrypt "irc.freenode.org#cryptography-dev" - - secure: "A93qvTOlwlMK5WoEvZQ5jQ8Z4Hd0JpeO53WYt8iIJ3s/L6AubkfiN7gwhThRtPnPx7DVMenoKRMlcRg76/ICvXEViVnGgXFjsypF0CzVcIay9pPdjpZjZHP735yLfX512RtxYEdEGwi5r25Z2CEFaydhhxNwfuMxGBtLUjusix4=" - use_notice: true - skip_join: true diff --git a/.travis/install.sh b/.travis/install.sh index ccc7f444dfe8..573399c1c2ab 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -3,46 +3,6 @@ set -e set -x -SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}") - -shlib_sed() { - # modify the shlib version to a unique one to make sure the dynamic - # linker doesn't load the system one. - sed -i "s/^SHLIB_MAJOR=.*/SHLIB_MAJOR=100/" Makefile - sed -i "s/^SHLIB_MINOR=.*/SHLIB_MINOR=0.0/" Makefile - sed -i "s/^SHLIB_VERSION_NUMBER=.*/SHLIB_VERSION_NUMBER=100.0.0/" Makefile -} - -# download, compile, and install if it's not already present via travis -# cache -if [ -n "${OPENSSL}" ]; then - . "$SCRIPT_DIR/openssl_config.sh" - if [[ ! -f "$HOME/$OPENSSL_DIR/bin/openssl" ]]; then - curl -O "https://www.openssl.org/source/openssl-${OPENSSL}.tar.gz" - tar zxf "openssl-${OPENSSL}.tar.gz" - pushd "openssl-${OPENSSL}" - ./config $OPENSSL_CONFIG_FLAGS -fPIC --prefix="$HOME/$OPENSSL_DIR" - shlib_sed - make depend - make -j"$(nproc)" - # avoid installing the docs on versions of OpenSSL that aren't ancient. - # https://github.com/openssl/openssl/issues/6685#issuecomment-403838728 - make install_sw install_ssldirs - popd - fi -elif [ -n "${LIBRESSL}" ]; then - LIBRESSL_DIR="ossl-2/${LIBRESSL}" - if [[ ! -f "$HOME/$LIBRESSL_DIR/bin/openssl" ]]; then - curl -O "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL}.tar.gz" - tar zxf "libressl-${LIBRESSL}.tar.gz" - pushd "libressl-${LIBRESSL}" - ./config -Wl -Wl,-Bsymbolic-functions -fPIC shared --prefix="$HOME/$LIBRESSL_DIR" - shlib_sed - make -j"$(nproc)" install - popd - fi -fi - if [ -z "${DOWNSTREAM}" ]; then git clone --depth=1 https://github.com/google/wycheproof "$HOME/wycheproof" fi diff --git a/.travis/openssl_config.sh b/.travis/openssl_config.sh deleted file mode 100755 index 83f16d2bfea8..000000000000 --- a/.travis/openssl_config.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -e -set -x - -DEFAULT_CONFIG_FLAGS="shared no-ssl2 no-ssl3" -if [ -n "${OPENSSL_CONFIG_FLAGS}" ]; then - OPENSSL_CONFIG_FLAGS="$DEFAULT_CONFIG_FLAGS $OPENSSL_CONFIG_FLAGS" -else - OPENSSL_CONFIG_FLAGS=$DEFAULT_CONFIG_FLAGS -fi -CONFIG_HASH=$(echo "$OPENSSL_CONFIG_FLAGS" | sha1sum | sed 's/ .*$//') -OPENSSL_DIR="ossl-2/${OPENSSL}${CONFIG_HASH}" diff --git a/.travis/run.sh b/.travis/run.sh index 313fb86b6d8d..41fc4a49a0d7 100755 --- a/.travis/run.sh +++ b/.travis/run.sh @@ -1,23 +1,5 @@ #!/bin/bash -ex -SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}") - -if [ -n "${LIBRESSL}" ]; then - LIBRESSL_DIR="ossl-2/${LIBRESSL}" - export CFLAGS="-Werror -Wno-error=deprecated-declarations -Wno-error=discarded-qualifiers -Wno-error=unused-function -I$HOME/$LIBRESSL_DIR/include" - export PATH="$HOME/$LIBRESSL_DIR/bin:$PATH" - export LDFLAGS="-L$HOME/$LIBRESSL_DIR/lib -Wl,-rpath=$HOME/$LIBRESSL_DIR/lib" -fi - -if [ -n "${OPENSSL}" ]; then - . "$SCRIPT_DIR/openssl_config.sh" - export PATH="$HOME/$OPENSSL_DIR/bin:$PATH" - export CFLAGS="${CFLAGS} -I$HOME/$OPENSSL_DIR/include" - # rpath on linux will cause it to use an absolute path so we don't need to - # do LD_LIBRARY_PATH - export LDFLAGS="-L$HOME/$OPENSSL_DIR/lib -Wl,-rpath=$HOME/$OPENSSL_DIR/lib" -fi - source ~/.venv/bin/activate if [ -n "${TOXENV}" ]; then diff --git a/.travis/upload_coverage.sh b/.travis/upload_coverage.sh deleted file mode 100755 index 28251f8ba010..000000000000 --- a/.travis/upload_coverage.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -set -e -set -x - -if [ -n "${TOXENV}" ]; then - case "${TOXENV}" in - pypy-nocoverage);; - pypy3-nocoverage);; - pep8);; - py3pep8);; - docs);; - *) - source ~/.venv/bin/activate - 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 - - bash codecov.sh -Z -e TRAVIS_OS_NAME,TOXENV,OPENSSL || \ - bash codecov.sh -Z -e TRAVIS_OS_NAME,TOXENV,OPENSSL - ;; - esac -fi From ce9645a9c75acb954709ce952931129c0f4fa345 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 11 Nov 2020 16:37:42 -0500 Subject: [PATCH 71/99] garbage collect dead code (#5562) --- src/_cffi_src/openssl/crypto.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/_cffi_src/openssl/crypto.py b/src/_cffi_src/openssl/crypto.py index 6284f0579582..6064a4eeea99 100644 --- a/src/_cffi_src/openssl/crypto.py +++ b/src/_cffi_src/openssl/crypto.py @@ -27,10 +27,6 @@ FUNCTIONS = """ void OPENSSL_cleanup(void); -/* as of 1.1.0 OpenSSL does its own locking *angelic chorus*. This function - is now a noop macro. We can delete this once we drop 1.0.2 support. */ -void (*CRYPTO_get_locking_callback(void))(int, int, const char *, int); - /* SSLeay was removed in 1.1.0 */ unsigned long SSLeay(void); const char *SSLeay_version(int); From a07da37f14fdd8e856bfef78bf81b46faff335c4 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 11 Nov 2020 17:56:58 -0600 Subject: [PATCH 72/99] port docs and docs-linkcheck (#5563) * port docs and docs-linkcheck * allow linkcheck if the commit msg says linkcheck combine docs job into lint jobs * can't get the commit msg at this time on a PR --- .github/workflows/ci.yml | 17 ++++++++++++++++- .travis.yml | 10 ---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4996232b1f7..19922890677c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: PYTHON: - - {VERSION: "3.9", TOXENV: "pep8,packaging", COVERAGE: "false"} + - {VERSION: "3.9", TOXENV: "pep8,packaging,docs", COVERAGE: "false"} - {VERSION: "pypy2", TOXENV: "pypy-nocoverage", COVERAGE: "false"} - {VERSION: "pypy3", TOXENV: "pypy3-nocoverage", COVERAGE: "false"} - {VERSION: "2.7", TOXENV: "py27", OPENSSL: {TYPE: "openssl", VERSION: "1.1.0l"}} @@ -230,3 +230,18 @@ jobs: - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh install - run: pip install . - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh run + + docs-linkcheck: + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + name: "linkcheck" + steps: + - uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - run: python -m pip install -U tox + - run: tox -r -- --color=yes + env: + TOXENV: docs-linkcheck diff --git a/.travis.yml b/.travis.yml index 03716a386594..873a432cd650 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,16 +19,6 @@ branches: matrix: include: # Setting 'python' is just to make travis's UI a bit prettier - - python: 3.8 - env: TOXENV=docs - addons: - apt: - packages: - - libenchant-dev - - python: 3.8 - env: TOXENV=docs-linkcheck - if: (branch = master AND type != pull_request) OR commit_message =~ /linkcheck/ - # TODO: This needs to be moved to GHA, but it fails there. The # confusing part is why it passes on Travis! - python: 3.7 From 9e081513a09035cf2710e84bfc96b8f5a0a611d0 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 11 Nov 2020 19:49:28 -0500 Subject: [PATCH 73/99] Garbage collect more CI code (#5564) --- .travis/install.sh | 4 ---- .travis/run.sh | 16 ++++++---------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/.travis/install.sh b/.travis/install.sh index 573399c1c2ab..4109cd135237 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -3,10 +3,6 @@ set -e set -x -if [ -z "${DOWNSTREAM}" ]; then - git clone --depth=1 https://github.com/google/wycheproof "$HOME/wycheproof" -fi - pip install -U pip pip install virtualenv diff --git a/.travis/run.sh b/.travis/run.sh index 41fc4a49a0d7..1b9200b9e4c8 100755 --- a/.travis/run.sh +++ b/.travis/run.sh @@ -2,14 +2,10 @@ source ~/.venv/bin/activate -if [ -n "${TOXENV}" ]; then - tox -- --wycheproof-root="$HOME/wycheproof" -else - downstream_script="${TRAVIS_BUILD_DIR}/.travis/downstream.d/${DOWNSTREAM}.sh" - if [ ! -x "$downstream_script" ]; then - exit 1 - fi - $downstream_script install - pip install . - $downstream_script run +downstream_script="${TRAVIS_BUILD_DIR}/.travis/downstream.d/${DOWNSTREAM}.sh" +if [ ! -x "$downstream_script" ]; then + exit 1 fi +$downstream_script install +pip install . +$downstream_script run From 548b1b2d40f6ab94072871deb90e29e05dc520c4 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 12 Nov 2020 12:31:10 -0500 Subject: [PATCH 74/99] Added python2 removal to the changelog (#5567) * Added python2 removal to the changelog * Update CHANGELOG.rst --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b001ec266c6a..3ea22fd0aa30 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,8 @@ Changelog 64-bit to 1024-bit (8 byte to 128 byte) initialization vectors. This change is to conform with an upcoming OpenSSL release that will no longer support sizes outside this window. +* Python 2 support is deprecated in ``cryptography``. This is the last release + that will support Python 2. .. _v3-2-1: From eb02f2127f427dcb53d7ca2b0918844ecf5ca872 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 13 Nov 2020 11:13:30 -0500 Subject: [PATCH 75/99] Update artifact name for changes from pyca-infra (#5569) --- .github/workflows/ci.yml | 6 +++--- .github/workflows/download_openssl.py | 1 + .github/workflows/wheel-builder.yml | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19922890677c..a0c1625b74fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -140,14 +140,14 @@ jobs: - name: Download OpenSSL run: | - python .github/workflows/download_openssl.py macos openssl-macos + python .github/workflows/download_openssl.py macos openssl-macos-x86-64 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Tests run: | CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1 \ - LDFLAGS="${HOME}/openssl-macos/lib/libcrypto.a ${HOME}/openssl-macos/lib/libssl.a" \ - CFLAGS="-I${HOME}/openssl-macos/include -Werror -Wno-error=deprecated-declarations -Wno-error=incompatible-pointer-types-discards-qualifiers -Wno-error=unused-function -Wno-error=unused-command-line-argument -mmacosx-version-min=10.10 -march=core2 $EXTRA_CFLAGS" \ + LDFLAGS="${HOME}/openssl-macos-x86-64/lib/libcrypto.a ${HOME}/openssl-macos-x86-64/lib/libssl.a" \ + CFLAGS="-I${HOME}/openssl-macos-x86-64/include -Werror -Wno-error=deprecated-declarations -Wno-error=incompatible-pointer-types-discards-qualifiers -Wno-error=unused-function -Wno-error=unused-command-line-argument -mmacosx-version-min=10.10 -march=core2 $EXTRA_CFLAGS" \ tox -r -- --color=yes --wycheproof-root=wycheproof env: TOXENV: ${{ matrix.PYTHON.TOXENV }} diff --git a/.github/workflows/download_openssl.py b/.github/workflows/download_openssl.py index 1d5e3bfb471a..46b3b7f0ee02 100644 --- a/.github/workflows/download_openssl.py +++ b/.github/workflows/download_openssl.py @@ -67,6 +67,7 @@ def main(platform, target): os.path.join(path, artifact["name"]) ) return + raise ValueError("Didn't find {} in {}".format(target, response)) if __name__ == "__main__": diff --git a/.github/workflows/wheel-builder.yml b/.github/workflows/wheel-builder.yml index c4bf9ab057d3..b74edc2da5a4 100644 --- a/.github/workflows/wheel-builder.yml +++ b/.github/workflows/wheel-builder.yml @@ -76,7 +76,7 @@ jobs: - run: ${{ matrix.PYTHON.BIN_PATH }} -m pip install -U virtualenv requests - name: Download OpenSSL run: | - ${{ matrix.PYTHON.BIN_PATH }} .github/workflows/download_openssl.py macos openssl-macos + ${{ matrix.PYTHON.BIN_PATH }} .github/workflows/download_openssl.py macos openssl-macos-x86-64 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -92,8 +92,8 @@ jobs: cd cryptography* CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS="1" \ - LDFLAGS="${HOME}/openssl-macos/lib/libcrypto.a ${HOME}/openssl-macos/lib/libssl.a" \ - CFLAGS="-I${HOME}/openssl-macos/include -mmacosx-version-min=10.10 -march=core2" \ + LDFLAGS="${HOME}/openssl-macos-x86-64/lib/libcrypto.a ${HOME}/openssl-macos-x86-64/lib/libssl.a" \ + CFLAGS="-I${HOME}/openssl-macos-x86-64/include -mmacosx-version-min=10.10 -march=core2" \ ../venv/bin/python setup.py bdist_wheel $PY_LIMITED_API && mv dist/cryptography*.whl ../wheelhouse - run: venv/bin/pip install -f wheelhouse --no-index cryptography - run: | From 488cd740bb9502af7faad79c0575c5454045e0d0 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 13 Nov 2020 12:25:43 -0500 Subject: [PATCH 76/99] Remove two linkcheck ignores (#5570) --- docs/conf.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 87e2b5869c6c..33240d8de1a3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -199,8 +199,4 @@ r"https://info.isl.ntt.co.jp/crypt/eng/camellia/", # Inconsistent small DH params they seem incapable of fixing r"https://www.secg.org/sec1-v2.pdf", - # 403ing from Travis - r"https://devblogs.microsoft.com/oldnewthing/\?p=4223", - # Incomplete cert chain - r"https://cveform.mitre.org/", ] From d22bdc87eede21e5ed592c1194e6a7002ef8a76a Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 13 Nov 2020 19:55:05 -0500 Subject: [PATCH 77/99] Move paramiko job to github actions (#5565) --- {.travis => .github}/downstream.d/paramiko.sh | 0 .github/workflows/ci.yml | 2 ++ .travis.yml | 31 ------------------- .travis/install.sh | 12 ------- .travis/run.sh | 11 ------- MANIFEST.in | 2 -- README.rst | 3 -- tox.ini | 2 +- 8 files changed, 3 insertions(+), 60 deletions(-) rename {.travis => .github}/downstream.d/paramiko.sh (100%) delete mode 100644 .travis.yml delete mode 100755 .travis/install.sh delete mode 100755 .travis/run.sh diff --git a/.travis/downstream.d/paramiko.sh b/.github/downstream.d/paramiko.sh similarity index 100% rename from .travis/downstream.d/paramiko.sh rename to .github/downstream.d/paramiko.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0c1625b74fb..f786bd600c06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -213,6 +213,7 @@ jobs: strategy: matrix: DOWNSTREAM: + - paramiko - pyopenssl - twisted - aws-encryption-sdk @@ -228,6 +229,7 @@ jobs: python-version: 3.7 - run: python -m pip install -U pip wheel - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh install + - run: pip uninstall -y enum34 - run: pip install . - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh run diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 873a432cd650..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -sudo: true -dist: focal - -language: python - -cache: - directories: - - $HOME/.cache/pip - - $HOME/ossl-2/ - -# Only build master, the version branches (e.g. 1.7.x), and -# version tags (which are apparently considered branches by travis) -branches: - only: - - master - - /^\d+\.\d+\.x$/ - - /^\d+\.\d+(\.\d+)?$/ - -matrix: - include: - # Setting 'python' is just to make travis's UI a bit prettier - # TODO: This needs to be moved to GHA, but it fails there. The - # confusing part is why it passes on Travis! - - python: 3.7 - env: DOWNSTREAM=paramiko - -install: - - ./.travis/install.sh - -script: - - ./.travis/run.sh diff --git a/.travis/install.sh b/.travis/install.sh deleted file mode 100755 index 4109cd135237..000000000000 --- a/.travis/install.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -set -e -set -x - -pip install -U pip -pip install virtualenv - -python -m virtualenv ~/.venv -source ~/.venv/bin/activate -# If we pin coverage it must be kept in sync with tox.ini and .github/workflows/ci.yml -pip install tox coverage diff --git a/.travis/run.sh b/.travis/run.sh deleted file mode 100755 index 1b9200b9e4c8..000000000000 --- a/.travis/run.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -ex - -source ~/.venv/bin/activate - -downstream_script="${TRAVIS_BUILD_DIR}/.travis/downstream.d/${DOWNSTREAM}.sh" -if [ ! -x "$downstream_script" ]; then - exit 1 -fi -$downstream_script install -pip install . -$downstream_script run diff --git a/MANIFEST.in b/MANIFEST.in index 2b90d2465543..5c82725a4148 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -16,8 +16,6 @@ recursive-include tests *.py exclude vectors recursive-exclude vectors * -exclude .travis.yml .travis -recursive-exclude .travis * recursive-exclude .github * exclude release.py .coveragerc codecov.yml .readthedocs.yml dev-requirements.txt rtd-requirements.txt tox.ini diff --git a/README.rst b/README.rst index 586cb1b0b3bc..10de198b8af4 100644 --- a/README.rst +++ b/README.rst @@ -9,9 +9,6 @@ pyca/cryptography :target: https://cryptography.io :alt: Latest Docs -.. image:: https://travis-ci.org/pyca/cryptography.svg?branch=master - :target: https://travis-ci.org/pyca/cryptography - .. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=master :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amaster diff --git a/tox.ini b/tox.ini index 2ecd30a222bb..e6e04575bbc6 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,7 @@ extras = test ssh: ssh deps = - # This must be kept in sync with .travis/install.sh and .github/workflows/ci.yml + # This must be kept in sync with .github/workflows/ci.yml coverage ./vectors randomorder: pytest-randomly From 239fddf2d97088251a5c5b1a5ee7306319776898 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 19 Nov 2020 14:07:47 -0500 Subject: [PATCH 78/99] Polish up the fernet limitations language (#5577) --- docs/fernet.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/fernet.rst b/docs/fernet.rst index 960f47137850..5e2655d3a72b 100644 --- a/docs/fernet.rst +++ b/docs/fernet.rst @@ -274,8 +274,9 @@ Limitations ----------- Fernet is ideal for encrypting data that easily fits in memory. As a design -feature it does not expose unauthenticated bytes. Unfortunately, this makes it -generally unsuitable for very large files at this time. +feature it does not expose unauthenticated bytes. This means that the complete +message contents must be available in memory, making Fernet generally +unsuitable for very large files at this time. .. _`Fernet`: https://github.com/fernet/spec/ From fd582e8913a4c061ea35025a528331f9ac851651 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 20 Nov 2020 00:48:13 -0500 Subject: [PATCH 79/99] Debian sid has python 3.9 now (#5580) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f786bd600c06..5c2ed80871e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,7 +94,7 @@ jobs: - {IMAGE: "pyca/cryptography-runner-stretch", TOXENV: "py27"} - {IMAGE: "pyca/cryptography-runner-buster", TOXENV: "py37"} - {IMAGE: "pyca/cryptography-runner-bullseye", TOXENV: "py38"} - - {IMAGE: "pyca/cryptography-runner-sid", TOXENV: "py38"} + - {IMAGE: "pyca/cryptography-runner-sid", TOXENV: "py39"} - {IMAGE: "pyca/cryptography-runner-ubuntu-bionic", TOXENV: "py36"} - {IMAGE: "pyca/cryptography-runner-ubuntu-focal", TOXENV: "py38"} - {IMAGE: "pyca/cryptography-runner-ubuntu-rolling", TOXENV: "py27"} From 2b85c4d9156ab4bd44e55b2843bd1233a7a5d9ef Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 20 Nov 2020 12:22:47 -0500 Subject: [PATCH 80/99] Simplify wycheproof pytest code (#5579) --- tests/conftest.py | 4 +--- tests/utils.py | 7 ------- tests/wycheproof/test_utils.py | 11 +---------- 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 4e3124fa76ea..ece7a11e716a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,7 +11,6 @@ from .utils import ( check_backend_support, load_wycheproof_tests, - skip_if_wycheproof_none, ) @@ -30,8 +29,7 @@ def pytest_addoption(parser): def pytest_generate_tests(metafunc): if "wycheproof" in metafunc.fixturenames: - wycheproof = metafunc.config.getoption("--wycheproof-root") - skip_if_wycheproof_none(wycheproof) + wycheproof = metafunc.config.getoption("--wycheproof-root", skip=True) testcases = [] marker = metafunc.definition.get_closest_marker("wycheproof_tests") diff --git a/tests/utils.py b/tests/utils.py index 5d98af00e337..497fde83f0a5 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -906,13 +906,6 @@ def has_flag(self, flag): return flag in self.testcase["flags"] -def skip_if_wycheproof_none(wycheproof): - # This is factored into its own function so we can easily test both - # branches - if wycheproof is None: - pytest.skip("--wycheproof-root not provided") - - def load_wycheproof_tests(wycheproof, test_file): path = os.path.join(wycheproof, "testvectors", test_file) with open(path) as f: diff --git a/tests/wycheproof/test_utils.py b/tests/wycheproof/test_utils.py index 2cf3be08e97c..593d26bdec91 100644 --- a/tests/wycheproof/test_utils.py +++ b/tests/wycheproof/test_utils.py @@ -4,18 +4,9 @@ from __future__ import absolute_import, division, print_function -import pytest - -from ..utils import WycheproofTest, skip_if_wycheproof_none +from ..utils import WycheproofTest def test_wycheproof_test_repr(): wycheproof = WycheproofTest({}, {}, {"tcId": 3}) assert repr(wycheproof) == "" - - -def test_skip_if_wycheproof_none(): - with pytest.raises(pytest.skip.Exception): - skip_if_wycheproof_none(None) - - skip_if_wycheproof_none("abc") From 21144be3049bfe567eed0f2e59027fe5d5489ca5 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 21 Nov 2020 20:38:08 -0500 Subject: [PATCH 81/99] Simplify CI scripts (#5582) --- .github/workflows/build_openssl.sh | 8 +++----- .github/workflows/ci.yml | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_openssl.sh b/.github/workflows/build_openssl.sh index d5f23f61aa3a..99c3f4d33805 100755 --- a/.github/workflows/build_openssl.sh +++ b/.github/workflows/build_openssl.sh @@ -10,18 +10,16 @@ shlib_sed() { sed -i "s/^SHLIB_VERSION_NUMBER=.*/SHLIB_VERSION_NUMBER=100.0.0/" Makefile } -# CONFIG_HASH is a global coming from a previous step -OPENSSL_DIR="${GITHUB_WORKSPACE}/osslcache/${TYPE}-${VERSION}-${CONFIG_HASH}" if [[ "${TYPE}" == "openssl" ]]; then curl -O "https://www.openssl.org/source/openssl-${VERSION}.tar.gz" tar zxf "openssl-${VERSION}.tar.gz" pushd "openssl-${VERSION}" # CONFIG_FLAGS is a global coming from a previous step - ./config ${CONFIG_FLAGS} -fPIC --prefix="${OPENSSL_DIR}" + ./config ${CONFIG_FLAGS} -fPIC --prefix="${OSSL_PATH}" shlib_sed make depend make -j"$(nproc)" - # avoid installing the docs on versions of OpenSSL that aren't ancient. + # avoid installing the docs (for performance) # https://github.com/openssl/openssl/issues/6685#issuecomment-403838728 make install_sw install_ssldirs popd @@ -29,7 +27,7 @@ elif [[ "${TYPE}" == "libressl" ]]; then curl -O "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${VERSION}.tar.gz" tar zxf "libressl-${VERSION}.tar.gz" pushd "libressl-${VERSION}" - ./config -Wl -Wl,-Bsymbolic-functions -fPIC shared --prefix="${OPENSSL_DIR}" + ./config -Wl -Wl,-Bsymbolic-functions -fPIC shared --prefix="${OSSL_PATH}" shlib_sed make -j"$(nproc)" install popd diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c2ed80871e4..2cd2d9c0149d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,6 @@ jobs: env: TYPE: ${{ matrix.PYTHON.OPENSSL.TYPE }} VERSION: ${{ matrix.PYTHON.OPENSSL.VERSION }} - GITHUB_WORKSPACE: ${{ github.workspace }} if: matrix.PYTHON.OPENSSL && steps.ossl-cache.outputs.cache-hit != 'true' - name: Set CFLAGS/LDFLAGS run: | From 5cd265aca0e90675ccf7e93bd900c9845fe7878b Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 23 Nov 2020 19:33:44 -0500 Subject: [PATCH 82/99] Start refactoring github actions to reduce duplication (#5583) --- .github/actions/upload-coverage/action.yml | 19 +++++++++++++++ .github/workflows/ci.yml | 28 ++++++++++------------ 2 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 .github/actions/upload-coverage/action.yml diff --git a/.github/actions/upload-coverage/action.yml b/.github/actions/upload-coverage/action.yml new file mode 100644 index 000000000000..11d97a7a614d --- /dev/null +++ b/.github/actions/upload-coverage/action.yml @@ -0,0 +1,19 @@ +name: Upload Coverage +description: Upload coverage to codecov + +inputs: + name: + description: "Job name" + required: true + +runs: + using: "composite" + + steps: + - 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 + + bash codecov.sh -n "${{ inputs.name }}" + shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cd2d9c0149d..10f9eb47d326 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,10 +75,9 @@ jobs: tox -r -- --color=yes --wycheproof-root=wycheproof env: TOXENV: ${{ matrix.PYTHON.TOXENV }} - - 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 - bash codecov.sh -n "tox -e ${{ matrix.PYTHON.TOXENV }} ${{ env.OSSL_INFO }}" + - uses: ./.github/actions/upload-coverage + with: + name: "tox -e ${{ matrix.PYTHON.TOXENV }} ${{ env.OSSL_INFO }}" if: matrix.PYTHON.COVERAGE != 'false' linux-distros: @@ -112,10 +111,9 @@ jobs: - run: 'tox -- --wycheproof-root="$HOME/wycheproof"' env: TOXENV: ${{ matrix.IMAGE.TOXENV }} - - 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 - bash codecov.sh -n "tox -e ${{ matrix.IMAGE.TOXENV }} on ${{ matrix.IMAGE.IMAGE }} ${{ matrix.IMAGE.ENV }}" + - uses: ./.github/actions/upload-coverage + with: + name: "tox -e ${{ matrix.IMAGE.TOXENV }} on ${{ matrix.IMAGE.IMAGE }} ${{ matrix.IMAGE.ENV }}" macos: runs-on: macos-latest @@ -152,10 +150,9 @@ jobs: TOXENV: ${{ matrix.PYTHON.TOXENV }} EXTRA_CFLAGS: ${{ matrix.PYTHON.EXTRA_CFLAGS }} - - 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 - bash codecov.sh -n "Python ${{ matrix.PYTHON.VERSION }} on macOS" + - uses: ./.github/actions/upload-coverage + with: + name: "Python ${{ matrix.PYTHON.VERSION }} on macOS" windows: runs-on: windows-latest @@ -202,10 +199,9 @@ jobs: env: TOXENV: ${{ matrix.PYTHON.TOXENV }} - - 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 - bash codecov.sh -n "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}" + - uses: ./.github/actions/upload-coverage + with: + name: "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}" linux-downstream: runs-on: ubuntu-latest From 417f684f6109357a97eee013de7d10fade25bdf7 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Thu, 26 Nov 2020 11:10:44 -0600 Subject: [PATCH 83/99] in OpenSSL 1.1.0+ error strings are automatically loaded (#5587) --- src/cryptography/hazmat/bindings/openssl/binding.py | 2 -- tests/hazmat/backends/test_openssl.py | 5 ----- 2 files changed, 7 deletions(-) diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index 6593bf1dc1b1..7a84a340e43b 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -139,8 +139,6 @@ def _ensure_ffi_initialized(cls): cls.lib.SSL_library_init() # adds all ciphers/digests for EVP cls.lib.OpenSSL_add_all_algorithms() - # loads error strings for libcrypto and libssl functions - cls.lib.SSL_load_error_strings() cls._register_osrandom_engine() @classmethod diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index e4bf7f97c6f5..eab868fad3a6 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -127,11 +127,6 @@ def test_evp_ciphers_registered(self): cipher = backend._lib.EVP_get_cipherbyname(b"aes-256-cbc") assert cipher != backend._ffi.NULL - def test_error_strings_loaded(self): - buf = backend._ffi.new("char[]", 256) - backend._lib.ERR_error_string_n(101183626, buf, len(buf)) - assert b"data not multiple of block length" in backend._ffi.string(buf) - def test_unknown_error_in_cipher_finalize(self): cipher = Cipher(AES(b"\0" * 16), CBC(b"\0" * 16), backend=backend) enc = cipher.encryptor() From d890e2a60616af098d6ec1d4e4a53cc82a335731 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Thu, 26 Nov 2020 11:52:47 -0600 Subject: [PATCH 84/99] define OAEP properties for all openssl versions (#5589) In 3.0 these aren't macros so we can't test this way. All our supported OpenSSLs have these bindings now and LibreSSL does not. --- src/_cffi_src/openssl/rsa.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/_cffi_src/openssl/rsa.py b/src/_cffi_src/openssl/rsa.py index 9298226bb223..92b8fa4600d8 100644 --- a/src/_cffi_src/openssl/rsa.py +++ b/src/_cffi_src/openssl/rsa.py @@ -48,17 +48,13 @@ """ CUSTOMIZATIONS = """ -#if defined(EVP_PKEY_CTX_set_rsa_oaep_md) +#if !CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_RSA_OAEP_MD = 1; -#else -static const long Cryptography_HAS_RSA_OAEP_MD = 0; -int (*EVP_PKEY_CTX_set_rsa_oaep_md)(EVP_PKEY_CTX *, EVP_MD *) = NULL; -#endif - -#if defined(EVP_PKEY_CTX_set0_rsa_oaep_label) static const long Cryptography_HAS_RSA_OAEP_LABEL = 1; #else +static const long Cryptography_HAS_RSA_OAEP_MD = 0; static const long Cryptography_HAS_RSA_OAEP_LABEL = 0; +int (*EVP_PKEY_CTX_set_rsa_oaep_md)(EVP_PKEY_CTX *, EVP_MD *) = NULL; int (*EVP_PKEY_CTX_set0_rsa_oaep_label)(EVP_PKEY_CTX *, unsigned char *, int) = NULL; #endif From ac4c22168f196921bfe00348250ff138e64bcd37 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Thu, 26 Nov 2020 13:07:25 -0600 Subject: [PATCH 85/99] Reduce granularity of error msging when deserializing keys (#5588) * Reduce granularity of error msging when deserializing keys In OpenSSL 3.0 it is no longer possible to determine whether the reason a key failed to deserialize is because of an unsupported cipher. Since we want to be more resilient to OpenSSL error code instability we'll just remove these paths. * black * changelog and update docs --- CHANGELOG.rst | 4 +++ .../primitives/asymmetric/serialization.rst | 8 +++--- .../hazmat/backends/openssl/backend.py | 26 +++++++------------ tests/hazmat/primitives/test_serialization.py | 7 +++-- 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3ea22fd0aa30..1f1ffd99589b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,10 @@ Changelog 64-bit to 1024-bit (8 byte to 128 byte) initialization vectors. This change is to conform with an upcoming OpenSSL release that will no longer support sizes outside this window. +* **BACKWARDS INCOMPATIBLE:** When deserializing asymmetric keys we now + raise ``ValueError`` rather than ``UnsupportedAlgorithm`` when an + unsupported cipher is used. This change is to conform with an upcoming + OpenSSL release that will no longer distinguish between error types. * Python 2 support is deprecated in ``cryptography``. This is the last release that will support Python 2. diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index 6b2e858db9af..8811646045f6 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -158,8 +158,7 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END password was supplied. :raises cryptography.exceptions.UnsupportedAlgorithm: If the serialized key - is of a type that is not supported by the backend or if the key is - encrypted with a symmetric cipher that is not supported by the backend. + is of a type that is not supported by the backend. .. function:: load_pem_public_key(data, backend=None) @@ -267,9 +266,8 @@ the rest. not encrypted. Or if the key was encrypted but no password was supplied. - :raises cryptography.exceptions.UnsupportedAlgorithm: If the serialized key is of a type that - is not supported by the backend or if the key is encrypted with a - symmetric cipher that is not supported by the backend. + :raises cryptography.exceptions.UnsupportedAlgorithm: If the serialized key + is of a type that is not supported by the backend. .. doctest:: diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index f2b7230bbaa7..e3b5718f2efb 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1491,8 +1491,11 @@ def _handle_key_loading_error(self): errors = self._consume_errors() if not errors: - raise ValueError("Could not deserialize key data.") - + raise ValueError( + "Could not deserialize key data. The data may be in an " + "incorrect format or it may be encrypted with an unsupported " + "algorithm." + ) elif errors[0]._lib_reason_match( self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT ) or errors[0]._lib_reason_match( @@ -1501,16 +1504,6 @@ def _handle_key_loading_error(self): ): raise ValueError("Bad decrypt. Incorrect password?") - elif errors[0]._lib_reason_match( - self._lib.ERR_LIB_EVP, self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM - ) or errors[0]._lib_reason_match( - self._lib.ERR_LIB_PEM, self._lib.PEM_R_UNSUPPORTED_ENCRYPTION - ): - raise UnsupportedAlgorithm( - "PEM data is encrypted with an unsupported cipher", - _Reasons.UNSUPPORTED_CIPHER, - ) - elif any( error._lib_reason_match( self._lib.ERR_LIB_EVP, @@ -1521,12 +1514,11 @@ def _handle_key_loading_error(self): raise ValueError("Unsupported public key algorithm.") else: - assert errors[0].lib in ( - self._lib.ERR_LIB_EVP, - self._lib.ERR_LIB_PEM, - self._lib.ERR_LIB_ASN1, + raise ValueError( + "Could not deserialize key data. The data may be in an " + "incorrect format or it may be encrypted with an unsupported " + "algorithm." ) - raise ValueError("Could not deserialize key data.") def elliptic_curve_supported(self, curve): try: diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index 2f56711d5dab..c21a4fa5ee35 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -13,7 +13,7 @@ import six -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.backends.interfaces import ( DERSerializationBackend, DSABackend, @@ -55,7 +55,6 @@ load_vectors_from_file, ) from ...doubles import DummyKeySerializationEncryption -from ...utils import raises_unsupported_algorithm def _skip_fips_format(key_path, password, backend): @@ -771,7 +770,7 @@ def test_unsupported_key_encryption(self, backend): password = b"password" - with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): + with pytest.raises(ValueError): load_pem_private_key(key_data, password, backend) def test_corrupt_pkcs8_format(self, backend): @@ -966,7 +965,7 @@ def test_load_bad_oid_key(self, key_file, password, backend): ("key_file", "password"), [("bad-encryption-oid.pem", b"password")] ) def test_load_bad_encryption_oid_key(self, key_file, password, backend): - with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): + with pytest.raises(ValueError): load_vectors_from_file( os.path.join("asymmetric", "PKCS8", key_file), lambda pemfile: load_pem_private_key( From fd7ed6704087f1d71781e48e6c268341429b3abc Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Thu, 26 Nov 2020 14:13:47 -0600 Subject: [PATCH 86/99] don't require errors to be on the stack when loading a key (#5590) In OpenSSL 3.0.0 no error is added in many cases for this path and since we don't do anything with the error anyway we should just consume and move on --- src/cryptography/hazmat/backends/openssl/backend.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index e3b5718f2efb..7f4ff2a3e2e2 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1459,8 +1459,7 @@ def _load_key(self, openssl_read_func, convert_func, data, password): if evp_pkey == self._ffi.NULL: if userdata.error != 0: - errors = self._consume_errors() - self.openssl_assert(errors) + self._consume_errors() if userdata.error == -1: raise TypeError( "Password was not given but private key is encrypted" From 4645f02c25d7d336a6d922e428c72beb55fb04cb Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 29 Nov 2020 10:01:16 -0600 Subject: [PATCH 87/99] disallow p less than 512-bit on DH (#5592) * disallow p less than 512-bit on DH OpenSSL 3.0.0 enforces this so we'll go ahead and enforce it everywhere that's practical for us. (Note that we do not enforce on deserializing PKCS1/PKCS8 keys in < 3.0.0, but this PR adds a test so that in the 3.0.0 support branch we can test an error path) * missing test * black * _MIN_MODULUS_SIZE is now a thing * skip on fips --- CHANGELOG.rst | 5 ++ docs/development/test-vectors.rst | 2 + .../hazmat/backends/openssl/backend.py | 9 +- .../hazmat/primitives/asymmetric/dh.py | 8 ++ tests/hazmat/primitives/test_dh.py | 84 +++++++++++++------ .../asymmetric/DH/dh_key_256.pem | 4 + 6 files changed, 84 insertions(+), 28 deletions(-) create mode 100644 vectors/cryptography_vectors/asymmetric/DH/dh_key_256.pem diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1f1ffd99589b..e9ea92a84756 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -20,6 +20,11 @@ Changelog raise ``ValueError`` rather than ``UnsupportedAlgorithm`` when an unsupported cipher is used. This change is to conform with an upcoming OpenSSL release that will no longer distinguish between error types. +* **BACKWARDS INCOMPATIBLE:** We no longer allow loading of finite field + Diffie-Hellman parameters of less than 512 bits in length. This change is to + conform with an upcoming OpenSSL release that no longer supports smaller + sizes. These keys were already wildly insecure and should not have been used + in any application outside of testing. * Python 2 support is deprecated in ``cryptography``. This is the last release that will support Python 2. diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index 6146c9c139b9..f952337e2347 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -184,6 +184,8 @@ Key exchange ``vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.der`` and ``vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.der`` contains are the above parameters and keys in DER format. +* ``vectors/cryptography_vectors/asymmetric/DH/dh_key_256.pem`` contains + a PEM PKCS8 encoded DH key with a 256-bit key size. * ``vectors/cryptoraphy_vectors/asymmetric/ECDH/brainpool.txt`` contains Brainpool vectors from :rfc:`7027`. diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 7f4ff2a3e2e2..45d4a1a1eec9 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -117,6 +117,7 @@ from cryptography.hazmat.bindings.openssl import binding from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ( + dh, dsa, ec, ed25519, @@ -2086,8 +2087,12 @@ def _parameter_bytes(self, encoding, format, cdata): return self._read_mem_bio(bio) def generate_dh_parameters(self, generator, key_size): - if key_size < 512: - raise ValueError("DH key_size must be at least 512 bits") + if key_size < dh._MIN_MODULUS_SIZE: + raise ValueError( + "DH key_size must be at least {} bits".format( + dh._MIN_MODULUS_SIZE + ) + ) if generator not in (2, 5): raise ValueError("DH generator must be 2 or 5") diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py index cd9fbfab4600..74a311d5015a 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/dh.py +++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py @@ -12,6 +12,9 @@ from cryptography.hazmat.backends import _get_backend +_MIN_MODULUS_SIZE = 512 + + def generate_parameters(generator, key_size, backend=None): backend = _get_backend(backend) return backend.generate_dh_parameters(generator, key_size) @@ -95,6 +98,11 @@ def __init__(self, p, g, q=None): if g < 2: raise ValueError("DH generator must be 2 or greater") + if p.bit_length() < _MIN_MODULUS_SIZE: + raise ValueError( + "p (modulus) must be at least {}-bit".format(_MIN_MODULUS_SIZE) + ) + self._p = p self._g = g self._q = q diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py index 63a7c642ef7a..4c2ee1a63f86 100644 --- a/tests/hazmat/primitives/test_dh.py +++ b/tests/hazmat/primitives/test_dh.py @@ -23,6 +23,19 @@ from ...doubles import DummyKeySerializationEncryption from ...utils import load_nist_vectors, load_vectors_from_file +# RFC 3526 +P_1536 = int( + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", + 16, +) + def _skip_dhx_unsupported(backend, is_dhx): if not is_dhx: @@ -32,35 +45,39 @@ def _skip_dhx_unsupported(backend, is_dhx): def test_dh_parameternumbers(): - params = dh.DHParameterNumbers(65537, 2) + params = dh.DHParameterNumbers(P_1536, 2) - assert params.p == 65537 + assert params.p == P_1536 assert params.g == 2 with pytest.raises(TypeError): dh.DHParameterNumbers(None, 2) with pytest.raises(TypeError): - dh.DHParameterNumbers(65537, None) + dh.DHParameterNumbers(P_1536, None) with pytest.raises(TypeError): dh.DHParameterNumbers(None, None) with pytest.raises(ValueError): - dh.DHParameterNumbers(65537, 1) + dh.DHParameterNumbers(P_1536, 1) - params = dh.DHParameterNumbers(65537, 7, 1245) + # p too small + with pytest.raises(ValueError): + dh.DHParameterNumbers(65537, 2) - assert params.p == 65537 + params = dh.DHParameterNumbers(P_1536, 7, 1245) + + assert params.p == P_1536 assert params.g == 7 assert params.q == 1245 with pytest.raises(TypeError): - dh.DHParameterNumbers(65537, 2, "hello") + dh.DHParameterNumbers(P_1536, 2, "hello") def test_dh_numbers(): - params = dh.DHParameterNumbers(65537, 2) + params = dh.DHParameterNumbers(P_1536, 2) public = dh.DHPublicNumbers(1, params) @@ -86,20 +103,22 @@ def test_dh_numbers(): def test_dh_parameter_numbers_equality(): - assert dh.DHParameterNumbers(65537, 2) == dh.DHParameterNumbers(65537, 2) - assert dh.DHParameterNumbers(65537, 7, 12345) == dh.DHParameterNumbers( - 65537, 7, 12345 + assert dh.DHParameterNumbers(P_1536, 2) == dh.DHParameterNumbers(P_1536, 2) + assert dh.DHParameterNumbers(P_1536, 7, 12345) == dh.DHParameterNumbers( + P_1536, 7, 12345 + ) + assert dh.DHParameterNumbers(P_1536 + 2, 2) != dh.DHParameterNumbers( + P_1536, 2 ) - assert dh.DHParameterNumbers(6, 2) != dh.DHParameterNumbers(65537, 2) - assert dh.DHParameterNumbers(65537, 2, 123) != dh.DHParameterNumbers( - 65537, 2, 456 + assert dh.DHParameterNumbers(P_1536, 2, 123) != dh.DHParameterNumbers( + P_1536, 2, 456 ) - assert dh.DHParameterNumbers(65537, 5) != dh.DHParameterNumbers(65537, 2) - assert dh.DHParameterNumbers(65537, 2) != object() + assert dh.DHParameterNumbers(P_1536, 5) != dh.DHParameterNumbers(P_1536, 2) + assert dh.DHParameterNumbers(P_1536, 2) != object() def test_dh_private_numbers_equality(): - params = dh.DHParameterNumbers(65537, 2) + params = dh.DHParameterNumbers(P_1536, 2) public = dh.DHPublicNumbers(1, params) private = dh.DHPrivateNumbers(2, public) @@ -107,18 +126,18 @@ def test_dh_private_numbers_equality(): assert private != dh.DHPrivateNumbers(0, public) assert private != dh.DHPrivateNumbers(2, dh.DHPublicNumbers(0, params)) assert private != dh.DHPrivateNumbers( - 2, dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 5)) + 2, dh.DHPublicNumbers(1, dh.DHParameterNumbers(P_1536, 5)) ) assert private != object() def test_dh_public_numbers_equality(): - params = dh.DHParameterNumbers(65537, 2) + params = dh.DHParameterNumbers(P_1536, 2) public = dh.DHPublicNumbers(1, params) assert public == dh.DHPublicNumbers(1, params) assert public != dh.DHPublicNumbers(0, params) - assert public != dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 5)) + assert public != dh.DHPublicNumbers(1, dh.DHParameterNumbers(P_1536, 5)) assert public != object() @@ -209,12 +228,11 @@ def test_convert_to_numbers(self, backend, with_q): ) def test_numbers_unsupported_parameters(self, backend): - # p is set to 21 because when calling private_key we want it to - # fail the DH_check call OpenSSL does. Originally this was 23, but - # we are allowing p % 24 to == 23 with this PR (see #3768 for more) - # By setting it to 21 it fails later in DH_check in a primality check - # which triggers the code path we want to test - params = dh.DHParameterNumbers(21, 2) + # p is set to P_1536 + 1 because when calling private_key we want it to + # fail the DH_check call OpenSSL does, but we specifically want it to + # fail such that we don't get a DH_NOT_SUITABLE_GENERATOR. We can cause + # this by making sure p is not prime. + params = dh.DHParameterNumbers(P_1536 + 1, 2) public = dh.DHPublicNumbers(1, params) private = dh.DHPrivateNumbers(2, public) @@ -363,6 +381,16 @@ def test_bad_exchange(self, backend, vector): assert symkey1 != symkey2 + @pytest.mark.skip_fips(reason="key_size too small for FIPS") + def test_load_256bit_key_from_pkcs8(self, backend): + data = load_vectors_from_file( + os.path.join("asymmetric", "DH", "dh_key_256.pem"), + lambda pemfile: pemfile.read(), + mode="rb", + ) + key = serialization.load_pem_private_key(data, None, backend) + assert key.key_size == 256 + @pytest.mark.parametrize( "vector", load_vectors_from_file( @@ -375,6 +403,10 @@ def test_dh_vectors(self, backend, vector): and int(vector["p"]) < backend._fips_dh_min_modulus ): pytest.skip("modulus too small for FIPS mode") + + if int(vector["p"]).bit_length() < 512: + pytest.skip("DH keys less than 512 bits are unsupported") + parameters = dh.DHParameterNumbers(int(vector["p"]), int(vector["g"])) public = dh.DHPublicNumbers(int(vector["y"]), parameters) private = dh.DHPrivateNumbers(int(vector["x"]), public) diff --git a/vectors/cryptography_vectors/asymmetric/DH/dh_key_256.pem b/vectors/cryptography_vectors/asymmetric/DH/dh_key_256.pem new file mode 100644 index 000000000000..1c01dd3eaf7e --- /dev/null +++ b/vectors/cryptography_vectors/asymmetric/DH/dh_key_256.pem @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MFwCAQAwMwYJKoZIhvcNAQMBMCYCIQCBPg6BS+5nbb09nSjtc9NnNdIf9kVyNvaN +PWFFVgwPqwIBAgQiAiBmJ3qBbu72ZnUxnCrr8ujWFU7jWTcOjhsZSqobmiD6vA== +-----END PRIVATE KEY----- From f133a3029a56d869084bc9839131bd57283027e0 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 29 Nov 2020 13:12:18 -0500 Subject: [PATCH 88/99] Don't build our custom osrandom engine on libressl (#5593) * Don't build our custom osrandom engine on libressl As far as I can tell it's never used on LibreSSL -- they're `RAND_bytes` function unconditionally calls `arc4random_buf` * Update cryptography.py --- src/_cffi_src/openssl/cryptography.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_cffi_src/openssl/cryptography.py b/src/_cffi_src/openssl/cryptography.py index 28d659b1194a..f24bee5a4f82 100644 --- a/src/_cffi_src/openssl/cryptography.py +++ b/src/_cffi_src/openssl/cryptography.py @@ -44,8 +44,8 @@ (OPENSSL_VERSION_NUMBER < 0x10101020 || CRYPTOGRAPHY_IS_LIBRESSL) #define CRYPTOGRAPHY_OPENSSL_LESS_THAN_111D \ (OPENSSL_VERSION_NUMBER < 0x10101040 || CRYPTOGRAPHY_IS_LIBRESSL) -#if (CRYPTOGRAPHY_OPENSSL_LESS_THAN_111D && !defined(OPENSSL_NO_ENGINE)) || \ - defined(USE_OSRANDOM_RNG_FOR_TESTING) +#if (CRYPTOGRAPHY_OPENSSL_LESS_THAN_111D && !CRYPTOGRAPHY_IS_LIBRESSL && \ + !defined(OPENSSL_NO_ENGINE)) || defined(USE_OSRANDOM_RNG_FOR_TESTING) #define CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE 1 #else #define CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE 0 From 6d858c8bacc86da7dd1f9907f134767a0c8f91de Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 30 Nov 2020 22:56:52 -0500 Subject: [PATCH 89/99] fixes #4531 -- support encoding SCTs in certificates (#5594) --- src/_cffi_src/openssl/ct.py | 10 ++++--- .../hazmat/backends/openssl/encode_asn1.py | 15 ++++++++++- .../hazmat/bindings/openssl/_conditional.py | 5 ++-- tests/x509/test_x509_ext.py | 27 +++++++++++++++++++ 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/_cffi_src/openssl/ct.py b/src/_cffi_src/openssl/ct.py index 162004a8da73..5f0670635fa2 100644 --- a/src/_cffi_src/openssl/ct.py +++ b/src/_cffi_src/openssl/ct.py @@ -50,13 +50,14 @@ int SCT_set_source(SCT *, sct_source_t); +Cryptography_STACK_OF_SCT *sk_SCT_new_null(void); +void sk_SCT_free(Cryptography_STACK_OF_SCT *); int sk_SCT_num(const Cryptography_STACK_OF_SCT *); SCT *sk_SCT_value(const Cryptography_STACK_OF_SCT *, int); +int sk_SCT_push(Cryptography_STACK_OF_SCT *, SCT *); void SCT_LIST_free(Cryptography_STACK_OF_SCT *); -int sk_SCT_push(Cryptography_STACK_OF_SCT *, SCT *); -Cryptography_STACK_OF_SCT *sk_SCT_new_null(void); SCT *SCT_new(void); int SCT_set1_log_id(SCT *, unsigned char *, size_t); void SCT_set_timestamp(SCT *, uint64_t); @@ -101,12 +102,13 @@ int (*SCT_set_source)(SCT *, sct_source_t) = NULL; +Cryptography_STACK_OF_SCT *(*sk_SCT_new_null)(void) = NULL; +void (*sk_SCT_free)(Cryptography_STACK_OF_SCT *) = NULL; int (*sk_SCT_num)(const Cryptography_STACK_OF_SCT *) = NULL; SCT *(*sk_SCT_value)(const Cryptography_STACK_OF_SCT *, int) = NULL; +int (*sk_SCT_push)(Cryptography_STACK_OF_SCT *, SCT *) = NULL; void (*SCT_LIST_free)(Cryptography_STACK_OF_SCT *) = NULL; -int (*sk_SCT_push)(Cryptography_STACK_OF_SCT *, SCT *) = NULL; -Cryptography_STACK_OF_SCT *(*sk_SCT_new_null)(void) = NULL; SCT *(*SCT_new)(void) = NULL; int (*SCT_set1_log_id)(SCT *, unsigned char *, size_t) = NULL; void (*SCT_set_timestamp)(SCT *, uint64_t) = NULL; diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py index 88d709d21457..0a33200bbcc2 100644 --- a/src/cryptography/hazmat/backends/openssl/encode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py @@ -604,11 +604,21 @@ def _encode_general_subtree(backend, subtrees): gs = backend._lib.GENERAL_SUBTREE_new() gs.base = _encode_general_name(backend, name) res = backend._lib.sk_GENERAL_SUBTREE_push(general_subtrees, gs) - assert res >= 1 + backend.openssl_assert(res >= 1) return general_subtrees +def _encode_precert_signed_certificate_timestamps(backend, scts): + sct_stack = backend._lib.sk_SCT_new_null() + backend.openssl_assert(sct_stack != backend._ffi.NULL) + sct_stack = backend._ffi.gc(sct_stack, backend._lib.sk_SCT_free) + for sct in scts: + res = backend._lib.sk_SCT_push(sct_stack, sct._sct) + backend.openssl_assert(res >= 1) + return sct_stack + + def _encode_nonce(backend, nonce): return _encode_asn1_str_gc(backend, nonce.nonce) @@ -630,6 +640,9 @@ def _encode_nonce(backend, nonce): ExtensionOID.OCSP_NO_CHECK: _encode_ocsp_nocheck, ExtensionOID.NAME_CONSTRAINTS: _encode_name_constraints, ExtensionOID.POLICY_CONSTRAINTS: _encode_policy_constraints, + ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: ( + _encode_precert_signed_certificate_timestamps + ), } _CRL_EXTENSION_ENCODE_HANDLERS = { diff --git a/src/cryptography/hazmat/bindings/openssl/_conditional.py b/src/cryptography/hazmat/bindings/openssl/_conditional.py index 585c7366454e..d990999cd1b9 100644 --- a/src/cryptography/hazmat/bindings/openssl/_conditional.py +++ b/src/cryptography/hazmat/bindings/openssl/_conditional.py @@ -100,11 +100,12 @@ def cryptography_has_sct(): "SCT_get0_signature", "SCT_get_timestamp", "SCT_set_source", + "sk_SCT_new_null", + "sk_SCT_free", "sk_SCT_num", "sk_SCT_value", - "SCT_LIST_free", "sk_SCT_push", - "sk_SCT_new_null", + "SCT_LIST_free", "SCT_new", "SCT_set1_log_id", "SCT_set_timestamp", diff --git a/tests/x509/test_x509_ext.py b/tests/x509/test_x509_ext.py index 429169a45ebc..8e2b402712ff 100644 --- a/tests/x509/test_x509_ext.py +++ b/tests/x509/test_x509_ext.py @@ -5678,6 +5678,33 @@ def test_simple(self, backend): == x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE ) + @pytest.mark.supported( + only_if=lambda backend: (backend._lib.Cryptography_HAS_SCT), + skip_message="Requires CT support", + ) + def test_generate(self, backend): + cert = _load_cert( + os.path.join("x509", "badssl-sct.pem"), + x509.load_pem_x509_certificate, + backend, + ) + scts = cert.extensions.get_extension_for_class( + x509.PrecertificateSignedCertificateTimestamps + ).value + assert len(scts) == 1 + [sct] = scts + + private_key = RSA_KEY_2048.private_key(backend) + builder = _make_certbuilder(private_key).add_extension( + x509.PrecertificateSignedCertificateTimestamps([sct]), + critical=False, + ) + cert = builder.sign(private_key, hashes.SHA256(), backend) + ext = cert.extensions.get_extension_for_class( + x509.PrecertificateSignedCertificateTimestamps + ).value + assert list(ext) == [sct] + @pytest.mark.supported( only_if=lambda backend: backend._lib.CRYPTOGRAPHY_IS_LIBRESSL, skip_message="Requires LibreSSL", From a2096694853aed0828d0aaf38e364577a52b3780 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 1 Dec 2020 10:10:56 -0500 Subject: [PATCH 90/99] Added tls bindings for new OpenSSL APIs (#5595) fixes #5379 closes #5483 --- src/_cffi_src/openssl/ssl.py | 30 ++++++++++++++++--- .../hazmat/bindings/openssl/_conditional.py | 10 +++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/_cffi_src/openssl/ssl.py b/src/_cffi_src/openssl/ssl.py index ff960f5775ad..f3511e454c98 100644 --- a/src/_cffi_src/openssl/ssl.py +++ b/src/_cffi_src/openssl/ssl.py @@ -24,6 +24,7 @@ static const long Cryptography_HAS_PSK; static const long Cryptography_HAS_VERIFIED_CHAIN; static const long Cryptography_HAS_KEYLOG; +static const long Cryptography_HAS_GET_PROTO_VERSION; /* Internally invented symbol to tell us if SSL_MODE_RELEASE_BUFFERS is * supported @@ -312,6 +313,16 @@ long SSL_total_renegotiations(SSL *); long SSL_get_secure_renegotiation_support(SSL *); +long SSL_CTX_set_min_proto_version(SSL_CTX *, int); +long SSL_CTX_set_max_proto_version(SSL_CTX *, int); +long SSL_set_min_proto_version(SSL *, int); +long SSL_set_max_proto_version(SSL *, int); + +long SSL_CTX_get_min_proto_version(SSL_CTX *); +long SSL_CTX_get_max_proto_version(SSL_CTX *); +long SSL_get_min_proto_version(SSL *); +long SSL_get_max_proto_version(SSL *); + /* Defined as unsigned long because SSL_OP_ALL is greater than signed 32-bit and Windows defines long as 32-bit. */ unsigned long SSL_CTX_set_options(SSL_CTX *, unsigned long); @@ -330,10 +341,6 @@ /* methods */ -/* - * TLSv1_1 and TLSv1_2 are recent additions. Only sufficiently new versions of - * OpenSSL support them. - */ const SSL_METHOD *TLSv1_1_method(void); const SSL_METHOD *TLSv1_1_server_method(void); const SSL_METHOD *TLSv1_1_client_method(void); @@ -363,6 +370,10 @@ const SSL_METHOD *SSLv23_server_method(void); const SSL_METHOD *SSLv23_client_method(void); +const SSL_METHOD *TLS_method(void); +const SSL_METHOD *TLS_server_method(void); +const SSL_METHOD *TLS_client_method(void); + /*- These aren't macros these arguments are all const X on openssl > 1.0.x -*/ SSL_CTX *SSL_CTX_new(SSL_METHOD *); long SSL_CTX_get_timeout(const SSL_CTX *); @@ -674,4 +685,15 @@ #else static const long Cryptography_HAS_TLSv1_3 = 1; #endif + +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 && !CRYPTOGRAPHY_IS_LIBRESSL +static const long Cryptography_HAS_GET_PROTO_VERSION = 0; + +long (*SSL_CTX_get_min_proto_version)(SSL_CTX *) = NULL; +long (*SSL_CTX_get_max_proto_version)(SSL_CTX *) = NULL; +long (*SSL_get_min_proto_version)(SSL *) = NULL; +long (*SSL_get_max_proto_version)(SSL *) = NULL; +#else +static const long Cryptography_HAS_GET_PROTO_VERSION = 1; +#endif """ diff --git a/src/cryptography/hazmat/bindings/openssl/_conditional.py b/src/cryptography/hazmat/bindings/openssl/_conditional.py index d990999cd1b9..ca50fed13414 100644 --- a/src/cryptography/hazmat/bindings/openssl/_conditional.py +++ b/src/cryptography/hazmat/bindings/openssl/_conditional.py @@ -262,6 +262,15 @@ def cryptography_has_srtp(): ] +def cryptography_has_get_proto_version(): + return [ + "SSL_CTX_get_min_proto_version", + "SSL_CTX_get_max_proto_version", + "SSL_get_min_proto_version", + "SSL_get_max_proto_version", + ] + + # This is a mapping of # {condition: function-returning-names-dependent-on-that-condition} so we can # loop over them and delete unsupported names at runtime. It will be removed @@ -309,4 +318,5 @@ def cryptography_has_srtp(): "Cryptography_HAS_ENGINE": cryptography_has_engine, "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain, "Cryptography_HAS_SRTP": cryptography_has_srtp, + "Cryptography_HAS_GET_PROTO_VERSION": cryptography_has_get_proto_version, } From 2660f93eca71be5558cfcb9a120310636791e6ec Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 1 Dec 2020 11:54:29 -0500 Subject: [PATCH 91/99] Document that Firefox doesn't support unencrypted pkcs12 (#5596) --- docs/hazmat/primitives/asymmetric/serialization.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index 8811646045f6..2324340e119c 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -496,6 +496,11 @@ file suffix. Serialize a PKCS12 blob. + .. note:: + + Due to `a bug in Firefox`_ it's not possible to load unencrypted PKCS12 + blobs in Firefox. + :param name: The friendly name to use for the supplied certificate and key. :type name: bytes @@ -942,6 +947,7 @@ Serialization Encryption Types Do not encrypt. +.. _`a bug in Firefox`: https://bugzilla.mozilla.org/show_bug.cgi?id=773111 .. _`PKCS3`: https://www.teletrust.de/fileadmin/files/oid/oid_pkcs-3v1-4.pdf .. _`SEC 1 v2.0`: https://www.secg.org/sec1-v2.pdf .. _`PROTOCOL.key`: https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key From 96f2d96d1c4884a7d314372d4876e0d45e24e342 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 1 Dec 2020 13:23:39 -0500 Subject: [PATCH 92/99] remove legacy debugging code from setup.py (#5597) --- setup.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/setup.py b/setup.py index c6c3e994625e..8477c826b28f 100644 --- a/setup.py +++ b/setup.py @@ -10,20 +10,9 @@ import platform import sys -import pkg_resources - -import setuptools from setuptools import find_packages, setup -if pkg_resources.parse_version( - setuptools.__version__ -) < pkg_resources.parse_version("18.5"): - raise RuntimeError( - "cryptography requires setuptools 18.5 or newer, please upgrade to a " - "newer version of setuptools" - ) - base_dir = os.path.dirname(__file__) src_dir = os.path.join(base_dir, "src") From 1be144acc6b46ae2ece459d80a20831ac2ac1c74 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 1 Dec 2020 14:01:43 -0500 Subject: [PATCH 93/99] bump cffi minimum version to help out pyopenssl (#5598) fixes https://github.com/pyca/pyopenssl/issues/971 --- pyproject.toml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 957b1fd04bb0..667f29e23ff2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ requires = [ "setuptools>=40.6.0", "wheel", # Must be kept in sync with the `setup_requirements` in `setup.py` - "cffi>=1.8,!=1.11.3; platform_python_implementation != 'PyPy'", + "cffi>=1.12; platform_python_implementation != 'PyPy'", ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 8477c826b28f..4ebbc1b50203 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ # `setup_requirements` must be kept in sync with `pyproject.toml` -setup_requirements = ["cffi>=1.8,!=1.11.3"] +setup_requirements = ["cffi>=1.12"] if platform.python_implementation() == "PyPy": if sys.pypy_version_info < (5, 4): From 8686d524b7b890bcbe6132b774bd72a3ae37cf0d Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 6 Dec 2020 23:12:44 -0500 Subject: [PATCH 94/99] Document that PKCS1v1.5 is not constant time (#5600) closes #5510 --- docs/hazmat/primitives/asymmetric/rsa.rst | 5 +++++ docs/limitations.rst | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index b8060e4740fd..fe3930fec29b 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -342,6 +342,11 @@ Padding :class:`OAEP` should be preferred for encryption and :class:`PSS` should be preferred for signatures. + .. warning:: + + Our implementation of PKCS1 v1.5 decryption is not constant time. See + :doc:`/limitations` for details. + .. function:: calculate_max_pss_salt_length(key, hash_algorithm) diff --git a/docs/limitations.rst b/docs/limitations.rst index 092d8a7cff91..5763ecd40299 100644 --- a/docs/limitations.rst +++ b/docs/limitations.rst @@ -20,5 +20,25 @@ like almost all software in Python is potentially vulnerable to this attack. The Likelihood: unlikely, Remediation Cost: expensive to repair" and we do not consider this a high risk for most users. +RSA PKCS1 v1.5 constant time decryption +--------------------------------------- + +RSA decryption has several different modes, one of which is PKCS1 v1.5. When +used in online contexts, a secure protocol implementation requires that peers +not be able to tell whether RSA PKCS1 v1.5 decryption failed or succeeded, +even by timing variability. + +``cryptography`` does not provide an API that makes this possible, due to the +fact that RSA decryption raises an exception on failure, which takes a +different amount of time than returning a value in the success case. + +For this reason, at present, we recommend not implementing online protocols +that use RSA PKCS1 v1.5 decryption with ``cryptography`` -- independent of this +limitation, such protocols generally have poor security properties due to their +lack of forward security. + +If a constant time RSA PKCS1 v1.5 decryption API is truly required, you should +contribute one to ``cryptography``. + .. _`Memory wiping`: https://devblogs.microsoft.com/oldnewthing/?p=4223 .. _`CERT secure coding guidelines`: https://wiki.sei.cmu.edu/confluence/display/c/MEM03-C.+Clear+sensitive+information+stored+in+reusable+resources From 6693d55cbe05c98c9e1fe3a8b08639f5491a572a Mon Sep 17 00:00:00 2001 From: Zoltan Kelemen <39551158+misterzed88@users.noreply.github.com> Date: Tue, 8 Dec 2020 05:58:04 +0100 Subject: [PATCH 95/99] Add support for RSA signature recovery (#5573) * Removed unused argument. * Added support for RSA signature recovery. * Syntatic corrections for passing pep8 tests. * Corrected typo. * Added test of invalid Prehashed parameter to RSA signature recover. * Renamed recover to a more descriptive name. * Extended RSA signature recovery with option to return full data (not only the digest part). * Added missing words to pass spell check. --- CHANGELOG.rst | 5 ++ docs/hazmat/primitives/asymmetric/rsa.rst | 49 +++++++++++++ docs/spelling_wordlist.txt | 1 + src/_cffi_src/openssl/evp.py | 3 + .../hazmat/backends/openssl/rsa.py | 73 ++++++++++++++++--- .../hazmat/backends/openssl/utils.py | 3 +- .../hazmat/primitives/asymmetric/rsa.py | 6 ++ tests/hazmat/primitives/test_rsa.py | 67 ++++++++++++++++- 8 files changed, 191 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e9ea92a84756..83d3b703504b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -27,6 +27,11 @@ Changelog in any application outside of testing. * Python 2 support is deprecated in ``cryptography``. This is the last release that will support Python 2. +* Added the + :meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey.recover_data_from_signature` + function to + :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` + for recovering the signed data from an RSA signature. .. _v3-2-1: diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index fe3930fec29b..33f402b65a43 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -714,6 +714,55 @@ Key interfaces :raises cryptography.exceptions.InvalidSignature: If the signature does not validate. + .. method:: recover_data_from_signature(signature, padding, algorithm) + + .. versionadded:: 3.3 + + Recovers the signed data from the signature. The data contains the + digest of the original message string. The ``padding`` and + ``algorithm`` parameters must match the ones used when the signature + was created for the recovery to succeed. + + The ``algorithm`` parameter can also be set to ``None`` to recover all + the data present in the signature, without regard to its format or the + hash algorithm used for its creation. + + For + :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15` + padding, this returns the data after removing the padding layer. For + standard signatures the data contains the full ``DigestInfo`` structure. + For non-standard signatures, any data can be returned, including zero- + length data. + + Normally you should use the + :meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey.verify` + function to validate the signature. But for some non-standard signature + formats you may need to explicitly recover and validate the signed + data. Following are some examples: + + - Some old Thawte and Verisign timestamp certificates without ``DigestInfo``. + - Signed MD5/SHA1 hashes in TLS 1.1 or earlier (RFC 4346, section 4.7). + - IKE version 1 signatures without ``DigestInfo`` (RFC 2409, section 5.1). + + :param bytes signature: The signature. + + :param padding: An instance of + :class:`~cryptography.hazmat.primitives.asymmetric.padding.AsymmetricPadding`. + Recovery is only supported with some of the padding types. (Currently + only with + :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`). + + :param algorithm: An instance of + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`. + Can be ``None`` to return the all the data present in the signature. + + :return bytes: The signed data. + + :raises cryptography.exceptions.InvalidSignature: If the signature is + invalid. + + :raises cryptography.exceptions.UnsupportedAlgorithm: If signature + data recovery is not supported with the provided ``padding`` type. .. class:: RSAPublicKeyWithSerialization diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index c8c275142ff7..f0486e05f704 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -103,6 +103,7 @@ Solaris syscall Tanja testability +Thawte timestamp timestamps tunable diff --git a/src/_cffi_src/openssl/evp.py b/src/_cffi_src/openssl/evp.py index 5b6e35cf9dd6..ab7cfeb39511 100644 --- a/src/_cffi_src/openssl/evp.py +++ b/src/_cffi_src/openssl/evp.py @@ -101,6 +101,9 @@ int EVP_PKEY_verify_init(EVP_PKEY_CTX *); int EVP_PKEY_verify(EVP_PKEY_CTX *, const unsigned char *, size_t, const unsigned char *, size_t); +int EVP_PKEY_verify_recover_init(EVP_PKEY_CTX *); +int EVP_PKEY_verify_recover(EVP_PKEY_CTX *, unsigned char *, + size_t *, const unsigned char *, size_t); int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *); int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *); diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index de299779d942..82cd49c960ab 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -142,6 +142,7 @@ def _rsa_sig_determine_padding(backend, key, padding, algorithm): backend.openssl_assert(pkey_size > 0) if isinstance(padding, PKCS1v15): + # Hash algorithm is ignored for PKCS1v15-padding, may be None. padding_enum = backend._lib.RSA_PKCS1_PADDING elif isinstance(padding, PSS): if not isinstance(padding._mgf, MGF1): @@ -150,6 +151,10 @@ def _rsa_sig_determine_padding(backend, key, padding, algorithm): _Reasons.UNSUPPORTED_MGF, ) + # PSS padding requires a hash algorithm + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + # Size of key in bytes - 2 is the maximum # PSS signature length (salt length is checked later) if pkey_size - algorithm.digest_size - 2 < 0: @@ -168,25 +173,37 @@ def _rsa_sig_determine_padding(backend, key, padding, algorithm): return padding_enum -def _rsa_sig_setup(backend, padding, algorithm, key, data, init_func): +# Hash algorithm can be absent (None) to initialize the context without setting +# any message digest algorithm. This is currently only valid for the PKCS1v15 +# padding type, where it means that the signature data is encoded/decoded +# as provided, without being wrapped in a DigestInfo structure. +def _rsa_sig_setup(backend, padding, algorithm, key, init_func): padding_enum = _rsa_sig_determine_padding(backend, key, padding, algorithm) - evp_md = backend._evp_md_non_null_from_algorithm(algorithm) pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL) backend.openssl_assert(pkey_ctx != backend._ffi.NULL) pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free) res = init_func(pkey_ctx) backend.openssl_assert(res == 1) - res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md) - if res == 0: + if algorithm is not None: + evp_md = backend._evp_md_non_null_from_algorithm(algorithm) + res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md) + if res == 0: + backend._consume_errors() + raise UnsupportedAlgorithm( + "{} is not supported by this backend for RSA signing.".format( + algorithm.name + ), + _Reasons.UNSUPPORTED_HASH, + ) + res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum) + if res <= 0: backend._consume_errors() raise UnsupportedAlgorithm( - "{} is not supported by this backend for RSA signing.".format( - algorithm.name + "{} is not supported for the RSA signature operation.".format( + padding.name ), - _Reasons.UNSUPPORTED_HASH, + _Reasons.UNSUPPORTED_PADDING, ) - res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum) - backend.openssl_assert(res > 0) if isinstance(padding, PSS): res = backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( pkey_ctx, _get_rsa_pss_salt_length(padding, key, algorithm) @@ -208,7 +225,6 @@ def _rsa_sig_sign(backend, padding, algorithm, private_key, data): padding, algorithm, private_key, - data, backend._lib.EVP_PKEY_sign_init, ) buflen = backend._ffi.new("size_t *") @@ -235,7 +251,6 @@ def _rsa_sig_verify(backend, padding, algorithm, public_key, signature, data): padding, algorithm, public_key, - data, backend._lib.EVP_PKEY_verify_init, ) res = backend._lib.EVP_PKEY_verify( @@ -250,6 +265,36 @@ def _rsa_sig_verify(backend, padding, algorithm, public_key, signature, data): raise InvalidSignature +def _rsa_sig_recover(backend, padding, algorithm, public_key, signature): + pkey_ctx = _rsa_sig_setup( + backend, + padding, + algorithm, + public_key, + backend._lib.EVP_PKEY_verify_recover_init, + ) + + # Attempt to keep the rest of the code in this function as constant/time + # as possible. See the comment in _enc_dec_rsa_pkey_ctx. Note that the + # outlen parameter is used even though its value may be undefined in the + # error case. Due to the tolerant nature of Python slicing this does not + # trigger any exceptions. + maxlen = backend._lib.EVP_PKEY_size(public_key._evp_pkey) + backend.openssl_assert(maxlen > 0) + buf = backend._ffi.new("unsigned char[]", maxlen) + buflen = backend._ffi.new("size_t *", maxlen) + res = backend._lib.EVP_PKEY_verify_recover( + pkey_ctx, buf, buflen, signature, len(signature) + ) + resbuf = backend._ffi.buffer(buf)[: buflen[0]] + backend._lib.ERR_clear_error() + # Assume that all parameter errors are handled during the setup phase and + # any error here is due to invalid signature. + if res != 1: + raise InvalidSignature + return resbuf + + @utils.register_interface(AsymmetricSignatureContext) class _RSASignatureContext(object): def __init__(self, backend, private_key, padding, algorithm): @@ -463,3 +508,9 @@ def verify(self, signature, data, padding, algorithm): return _rsa_sig_verify( self._backend, padding, algorithm, self, signature, data ) + + def recover_data_from_signature(self, signature, padding, algorithm): + _check_not_prehashed(algorithm) + return _rsa_sig_recover( + self._backend, padding, algorithm, self, signature + ) diff --git a/src/cryptography/hazmat/backends/openssl/utils.py b/src/cryptography/hazmat/backends/openssl/utils.py index ec0b947a44c6..3d697d1fb56f 100644 --- a/src/cryptography/hazmat/backends/openssl/utils.py +++ b/src/cryptography/hazmat/backends/openssl/utils.py @@ -52,7 +52,8 @@ def _check_not_prehashed(signature_algorithm): if isinstance(signature_algorithm, Prehashed): raise TypeError( "Prehashed is only supported in the sign and verify methods. " - "It cannot be used with signer or verifier." + "It cannot be used with signer, verifier or " + "recover_data_from_signature." ) diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py index d8b8ddd914cc..ea16bbf66e66 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -106,6 +106,12 @@ def verify(self, signature, data, padding, algorithm): Verifies the signature of the data. """ + @abc.abstractmethod + def recover_data_from_signature(self, signature, padding, algorithm): + """ + Recovers the original data from the signature. + """ + RSAPublicKeyWithSerialization = RSAPublicKey diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index 1a770d3efe50..61c481504ecc 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -730,6 +730,18 @@ def test_prehashed_unsupported_in_verifier_ctx(self, backend): asym_utils.Prehashed(hashes.SHA1()), ) + def test_prehashed_unsupported_in_signature_recover(self, backend): + private_key = RSA_KEY_512.private_key(backend) + public_key = private_key.public_key() + signature = private_key.sign( + b"sign me", padding.PKCS1v15(), hashes.SHA1() + ) + prehashed_alg = asym_utils.Prehashed(hashes.SHA1()) + with pytest.raises(TypeError): + public_key.recover_data_from_signature( + signature, padding.PKCS1v15(), prehashed_alg + ) + def test_corrupted_private_key(self, backend): with pytest.raises(ValueError): serialization.load_pem_private_key( @@ -759,13 +771,28 @@ def test_pkcs1v15_verification(self, pkcs1_example, backend): public_key = rsa.RSAPublicNumbers( e=public["public_exponent"], n=public["modulus"] ).public_key(backend) + signature = binascii.unhexlify(example["signature"]) + message = binascii.unhexlify(example["message"]) public_key.verify( - binascii.unhexlify(example["signature"]), - binascii.unhexlify(example["message"]), - padding.PKCS1v15(), - hashes.SHA1(), + signature, message, padding.PKCS1v15(), hashes.SHA1() ) + # Test digest recovery by providing hash + digest = hashes.Hash(hashes.SHA1()) + digest.update(message) + msg_digest = digest.finalize() + rec_msg_digest = public_key.recover_data_from_signature( + signature, padding.PKCS1v15(), hashes.SHA1() + ) + assert msg_digest == rec_msg_digest + + # Test recovery of all data (full DigestInfo) with hash alg. as None + rec_sig_data = public_key.recover_data_from_signature( + signature, padding.PKCS1v15(), None + ) + assert len(rec_sig_data) > len(msg_digest) + assert msg_digest == rec_sig_data[-len(msg_digest) :] + @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( padding.PKCS1v15() @@ -783,6 +810,17 @@ def test_invalid_pkcs1v15_signature_wrong_data(self, backend): signature, b"incorrect data", padding.PKCS1v15(), hashes.SHA1() ) + def test_invalid_pkcs1v15_signature_recover_wrong_hash_alg(self, backend): + private_key = RSA_KEY_512.private_key(backend) + public_key = private_key.public_key() + signature = private_key.sign( + b"sign me", padding.PKCS1v15(), hashes.SHA1() + ) + with pytest.raises(InvalidSignature): + public_key.recover_data_from_signature( + signature, padding.PKCS1v15(), hashes.SHA256() + ) + def test_invalid_signature_sequence_removed(self, backend): """ This test comes from wycheproof @@ -970,6 +1008,27 @@ def test_invalid_pss_signature_data_too_large_for_modulus(self, backend): hashes.SHA1(), ) + def test_invalid_pss_signature_recover(self, backend): + private_key = RSA_KEY_1024.private_key(backend) + public_key = private_key.public_key() + pss_padding = padding.PSS( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH, + ) + signature = private_key.sign(b"sign me", pss_padding, hashes.SHA1()) + + # Hash algorithm can not be absent for PSS padding + with pytest.raises(TypeError): + public_key.recover_data_from_signature( + signature, pss_padding, None + ) + + # Signature data recovery not supported with PSS + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): + public_key.recover_data_from_signature( + signature, pss_padding, hashes.SHA1() + ) + @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( padding.PKCS1v15() From b5278c908574b2d965755d063a95812c6b520a8e Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 8 Dec 2020 16:45:30 -0500 Subject: [PATCH 96/99] Fixed DH tests for latest CentOS FIPS OpenSSL (#5604) * Fixed DH tests for latest CentOS FIPS OpenSSL (1.1.1g) --- .github/workflows/ci.yml | 14 +++++++------- tests/hazmat/primitives/test_dh.py | 12 ++++++++++++ tests/hazmat/primitives/test_serialization.py | 2 ++ tests/x509/test_x509.py | 18 +++++++++++------- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10f9eb47d326..1e03eaede894 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,7 +88,7 @@ jobs: IMAGE: - {IMAGE: "pyca/cryptography-runner-centos8", TOXENV: "py27"} - {IMAGE: "pyca/cryptography-runner-centos8", TOXENV: "py36"} - - {IMAGE: "pyca/cryptography-runner-centos8-fips", TOXENV: "py36", ENV: "OPENSSL_FORCE_FIPS_MODE=1"} + - {IMAGE: "pyca/cryptography-runner-centos8-fips", TOXENV: "py36", FIPS: true} - {IMAGE: "pyca/cryptography-runner-stretch", TOXENV: "py27"} - {IMAGE: "pyca/cryptography-runner-buster", TOXENV: "py37"} - {IMAGE: "pyca/cryptography-runner-bullseye", TOXENV: "py38"} @@ -100,20 +100,20 @@ jobs: - {IMAGE: "pyca/cryptography-runner-ubuntu-rolling", TOXENV: "py38-randomorder"} - {IMAGE: "pyca/cryptography-runner-fedora", TOXENV: "py39"} - {IMAGE: "pyca/cryptography-runner-alpine", TOXENV: "py38"} - name: "tox -e ${{ matrix.IMAGE.TOXENV }} on ${{ matrix.IMAGE.IMAGE }} ${{ matrix.IMAGE.ENV }}" + name: "tox -e ${{ matrix.IMAGE.TOXENV }} on ${{ matrix.IMAGE.IMAGE }}" steps: - uses: actions/checkout@v2 - run: 'git clone --depth=1 https://github.com/google/wycheproof "$HOME/wycheproof"' - - run: 'echo "$ENV_VAR" >> $GITHUB_ENV' - if: matrix.IMAGE.ENV - env: - ENV_VAR: ${{ matrix.IMAGE.ENV }} + - run: | + echo "OPENSSL_FORCE_FIPS_MODE=1" >> $GITHUB_ENV + echo "CFLAGS=-DUSE_OSRANDOM_RNG_FOR_TESTING" >> $GITHUB_ENV + if: matrix.IMAGE.FIPS - run: 'tox -- --wycheproof-root="$HOME/wycheproof"' env: TOXENV: ${{ matrix.IMAGE.TOXENV }} - uses: ./.github/actions/upload-coverage with: - name: "tox -e ${{ matrix.IMAGE.TOXENV }} on ${{ matrix.IMAGE.IMAGE }} ${{ matrix.IMAGE.ENV }}" + name: "tox -e ${{ matrix.IMAGE.TOXENV }} on ${{ matrix.IMAGE.IMAGE }}" macos: runs-on: macos-latest diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py index 4c2ee1a63f86..bc5ed8fb0035 100644 --- a/tests/hazmat/primitives/test_dh.py +++ b/tests/hazmat/primitives/test_dh.py @@ -151,6 +151,7 @@ def test_unsupported_generator_generate_dh(self, backend): with pytest.raises(ValueError): dh.generate_parameters(7, 512, backend) + @pytest.mark.skip_fips(reason="non-FIPS parameters") def test_dh_parameters_supported(self, backend): valid_p = int( b"907c7211ae61aaaba1825ff53b6cb71ac6df9f1a424c033f4a0a41ac42fad3a9" @@ -171,6 +172,12 @@ def test_dh_parameters_supported(self, backend): ) def test_dh_parameters_allows_rfc3526_groups(self, backend, vector): p = int_from_bytes(binascii.unhexlify(vector["p"]), "big") + if ( + backend._fips_enabled + and p.bit_length() < backend._fips_dh_min_modulus + ): + pytest.skip("modulus too small for FIPS mode") + params = dh.DHParameterNumbers(p, int(vector["g"])) param = params.parameters(backend) key = param.generate_private_key() @@ -180,6 +187,7 @@ def test_dh_parameters_allows_rfc3526_groups(self, backend, vector): roundtripped_key = key.private_numbers().private_key(backend) assert key.private_numbers() == roundtripped_key.private_numbers() + @pytest.mark.skip_fips(reason="non-FIPS parameters") @pytest.mark.parametrize( "vector", load_vectors_from_file( @@ -227,6 +235,7 @@ def test_convert_to_numbers(self, backend, with_q): deserialized_private, dh.DHPrivateKeyWithSerialization ) + @pytest.mark.skip_fips(reason="FIPS requires specific parameters") def test_numbers_unsupported_parameters(self, backend): # p is set to P_1536 + 1 because when calling private_key we want it to # fail the DH_check call OpenSSL does, but we specifically want it to @@ -415,6 +424,7 @@ def test_dh_vectors(self, backend, vector): assert int_from_bytes(symkey, "big") == int(vector["k"], 16) + @pytest.mark.skip_fips(reason="non-FIPS parameters") @pytest.mark.parametrize( "vector", load_vectors_from_file( @@ -477,6 +487,7 @@ def test_private_bytes_rejects_invalid(self, encoding, fmt, backend): with pytest.raises(ValueError): key.private_bytes(encoding, fmt, serialization.NoEncryption()) + @pytest.mark.skip_fips(reason="non-FIPS parameters") @pytest.mark.parametrize( ("key_path", "loader_func", "encoding", "is_dhx"), [ @@ -521,6 +532,7 @@ def test_private_bytes_match( ) assert serialized == key_bytes + @pytest.mark.skip_fips(reason="non-FIPS parameters") @pytest.mark.parametrize( ("key_path", "loader_func", "vec_path", "is_dhx"), [ diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index c21a4fa5ee35..32debd46c7d2 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -1757,6 +1757,7 @@ def test_openssh_serialization_unsupported(self, backend): class TestDHSerialization(object): """Test all options with least-supported key type.""" + @pytest.mark.skip_fips(reason="non-FIPS parameters") def test_dh_public_key(self, backend): data = load_vectors_from_file( os.path.join("asymmetric", "DH", "dhkey.pem"), @@ -1788,6 +1789,7 @@ def test_dh_public_key(self, backend): with pytest.raises(ValueError): public_key.public_bytes(enc, fmt) + @pytest.mark.skip_fips(reason="non-FIPS parameters") def test_dh_private_key(self, backend): data = load_vectors_from_file( os.path.join("asymmetric", "DH", "dhkey.pem"), diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index f4d93389ac02..146619b9a84b 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -41,6 +41,7 @@ ) from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ( + dh, dsa, ec, ed25519, @@ -51,6 +52,7 @@ from cryptography.hazmat.primitives.asymmetric.utils import ( decode_dss_signature, ) +from cryptography.utils import int_from_bytes from cryptography.x509.name import _ASN1Type from cryptography.x509.oid import ( AuthorityInformationAccessOID, @@ -65,7 +67,7 @@ from ..hazmat.primitives.fixtures_ec import EC_KEY_SECP256R1 from ..hazmat.primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512 from ..hazmat.primitives.test_ec import _skip_curve_unsupported -from ..utils import load_vectors_from_file +from ..utils import load_nist_vectors, load_vectors_from_file @utils.register_interface(x509.ExtensionType) @@ -5237,12 +5239,14 @@ class TestSignatureRejection(object): """Test if signing rejects DH keys properly.""" def load_key(self, backend): - data = load_vectors_from_file( - os.path.join("asymmetric", "DH", "dhkey.pem"), - lambda pemfile: pemfile.read(), - mode="rb", - ) - return serialization.load_pem_private_key(data, None, backend) + vector = load_vectors_from_file( + os.path.join("asymmetric", "DH", "rfc3526.txt"), + load_nist_vectors, + )[1] + p = int_from_bytes(binascii.unhexlify(vector["p"]), "big") + params = dh.DHParameterNumbers(p, int(vector["g"])) + param = params.parameters(backend) + return param.generate_private_key() def test_crt_signing_check(self, backend): issuer_private_key = self.load_key(backend) From 7e8fff73cf0c597fe2df34daf2027506f84b9d3b Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 8 Dec 2020 17:26:19 -0500 Subject: [PATCH 97/99] Prepare for 3.3 release (#5603) --- CHANGELOG.rst | 6 ++++-- src/cryptography/__about__.py | 2 +- vectors/cryptography_vectors/__about__.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 83d3b703504b..caa8e20c525b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,8 +3,8 @@ Changelog .. _v3-3: -3.3 - `master`_ -~~~~~~~~~~~~~~~ +3.3 - 2020-12-08 +~~~~~~~~~~~~~~~~ .. note:: This version is not yet released and is under active development. @@ -25,6 +25,8 @@ Changelog conform with an upcoming OpenSSL release that no longer supports smaller sizes. These keys were already wildly insecure and should not have been used in any application outside of testing. +* Updated Windows, macOS, and ``manylinux`` wheels to be compiled with + OpenSSL 1.1.1i. * Python 2 support is deprecated in ``cryptography``. This is the last release that will support Python 2. * Added the diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py index 758b17899647..d7a3c1e6de87 100644 --- a/src/cryptography/__about__.py +++ b/src/cryptography/__about__.py @@ -22,7 +22,7 @@ ) __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.3.dev1" +__version__ = "3.3" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" diff --git a/vectors/cryptography_vectors/__about__.py b/vectors/cryptography_vectors/__about__.py index 9252c0f0f0d0..5cb6768fa234 100644 --- a/vectors/cryptography_vectors/__about__.py +++ b/vectors/cryptography_vectors/__about__.py @@ -20,7 +20,7 @@ __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.3.dev1" +__version__ = "3.3" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" From 1ff0d50948bbb6f2aa53d5648f1188a567d941cd Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 9 Dec 2020 18:27:21 -0600 Subject: [PATCH 98/99] re-add Cryptography_HAS_TLSEXT_HOSTNAME and bump for 3.3.1 (#5625) * re-add Cryptography_HAS_TLSEXT_HOSTNAME and bump for 3.3.1 * review feedback --- CHANGELOG.rst | 9 +++++++-- src/_cffi_src/openssl/ssl.py | 6 ++++++ src/cryptography/__about__.py | 2 +- vectors/cryptography_vectors/__about__.py | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index caa8e20c525b..3cb53d00e03d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,13 +1,18 @@ Changelog ========= +.. _v3-3-1: + +3.3.1 - 2020-12-09 +~~~~~~~~~~~~~~~~~~ + +* Re-added a legacy symbol causing problems for older ``pyOpenSSL`` users. + .. _v3-3: 3.3 - 2020-12-08 ~~~~~~~~~~~~~~~~ -.. note:: This version is not yet released and is under active development. - * **BACKWARDS INCOMPATIBLE:** Support for Python 3.5 has been removed due to low usage and maintenance burden. * **BACKWARDS INCOMPATIBLE:** The diff --git a/src/_cffi_src/openssl/ssl.py b/src/_cffi_src/openssl/ssl.py index f3511e454c98..9400f115fb44 100644 --- a/src/_cffi_src/openssl/ssl.py +++ b/src/_cffi_src/openssl/ssl.py @@ -25,6 +25,7 @@ static const long Cryptography_HAS_VERIFIED_CHAIN; static const long Cryptography_HAS_KEYLOG; static const long Cryptography_HAS_GET_PROTO_VERSION; +static const long Cryptography_HAS_TLSEXT_HOSTNAME; /* Internally invented symbol to tell us if SSL_MODE_RELEASE_BUFFERS is * supported @@ -504,6 +505,11 @@ """ CUSTOMIZATIONS = """ +// This symbol is being preserved because removing it will break users with +// pyOpenSSL < 19.1 and pip < 20.x. We need to leave this in place until those +// users have upgraded. PersistentlyDeprecated2020 +static const long Cryptography_HAS_TLSEXT_HOSTNAME = 1; + #if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_VERIFIED_CHAIN = 0; Cryptography_STACK_OF_X509 *(*SSL_get0_verified_chain)(const SSL *) = NULL; diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py index d7a3c1e6de87..0c7eaaa09603 100644 --- a/src/cryptography/__about__.py +++ b/src/cryptography/__about__.py @@ -22,7 +22,7 @@ ) __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.3" +__version__ = "3.3.1" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" diff --git a/vectors/cryptography_vectors/__about__.py b/vectors/cryptography_vectors/__about__.py index 5cb6768fa234..44fe9e7d85e7 100644 --- a/vectors/cryptography_vectors/__about__.py +++ b/vectors/cryptography_vectors/__about__.py @@ -20,7 +20,7 @@ __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.3" +__version__ = "3.3.1" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" From 82b6ce28389f0a317bc55ba2091a74b346db7cae Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 7 Feb 2021 11:36:56 -0500 Subject: [PATCH 99/99] correct buffer overflows cause by integer overflow in openssl (#5747) * correct buffer overflows cause by integer overflow in openssl frustratingly, there is no test for this -- that's because testing this requires allocating more memory than is available in CI. fixes #5615. * backport CI fixes * another CI backport --- .github/workflows/ci.yml | 4 ++-- .github/workflows/wheel-builder.yml | 2 +- .zuul.d/jobs.yaml | 6 +++--- CHANGELOG.rst | 9 +++++++++ docs/conf.py | 2 +- src/cryptography/__about__.py | 4 ++-- src/cryptography/hazmat/backends/openssl/ciphers.py | 2 +- vectors/cryptography_vectors/__about__.py | 4 ++-- 8 files changed, 21 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e03eaede894..3cc8433610f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,7 @@ jobs: linux-distros: runs-on: ubuntu-latest - container: ${{ matrix.IMAGE.IMAGE }} + container: ghcr.io/${{ matrix.IMAGE.IMAGE }} strategy: matrix: IMAGE: @@ -91,7 +91,7 @@ jobs: - {IMAGE: "pyca/cryptography-runner-centos8-fips", TOXENV: "py36", FIPS: true} - {IMAGE: "pyca/cryptography-runner-stretch", TOXENV: "py27"} - {IMAGE: "pyca/cryptography-runner-buster", TOXENV: "py37"} - - {IMAGE: "pyca/cryptography-runner-bullseye", TOXENV: "py38"} + - {IMAGE: "pyca/cryptography-runner-bullseye", TOXENV: "py39"} - {IMAGE: "pyca/cryptography-runner-sid", TOXENV: "py39"} - {IMAGE: "pyca/cryptography-runner-ubuntu-bionic", TOXENV: "py36"} - {IMAGE: "pyca/cryptography-runner-ubuntu-focal", TOXENV: "py38"} diff --git a/.github/workflows/wheel-builder.yml b/.github/workflows/wheel-builder.yml index b74edc2da5a4..94d24c5e3810 100644 --- a/.github/workflows/wheel-builder.yml +++ b/.github/workflows/wheel-builder.yml @@ -8,7 +8,7 @@ on: jobs: manylinux: runs-on: ubuntu-latest - container: ${{ matrix.MANYLINUX.CONTAINER }} + container: ghcr.io/${{ matrix.MANYLINUX.CONTAINER }} strategy: matrix: PYTHON: ["cp27-cp27m", "cp27-cp27mu", "cp36-cp36m"] diff --git a/.zuul.d/jobs.yaml b/.zuul.d/jobs.yaml index 38cab295060f..83f2c6597038 100644 --- a/.zuul.d/jobs.yaml +++ b/.zuul.d/jobs.yaml @@ -44,7 +44,7 @@ vars: wheel_builds: - platform: manylinux2014_aarch64 - image: pyca/cryptography-manylinux2014_aarch64 + image: ghcr.io/pyca/cryptography-manylinux2014_aarch64 pythons: - cp36-cp36m @@ -55,13 +55,13 @@ vars: wheel_builds: - platform: manylinux1_x86_64 - image: pyca/cryptography-manylinux1:x86_64 + image: ghcr.io/pyca/cryptography-manylinux1:x86_64 pythons: - cp27-cp27m - cp27-cp27mu - cp36-cp36m - platform: manylinux2010_x86_64 - image: pyca/cryptography-manylinux2010:x86_64 + image: ghcr.io/pyca/cryptography-manylinux2010:x86_64 pythons: - cp27-cp27m - cp27-cp27mu diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3cb53d00e03d..4dd71460069f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,15 @@ Changelog ========= +.. _v3-3-2: + +3.3.2 - 2021-02-07 +~~~~~~~~~~~~~~~~~~ + +* **SECURITY ISSUE:** Fixed a bug where certain sequences of ``update()`` calls + when symmetrically encrypting very large payloads (>2GB) could result in an + integer overflow, leading to buffer overflows. *CVE-2020-36242* + .. _v3-3-1: 3.3.1 - 2020-12-09 diff --git a/docs/conf.py b/docs/conf.py index 33240d8de1a3..fb67adabc905 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -71,7 +71,7 @@ # General information about the project. project = "Cryptography" -copyright = "2013-2020, Individual Contributors" +copyright = "2013-2021, Individual Contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py index 0c7eaaa09603..f816509257e8 100644 --- a/src/cryptography/__about__.py +++ b/src/cryptography/__about__.py @@ -22,10 +22,10 @@ ) __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.3.1" +__version__ = "3.3.2" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" __license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2013-2020 {}".format(__author__) +__copyright__ = "Copyright 2013-2021 {}".format(__author__) diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py index 1e805d235aa2..ad5dad3f7ed2 100644 --- a/src/cryptography/hazmat/backends/openssl/ciphers.py +++ b/src/cryptography/hazmat/backends/openssl/ciphers.py @@ -17,7 +17,7 @@ class _CipherContext(object): _ENCRYPT = 1 _DECRYPT = 0 - _MAX_CHUNK_SIZE = 2 ** 31 - 1 + _MAX_CHUNK_SIZE = 2 ** 30 - 1 def __init__(self, backend, cipher, mode, operation): self._backend = backend diff --git a/vectors/cryptography_vectors/__about__.py b/vectors/cryptography_vectors/__about__.py index 44fe9e7d85e7..dc069d51ba86 100644 --- a/vectors/cryptography_vectors/__about__.py +++ b/vectors/cryptography_vectors/__about__.py @@ -20,10 +20,10 @@ __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.3.1" +__version__ = "3.3.2" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" __license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2013-2020 %s" % __author__ +__copyright__ = "Copyright 2013-2021 %s" % __author__