From bbfd7e0ab5b7f93d6ab8a7c668727e918606c9e4 Mon Sep 17 00:00:00 2001 From: Jose Corella Date: Tue, 7 Jun 2022 10:58:29 -0700 Subject: [PATCH 1/6] refactor: change verify_interface to isinstance --- requirements.txt | 2 +- .../internal/crypto/authentication.py | 10 ++- .../internal/crypto/elliptic_curve.py | 11 ++-- .../unit/test_crypto_authentication_signer.py | 61 ++++++++++++------- .../test_crypto_authentication_verifier.py | 7 ++- test/unit/test_crypto_elliptic_curve.py | 20 +++--- .../test_crypto_prehashing_authenticator.py | 24 +++----- 7 files changed, 71 insertions(+), 64 deletions(-) diff --git a/requirements.txt b/requirements.txt index fab293c05..f78177c65 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ boto3>=1.10.0 -cryptography>=2.5.0 +cryptography>=2.5.0,<3.3.2 attrs>=17.4.0 wrapt>=1.10.11 diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index 560fdb2a2..cf443bbe6 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -18,7 +18,6 @@ from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import Prehashed -from cryptography.utils import InterfaceNotImplemented, verify_interface from ...exceptions import NotSupportedError from .elliptic_curve import ( @@ -47,11 +46,10 @@ def __init__(self, algorithm, key): def _set_signature_type(self): """Ensures that the algorithm signature type is a known type and sets a reference value.""" - try: - verify_interface(ec.EllipticCurve, self.algorithm.signing_algorithm_info) - return ec.EllipticCurve - except InterfaceNotImplemented: - raise NotSupportedError("Unsupported signing algorithm info") + for method in ec.EllipticCurve.__abstractmethods__: + if not hasattr(self.algorithm.signing_algorithm_info(), method): + raise NotSupportedError("Unsupported signing algorithm info") + return ec.EllipticCurve def _build_hasher(self): """Builds the hasher instance which will calculate the digest of all passed data. diff --git a/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py b/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py index fd96abf6f..c46bc2262 100644 --- a/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py +++ b/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py @@ -18,7 +18,7 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import Prehashed, decode_dss_signature, encode_dss_signature -from cryptography.utils import InterfaceNotImplemented, int_from_bytes, int_to_bytes, verify_interface +from cryptography.utils import int_from_bytes, int_to_bytes from ...exceptions import NotSupportedError from ..str_ops import to_bytes @@ -183,8 +183,7 @@ def generate_ecc_signing_key(algorithm): :returns: Generated signing key :raises NotSupportedError: if signing algorithm is not supported on this platform """ - try: - verify_interface(ec.EllipticCurve, algorithm.signing_algorithm_info) - return ec.generate_private_key(curve=algorithm.signing_algorithm_info(), backend=default_backend()) - except InterfaceNotImplemented: - raise NotSupportedError("Unsupported signing algorithm info") + for method in ec.EllipticCurve.__abstractmethods__: + if not hasattr(algorithm.signing_algorithm_info(), method): + raise NotSupportedError("Unsupported signing algorithm info") + return ec.generate_private_key(curve=algorithm.signing_algorithm_info(), backend=default_backend()) diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index 440fa4f9b..07cc91793 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -1,18 +1,6 @@ -# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. """Unit test suite for ``aws_encryption_sdk.internal.crypto.authentication.Signer``.""" import pytest -from mock import MagicMock, sentinel +from mock import MagicMock, sentinel, patch from pytest_mock import mocker # noqa pylint: disable=unused-import import aws_encryption_sdk.internal.crypto.authentication @@ -30,6 +18,12 @@ def patch_default_backend(mocker): yield aws_encryption_sdk.internal.crypto.authentication.default_backend +@pytest.fixture +def patch_ec(mocker): + mocker.patch.object(aws_encryption_sdk.internal.crypto.authentication, "ec") + yield aws_encryption_sdk.internal.crypto.authentication.ec + + @pytest.fixture def patch_serialization(mocker): mocker.patch.object(aws_encryption_sdk.internal.crypto.authentication, "serialization") @@ -71,8 +65,11 @@ def test_f_signer_key_bytes(): assert test.key_bytes() == VALUES["ecc_private_key_prime_private_bytes"] -def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher): - _algorithm = MagicMock() +def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): + patch_ec.EllipticCurve.__abstractmethods__ = set() + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + signer = Signer.from_key_bytes(algorithm=_algorithm, key_bytes=sentinel.key_bytes) patch_serialization.load_der_private_key.assert_called_once_with( @@ -83,9 +80,12 @@ def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch assert signer.key is patch_serialization.load_der_private_key.return_value -def test_signer_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher): +def test_signer_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): + patch_ec.EllipticCurve.__abstractmethods__ = set() + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) private_key = MagicMock() - signer = Signer(MagicMock(), key=private_key) + signer = Signer(algorithm, key=private_key) test = signer.key_bytes() @@ -98,13 +98,23 @@ def test_signer_key_bytes(patch_default_backend, patch_serialization, patch_buil def test_signer_encoded_public_key( - patch_default_backend, patch_serialization, patch_build_hasher, patch_ecc_encode_compressed_point, patch_base64 + patch_default_backend, + patch_serialization, + patch_build_hasher, + patch_ecc_encode_compressed_point, + patch_base64, + patch_ec ): patch_ecc_encode_compressed_point.return_value = sentinel.compressed_point patch_base64.b64encode.return_value = sentinel.encoded_point private_key = MagicMock() - signer = Signer(MagicMock(), key=private_key) + patch_ec.EllipticCurve.__abstractmethods__ = set() + + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + + signer = Signer(algorithm, key=private_key) test_key = signer.encoded_public_key() patch_ecc_encode_compressed_point.assert_called_once_with(private_key) @@ -112,16 +122,21 @@ def test_signer_encoded_public_key( assert test_key == sentinel.encoded_point -def test_signer_update(patch_default_backend, patch_serialization, patch_build_hasher): - signer = Signer(MagicMock(), key=MagicMock()) +def test_signer_update(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): + patch_ec.EllipticCurve.__abstractmethods__ = set() + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + signer = Signer(algorithm, key=MagicMock()) signer.update(sentinel.data) patch_build_hasher.return_value.update.assert_called_once_with(sentinel.data) def test_signer_finalize( - patch_default_backend, patch_serialization, patch_build_hasher, patch_ecc_static_length_signature + patch_default_backend, patch_serialization, patch_build_hasher, patch_ecc_static_length_signature, patch_ec ): - algorithm = MagicMock() + patch_ec.EllipticCurve.__abstractmethods__ = set() + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) private_key = MagicMock() signer = Signer(algorithm, key=private_key) diff --git a/test/unit/test_crypto_authentication_verifier.py b/test/unit/test_crypto_authentication_verifier.py index b75d1c310..1062d77e8 100644 --- a/test/unit/test_crypto_authentication_verifier.py +++ b/test/unit/test_crypto_authentication_verifier.py @@ -18,6 +18,7 @@ import aws_encryption_sdk.internal.crypto.authentication from aws_encryption_sdk.internal.crypto.authentication import Verifier from aws_encryption_sdk.internal.defaults import ALGORITHM +from cryptography.hazmat.primitives.asymmetric import ec from .test_crypto import VALUES @@ -89,12 +90,16 @@ def test_verifier_from_encoded_point( patch_ecc_public_numbers_from_compressed_point, patch_base64, patch_build_hasher, + patch_ec ): mock_point_instance = MagicMock() mock_point_instance.public_key.return_value = sentinel.public_key patch_ecc_public_numbers_from_compressed_point.return_value = mock_point_instance patch_base64.b64decode.return_value = sentinel.compressed_point - algorithm = MagicMock() + + patch_ec.EllipticCurve.__abstractmethods__ = set() + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) verifier = Verifier.from_encoded_point(algorithm=algorithm, encoded_point=sentinel.encoded_point) diff --git a/test/unit/test_crypto_elliptic_curve.py b/test/unit/test_crypto_elliptic_curve.py index 445a28c79..ef1a4ee61 100644 --- a/test/unit/test_crypto_elliptic_curve.py +++ b/test/unit/test_crypto_elliptic_curve.py @@ -15,7 +15,6 @@ import pytest from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.utils import InterfaceNotImplemented from mock import MagicMock, sentinel from pytest_mock import mocker # noqa pylint: disable=unused-import @@ -72,12 +71,6 @@ def patch_ecc_decode_compressed_point(mocker): yield aws_encryption_sdk.internal.crypto.elliptic_curve._ecc_decode_compressed_point -@pytest.fixture -def patch_verify_interface(mocker): - mocker.patch.object(aws_encryption_sdk.internal.crypto.elliptic_curve, "verify_interface") - yield aws_encryption_sdk.internal.crypto.elliptic_curve.verify_interface - - @pytest.fixture def patch_ecc_curve_parameters(mocker): mocker.patch.object(aws_encryption_sdk.internal.crypto.elliptic_curve, "_ECC_CURVE_PARAMETERS") @@ -374,23 +367,24 @@ def test_ecc_public_numbers_from_compressed_point(patch_ec, patch_ecc_decode_com assert test == sentinel.public_numbers_instance -def test_generate_ecc_signing_key_supported(patch_default_backend, patch_ec, patch_verify_interface): +def test_generate_ecc_signing_key_supported(patch_default_backend, patch_ec): patch_ec.generate_private_key.return_value = sentinel.raw_signing_key - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info) + patch_ec.EllipticCurve.__abstractmethods__ = set() + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) test_signing_key = generate_ecc_signing_key(algorithm=mock_algorithm) - patch_verify_interface.assert_called_once_with(patch_ec.EllipticCurve, mock_algorithm_info) patch_ec.generate_private_key.assert_called_once_with( curve=sentinel.algorithm_info, backend=patch_default_backend.return_value ) assert test_signing_key is sentinel.raw_signing_key -def test_generate_ecc_signing_key_unsupported(patch_default_backend, patch_ec, patch_verify_interface): - patch_verify_interface.side_effect = InterfaceNotImplemented - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info) +def test_generate_ecc_signing_key_unsupported(patch_default_backend, patch_ec): + patch_ec.generate_private_key.return_value = sentinel.raw_signing_key + patch_ec.EllipticCurve.__abstractmethods__ = set("notName") + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) with pytest.raises(NotSupportedError) as excinfo: diff --git a/test/unit/test_crypto_prehashing_authenticator.py b/test/unit/test_crypto_prehashing_authenticator.py index 158872f8c..2f9c52583 100644 --- a/test/unit/test_crypto_prehashing_authenticator.py +++ b/test/unit/test_crypto_prehashing_authenticator.py @@ -35,12 +35,6 @@ def patch_build_hasher(mocker): yield _PrehashingAuthenticator._build_hasher -@pytest.fixture -def patch_cryptography_utils_verify_interface(mocker): - mocker.patch.object(aws_encryption_sdk.internal.crypto.authentication, "verify_interface") - yield aws_encryption_sdk.internal.crypto.authentication.verify_interface - - @pytest.fixture def patch_cryptography_ec(mocker): mocker.patch.object(aws_encryption_sdk.internal.crypto.authentication, "ec") @@ -71,23 +65,25 @@ def test_init(patch_set_signature_type, patch_build_hasher): def test_set_signature_type_elliptic_curve( - patch_build_hasher, patch_cryptography_utils_verify_interface, patch_cryptography_ec + patch_build_hasher, patch_cryptography_ec ): - mock_algorithm = MagicMock() + patch_cryptography_ec.generate_private_key.return_value = sentinel.raw_signing_key + patch_cryptography_ec.EllipticCurve.__abstractmethods__ = set() + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_cryptography_ec.EllipticCurve) + mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) test = _PrehashingAuthenticator(algorithm=mock_algorithm, key=sentinel.key) - patch_cryptography_utils_verify_interface.assert_called_once_with( - patch_cryptography_ec.EllipticCurve, mock_algorithm.signing_algorithm_info - ) assert test._signature_type is patch_cryptography_ec.EllipticCurve def test_set_signature_type_unknown( - patch_build_hasher, patch_cryptography_utils_verify_interface, patch_cryptography_ec + patch_build_hasher, patch_cryptography_ec ): - patch_cryptography_utils_verify_interface.side_effect = InterfaceNotImplemented + patch_cryptography_ec.EllipticCurve.__abstractmethods__ = set("invalidSetup") + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_cryptography_ec.EllipticCurve) + mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) with pytest.raises(NotSupportedError) as excinfo: - _PrehashingAuthenticator(algorithm=MagicMock(), key=sentinel.key) + _PrehashingAuthenticator(algorithm=mock_algorithm, key=sentinel.key) excinfo.match(r"Unsupported signing algorithm info") From d98e57354c9589143396bb97c465479407ce6557 Mon Sep 17 00:00:00 2001 From: Jose Corella Date: Thu, 9 Jun 2022 14:53:40 -0700 Subject: [PATCH 2/6] chore(deps): fix linting --- dev_requirements/linter-requirements.txt | 13 +++-- dev_requirements/release-requirements.txt | 4 ++ .../unit/test_crypto_authentication_signer.py | 14 +++++- .../test_crypto_authentication_verifier.py | 1 - .../test_crypto_prehashing_authenticator.py | 1 - test_vector_handlers/tox.ini | 47 +++++-------------- 6 files changed, 36 insertions(+), 44 deletions(-) create mode 100644 dev_requirements/release-requirements.txt diff --git a/dev_requirements/linter-requirements.txt b/dev_requirements/linter-requirements.txt index e759859c7..542451992 100644 --- a/dev_requirements/linter-requirements.txt +++ b/dev_requirements/linter-requirements.txt @@ -1,14 +1,13 @@ -bandit==1.7.0 -black==21.12b0 +bandit==1.7.4 +black==22.3.0 doc8==0.10.1 flake8==4.0.1 -flake8-bugbear==21.11.29 +flake8-bugbear==22.1.11 flake8-docstrings==1.6.0 flake8-print==4.0.0 isort==5.10.1 pyflakes==2.4.0 -pylint==2.12.2 -readme_renderer==32.0 +pylint==2.13.5 +readme_renderer==34.0 seed-isort-config==2.2.0 -vulture==2.3 -sphinx==4.4.0 \ No newline at end of file +vulture==2.3 diff --git a/dev_requirements/release-requirements.txt b/dev_requirements/release-requirements.txt new file mode 100644 index 000000000..1b1d710ca --- /dev/null +++ b/dev_requirements/release-requirements.txt @@ -0,0 +1,4 @@ +pypi-parker==0.1.2 +setuptools==62.0.0 +twine==3.8.0 +wheel==0.37.1 diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index 07cc91793..6a2a8c6ae 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -1,6 +1,18 @@ +# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. """Unit test suite for ``aws_encryption_sdk.internal.crypto.authentication.Signer``.""" import pytest -from mock import MagicMock, sentinel, patch +from mock import MagicMock, sentinel from pytest_mock import mocker # noqa pylint: disable=unused-import import aws_encryption_sdk.internal.crypto.authentication diff --git a/test/unit/test_crypto_authentication_verifier.py b/test/unit/test_crypto_authentication_verifier.py index 1062d77e8..86ca69f08 100644 --- a/test/unit/test_crypto_authentication_verifier.py +++ b/test/unit/test_crypto_authentication_verifier.py @@ -18,7 +18,6 @@ import aws_encryption_sdk.internal.crypto.authentication from aws_encryption_sdk.internal.crypto.authentication import Verifier from aws_encryption_sdk.internal.defaults import ALGORITHM -from cryptography.hazmat.primitives.asymmetric import ec from .test_crypto import VALUES diff --git a/test/unit/test_crypto_prehashing_authenticator.py b/test/unit/test_crypto_prehashing_authenticator.py index 2f9c52583..036bb8c65 100644 --- a/test/unit/test_crypto_prehashing_authenticator.py +++ b/test/unit/test_crypto_prehashing_authenticator.py @@ -12,7 +12,6 @@ # language governing permissions and limitations under the License. """Unit test suite for ``aws_encryption_sdk.internal.crypto._PrehashingAuthenticater``.""" import pytest -from cryptography.utils import InterfaceNotImplemented from mock import MagicMock, sentinel from pytest_mock import mocker # noqa pylint: disable=unused-import diff --git a/test_vector_handlers/tox.ini b/test_vector_handlers/tox.ini index 8aebe09da..b9caa5d12 100644 --- a/test_vector_handlers/tox.ini +++ b/test_vector_handlers/tox.ini @@ -117,12 +117,7 @@ commands = # Linters [testenv:flake8] basepython = python3 -deps = - .. - flake8 - flake8-docstrings - # https://github.com/JBKahn/flake8-print/pull/30 - flake8-print>=3.1.0 +deps = -r../dev_requirements/linter-requirements.txt commands = flake8 \ src/awses_test_vectors/ \ @@ -132,15 +127,13 @@ commands = [testenv:flake8-tests] basepython = {[testenv:flake8]basepython} -deps = - .. - flake8 +deps = -r../dev_requirements/linter-requirements.txt commands = flake8 \ # Ignore F811 redefinition errors in tests (breaks with pytest-mock use) # E203 is not PEP8 compliant https://github.com/ambv/black#slices # W503 is not PEP8 compliant https://github.com/ambv/black#line-breaks--binary-operators - --ignore F811,E203,W503 \ + --ignore F811,E203,W503,D \ test/ \ {posargs} @@ -148,9 +141,7 @@ commands = basepython = python3 deps = -rtest/requirements.txt - .. - pyflakes - pylint + -r../dev_requirements/linter-requirements.txt commands = pylint \ --rcfile=src/pylintrc \ @@ -169,8 +160,7 @@ commands = [testenv:blacken-src] basepython = python3 -deps = - black +deps = -r../dev_requirements/linter-requirements.txt commands = black --line-length 120 \ src/awses_test_vectors/ \ @@ -196,14 +186,12 @@ commands = [testenv:isort-seed] basepython = python3 -deps = seed-isort-config +deps = -r../dev_requirements/linter-requirements.txt commands = seed-isort-config [testenv:isort] basepython = python3 -deps = - isort - .. +deps = -r../dev_requirements/linter-requirements.txt commands = isort -rc \ src \ test \ @@ -228,29 +216,23 @@ commands = [testenv:doc8] basepython = python3 -deps = - sphinx - doc8 +deps = -r../dev_requirements/linter-requirements.txt commands = doc8 doc/index.rst README.rst CHANGELOG.rst [testenv:readme] basepython = python3 -deps = - .. - readme_renderer +deps = -r../dev_requirements/linter-requirements.txt commands = python setup.py check -r -s [testenv:bandit] basepython = python3 -deps = - .. - bandit>=1.5.1 +deps = -r../dev_requirements/linter-requirements.txt commands = bandit -r src/awses_test_vectors/ # Prone to false positives: only run independently [testenv:vulture] basepython = python3 -deps = vulture +deps = -r../dev_requirements/linter-requirements.txt commands = vulture src/awses_test_vectors/ [testenv:linters] @@ -296,9 +278,7 @@ commands = [testenv:park] basepython = python3 skip_install = true -deps = - pypi-parker - setuptools +deps = -r../dev_requirements/release-requirements.txt commands = python setup.py park [testenv:build] @@ -306,8 +286,7 @@ basepython = python3 skip_install = true deps = {[testenv:docs]deps} - wheel - setuptools + -r../dev_requirements/release-requirements.txt commands = {[testenv:docs]commands} python setup.py sdist bdist_wheel From 664502aa0ba6fab817289ed6ec0ea3357a928708 Mon Sep 17 00:00:00 2001 From: Jose Corella Date: Mon, 13 Jun 2022 09:07:55 -0700 Subject: [PATCH 3/6] chore: refactor duplicate code --- requirements.txt | 2 +- .../internal/crypto/authentication.py | 6 +++--- .../internal/crypto/elliptic_curve.py | 6 +++--- .../internal/utils/__init__.py | 15 +++++++++++++++ .../unit/test_crypto_authentication_signer.py | 2 ++ .../test_crypto_authentication_verifier.py | 3 ++- test/unit/test_crypto_elliptic_curve.py | 10 +++++++--- .../test_crypto_prehashing_authenticator.py | 7 +++++-- test/unit/test_utils.py | 19 +++++++++++++++++++ 9 files changed, 57 insertions(+), 13 deletions(-) diff --git a/requirements.txt b/requirements.txt index f78177c65..beb06476c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ boto3>=1.10.0 -cryptography>=2.5.0,<3.3.2 +cryptography>=2.5.0,<=3.3.2 attrs>=17.4.0 wrapt>=1.10.11 diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index cf443bbe6..861533989 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -14,6 +14,7 @@ import base64 import logging +from aws_encryption_sdk.internal.utils import verify_interface from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec @@ -46,9 +47,8 @@ def __init__(self, algorithm, key): def _set_signature_type(self): """Ensures that the algorithm signature type is a known type and sets a reference value.""" - for method in ec.EllipticCurve.__abstractmethods__: - if not hasattr(self.algorithm.signing_algorithm_info(), method): - raise NotSupportedError("Unsupported signing algorithm info") + if not verify_interface(self.algorithm): + raise NotSupportedError("Unsupported signing algorithm info") return ec.EllipticCurve def _build_hasher(self): diff --git a/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py b/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py index c46bc2262..890d77370 100644 --- a/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py +++ b/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py @@ -15,6 +15,7 @@ from collections import namedtuple import six +from aws_encryption_sdk.internal.utils import verify_interface from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import Prehashed, decode_dss_signature, encode_dss_signature @@ -183,7 +184,6 @@ def generate_ecc_signing_key(algorithm): :returns: Generated signing key :raises NotSupportedError: if signing algorithm is not supported on this platform """ - for method in ec.EllipticCurve.__abstractmethods__: - if not hasattr(algorithm.signing_algorithm_info(), method): - raise NotSupportedError("Unsupported signing algorithm info") + if not verify_interface(algorithm): + raise NotSupportedError("Unsupported signing algorithm info") return ec.generate_private_key(curve=algorithm.signing_algorithm_info(), backend=default_backend()) diff --git a/src/aws_encryption_sdk/internal/utils/__init__.py b/src/aws_encryption_sdk/internal/utils/__init__.py index dac38ac73..1d119e871 100644 --- a/src/aws_encryption_sdk/internal/utils/__init__.py +++ b/src/aws_encryption_sdk/internal/utils/__init__.py @@ -22,6 +22,7 @@ from aws_encryption_sdk.identifiers import ContentAADString, ContentType from aws_encryption_sdk.internal.str_ops import to_bytes from aws_encryption_sdk.structures import EncryptedDataKey +from cryptography.hazmat.primitives.asymmetric import ec from .streams import InsistentReaderBytesIO @@ -163,3 +164,17 @@ def source_data_key_length_check(source_data_key, algorithm): actual=len(source_data_key.data_key), required=algorithm.kdf_input_len ) ) + + +def verify_interface(algorithm): + """Validates that the provided EllipticCurve Algorithm is correctly implemented by + checking if it implements both of the abstract methods from the EllipticCurve + abstract class. + + :param algorithm: Algorithm object which directs which Elliptic Curve will be used + :type algorithm: aws_encryption_sdk.identifiers.Algorithm + """ + for method in ec.EllipticCurve.__abstractmethods__: + if not hasattr(algorithm.signing_algorithm_info(), method): + return False + return True diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index 6a2a8c6ae..5db965263 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -80,6 +80,8 @@ def test_f_signer_key_bytes(): def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): patch_ec.EllipticCurve.__abstractmethods__ = set() mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + mock_algorithm_info.return_value.name = True + mock_algorithm_info.return_value.key_size = True _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) signer = Signer.from_key_bytes(algorithm=_algorithm, key_bytes=sentinel.key_bytes) diff --git a/test/unit/test_crypto_authentication_verifier.py b/test/unit/test_crypto_authentication_verifier.py index 86ca69f08..c3b0ccfdf 100644 --- a/test/unit/test_crypto_authentication_verifier.py +++ b/test/unit/test_crypto_authentication_verifier.py @@ -98,12 +98,13 @@ def test_verifier_from_encoded_point( patch_ec.EllipticCurve.__abstractmethods__ = set() mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + mock_algorithm_info.return_value.name = True + mock_algorithm_info.return_value.key_size = True algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) verifier = Verifier.from_encoded_point(algorithm=algorithm, encoded_point=sentinel.encoded_point) patch_base64.b64decode.assert_called_once_with(sentinel.encoded_point) - algorithm.signing_algorithm_info.assert_called_once_with() patch_ecc_public_numbers_from_compressed_point.assert_called_once_with( curve=algorithm.signing_algorithm_info.return_value, compressed_point=sentinel.compressed_point ) diff --git a/test/unit/test_crypto_elliptic_curve.py b/test/unit/test_crypto_elliptic_curve.py index ef1a4ee61..8903ccc63 100644 --- a/test/unit/test_crypto_elliptic_curve.py +++ b/test/unit/test_crypto_elliptic_curve.py @@ -19,6 +19,7 @@ from pytest_mock import mocker # noqa pylint: disable=unused-import import aws_encryption_sdk.internal.crypto.elliptic_curve +import aws_encryption_sdk.internal.defaults from aws_encryption_sdk.exceptions import NotSupportedError from aws_encryption_sdk.internal.crypto.elliptic_curve import ( _ECC_CURVE_PARAMETERS, @@ -369,8 +370,10 @@ def test_ecc_public_numbers_from_compressed_point(patch_ec, patch_ecc_decode_com def test_generate_ecc_signing_key_supported(patch_default_backend, patch_ec): patch_ec.generate_private_key.return_value = sentinel.raw_signing_key - patch_ec.EllipticCurve.__abstractmethods__ = set() + patch_ec.EllipticCurve.__abstractmethods__ = {"key_size", "name"} mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + mock_algorithm_info.return_value.name = MagicMock(return_value=True) + mock_algorithm_info.return_value.key_size = MagicMock(return_value=True) mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) test_signing_key = generate_ecc_signing_key(algorithm=mock_algorithm) @@ -383,8 +386,9 @@ def test_generate_ecc_signing_key_supported(patch_default_backend, patch_ec): def test_generate_ecc_signing_key_unsupported(patch_default_backend, patch_ec): patch_ec.generate_private_key.return_value = sentinel.raw_signing_key - patch_ec.EllipticCurve.__abstractmethods__ = set("notName") - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + mock_algorithm_info = MagicMock(return_value=sentinel.invalid_algorithm_info, spec=patch_ec.EllipticCurve) + mock_algorithm_info.return_value.not_name = MagicMock(return_value=True) + mock_algorithm_info.return_value.not_key_size = MagicMock(return_value=True) mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) with pytest.raises(NotSupportedError) as excinfo: diff --git a/test/unit/test_crypto_prehashing_authenticator.py b/test/unit/test_crypto_prehashing_authenticator.py index 036bb8c65..b6226202a 100644 --- a/test/unit/test_crypto_prehashing_authenticator.py +++ b/test/unit/test_crypto_prehashing_authenticator.py @@ -69,6 +69,8 @@ def test_set_signature_type_elliptic_curve( patch_cryptography_ec.generate_private_key.return_value = sentinel.raw_signing_key patch_cryptography_ec.EllipticCurve.__abstractmethods__ = set() mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_cryptography_ec.EllipticCurve) + mock_algorithm_info.return_value.name = MagicMock(return_value=True) + mock_algorithm_info.return_value.key_size = MagicMock(return_value=True) mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) test = _PrehashingAuthenticator(algorithm=mock_algorithm, key=sentinel.key) @@ -78,8 +80,9 @@ def test_set_signature_type_elliptic_curve( def test_set_signature_type_unknown( patch_build_hasher, patch_cryptography_ec ): - patch_cryptography_ec.EllipticCurve.__abstractmethods__ = set("invalidSetup") - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_cryptography_ec.EllipticCurve) + mock_algorithm_info = MagicMock(return_value=sentinel.not_algorithm_info, spec=patch_cryptography_ec.EllipticCurve) + mock_algorithm_info.return_value.not_name = MagicMock(return_value=True) + mock_algorithm_info.return_value.not_key_size = MagicMock(return_value=True) mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) with pytest.raises(NotSupportedError) as excinfo: _PrehashingAuthenticator(algorithm=mock_algorithm, key=sentinel.key) diff --git a/test/unit/test_utils.py b/test/unit/test_utils.py index 72f98e731..fa5cfd0b1 100644 --- a/test/unit/test_utils.py +++ b/test/unit/test_utils.py @@ -19,6 +19,7 @@ import aws_encryption_sdk.identifiers import aws_encryption_sdk.internal.utils +from aws_encryption_sdk.internal.utils import verify_interface from aws_encryption_sdk.exceptions import InvalidDataKeyError, SerializationError, UnknownIdentityError from aws_encryption_sdk.internal.defaults import MAX_FRAME_SIZE from aws_encryption_sdk.structures import DataKey, EncryptedDataKey, MasterKeyInfo, RawDataKey @@ -29,12 +30,30 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] +@pytest.yield_fixture +def patch_ec(mocker): + mocker.patch.object(aws_encryption_sdk.internal.crypto.authentication, "ec") + yield aws_encryption_sdk.internal.crypto.authentication.ec + + def test_prep_stream_data_passthrough(): test = aws_encryption_sdk.internal.utils.prep_stream_data(io.BytesIO(b"some data")) assert_prepped_stream_identity(test, io.BytesIO) +def test_verify_interface(patch_ec): + patch_ec.EllipticCurve.__abstractmethods__ = set(("key_size", "name")) + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + mock_algorithm_info.return_value.name = True + mock_algorithm_info.return_value.key_size = True + _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + + implemented = verify_interface(algorithm=_algorithm) + + assert implemented is True + + @pytest.mark.parametrize("source", (u"some unicode data ловие", b"\x00\x01\x02")) def test_prep_stream_data_wrap(source): test = aws_encryption_sdk.internal.utils.prep_stream_data(source) From 4a231f7680eddab007f0dae6d3538fb4cab575cb Mon Sep 17 00:00:00 2001 From: Jose Corella Date: Tue, 14 Jun 2022 13:37:04 -0700 Subject: [PATCH 4/6] chore: run formatter --- src/aws_encryption_sdk/internal/crypto/authentication.py | 3 ++- src/aws_encryption_sdk/internal/crypto/elliptic_curve.py | 3 ++- src/aws_encryption_sdk/internal/utils/__init__.py | 2 +- test/unit/test_utils.py | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index 861533989..a9aa1fdef 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -14,12 +14,13 @@ import base64 import logging -from aws_encryption_sdk.internal.utils import verify_interface from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import Prehashed +from aws_encryption_sdk.internal.utils import verify_interface + from ...exceptions import NotSupportedError from .elliptic_curve import ( _ecc_encode_compressed_point, diff --git a/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py b/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py index 890d77370..a8f61b811 100644 --- a/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py +++ b/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py @@ -15,12 +15,13 @@ from collections import namedtuple import six -from aws_encryption_sdk.internal.utils import verify_interface from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import Prehashed, decode_dss_signature, encode_dss_signature from cryptography.utils import int_from_bytes, int_to_bytes +from aws_encryption_sdk.internal.utils import verify_interface + from ...exceptions import NotSupportedError from ..str_ops import to_bytes diff --git a/src/aws_encryption_sdk/internal/utils/__init__.py b/src/aws_encryption_sdk/internal/utils/__init__.py index 1d119e871..b5baa2941 100644 --- a/src/aws_encryption_sdk/internal/utils/__init__.py +++ b/src/aws_encryption_sdk/internal/utils/__init__.py @@ -16,13 +16,13 @@ import os import six +from cryptography.hazmat.primitives.asymmetric import ec import aws_encryption_sdk.internal.defaults from aws_encryption_sdk.exceptions import InvalidDataKeyError, SerializationError, UnknownIdentityError from aws_encryption_sdk.identifiers import ContentAADString, ContentType from aws_encryption_sdk.internal.str_ops import to_bytes from aws_encryption_sdk.structures import EncryptedDataKey -from cryptography.hazmat.primitives.asymmetric import ec from .streams import InsistentReaderBytesIO diff --git a/test/unit/test_utils.py b/test/unit/test_utils.py index fa5cfd0b1..e9a4444bf 100644 --- a/test/unit/test_utils.py +++ b/test/unit/test_utils.py @@ -19,9 +19,9 @@ import aws_encryption_sdk.identifiers import aws_encryption_sdk.internal.utils -from aws_encryption_sdk.internal.utils import verify_interface from aws_encryption_sdk.exceptions import InvalidDataKeyError, SerializationError, UnknownIdentityError from aws_encryption_sdk.internal.defaults import MAX_FRAME_SIZE +from aws_encryption_sdk.internal.utils import verify_interface from aws_encryption_sdk.structures import DataKey, EncryptedDataKey, MasterKeyInfo, RawDataKey from .test_values import VALUES From 943e8d0d613a5fb6baea5ec582c23f536a41d1f4 Mon Sep 17 00:00:00 2001 From: Jose Corella Date: Tue, 14 Jun 2022 16:03:46 -0700 Subject: [PATCH 5/6] naming is hard --- src/aws_encryption_sdk/internal/crypto/authentication.py | 4 ++-- src/aws_encryption_sdk/internal/crypto/elliptic_curve.py | 4 ++-- src/aws_encryption_sdk/internal/utils/__init__.py | 2 +- test/unit/test_utils.py | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index a9aa1fdef..ef60f34ea 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -19,7 +19,7 @@ from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import Prehashed -from aws_encryption_sdk.internal.utils import verify_interface +from aws_encryption_sdk.internal.utils import verify_ec_interface from ...exceptions import NotSupportedError from .elliptic_curve import ( @@ -48,7 +48,7 @@ def __init__(self, algorithm, key): def _set_signature_type(self): """Ensures that the algorithm signature type is a known type and sets a reference value.""" - if not verify_interface(self.algorithm): + if not verify_ec_interface(self.algorithm): raise NotSupportedError("Unsupported signing algorithm info") return ec.EllipticCurve diff --git a/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py b/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py index a8f61b811..330d9b2a5 100644 --- a/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py +++ b/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py @@ -20,7 +20,7 @@ from cryptography.hazmat.primitives.asymmetric.utils import Prehashed, decode_dss_signature, encode_dss_signature from cryptography.utils import int_from_bytes, int_to_bytes -from aws_encryption_sdk.internal.utils import verify_interface +from aws_encryption_sdk.internal.utils import verify_ec_interface from ...exceptions import NotSupportedError from ..str_ops import to_bytes @@ -185,6 +185,6 @@ def generate_ecc_signing_key(algorithm): :returns: Generated signing key :raises NotSupportedError: if signing algorithm is not supported on this platform """ - if not verify_interface(algorithm): + if not verify_ec_interface(algorithm): raise NotSupportedError("Unsupported signing algorithm info") return ec.generate_private_key(curve=algorithm.signing_algorithm_info(), backend=default_backend()) diff --git a/src/aws_encryption_sdk/internal/utils/__init__.py b/src/aws_encryption_sdk/internal/utils/__init__.py index b5baa2941..cbb3202d4 100644 --- a/src/aws_encryption_sdk/internal/utils/__init__.py +++ b/src/aws_encryption_sdk/internal/utils/__init__.py @@ -166,7 +166,7 @@ def source_data_key_length_check(source_data_key, algorithm): ) -def verify_interface(algorithm): +def verify_ec_interface(algorithm): """Validates that the provided EllipticCurve Algorithm is correctly implemented by checking if it implements both of the abstract methods from the EllipticCurve abstract class. diff --git a/test/unit/test_utils.py b/test/unit/test_utils.py index e9a4444bf..36d92a661 100644 --- a/test/unit/test_utils.py +++ b/test/unit/test_utils.py @@ -20,8 +20,8 @@ import aws_encryption_sdk.identifiers import aws_encryption_sdk.internal.utils from aws_encryption_sdk.exceptions import InvalidDataKeyError, SerializationError, UnknownIdentityError -from aws_encryption_sdk.internal.defaults import MAX_FRAME_SIZE -from aws_encryption_sdk.internal.utils import verify_interface +from aws_encryption_sdk.internal.defaults import MAX_FRAME_SIZE, MESSAGE_ID_LENGTH +from aws_encryption_sdk.internal.utils import verify_ec_interface from aws_encryption_sdk.structures import DataKey, EncryptedDataKey, MasterKeyInfo, RawDataKey from .test_values import VALUES @@ -49,7 +49,7 @@ def test_verify_interface(patch_ec): mock_algorithm_info.return_value.key_size = True _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) - implemented = verify_interface(algorithm=_algorithm) + implemented = verify_ec_interface(algorithm=_algorithm) assert implemented is True From 25011d756b21a674f8ace29fb448da1f8bca52df Mon Sep 17 00:00:00 2001 From: Jose Corella Date: Tue, 14 Jun 2022 16:18:23 -0700 Subject: [PATCH 6/6] somehow a check was missed in local testing --- test/unit/test_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/test_utils.py b/test/unit/test_utils.py index 36d92a661..a3da319ad 100644 --- a/test/unit/test_utils.py +++ b/test/unit/test_utils.py @@ -20,7 +20,7 @@ import aws_encryption_sdk.identifiers import aws_encryption_sdk.internal.utils from aws_encryption_sdk.exceptions import InvalidDataKeyError, SerializationError, UnknownIdentityError -from aws_encryption_sdk.internal.defaults import MAX_FRAME_SIZE, MESSAGE_ID_LENGTH +from aws_encryption_sdk.internal.defaults import MAX_FRAME_SIZE from aws_encryption_sdk.internal.utils import verify_ec_interface from aws_encryption_sdk.structures import DataKey, EncryptedDataKey, MasterKeyInfo, RawDataKey @@ -42,7 +42,7 @@ def test_prep_stream_data_passthrough(): assert_prepped_stream_identity(test, io.BytesIO) -def test_verify_interface(patch_ec): +def test_verify_ec_interface(patch_ec): patch_ec.EllipticCurve.__abstractmethods__ = set(("key_size", "name")) mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) mock_algorithm_info.return_value.name = True