From c0a51267ddcebff7908d280432096d6f3e77074c Mon Sep 17 00:00:00 2001 From: Jose Corella Date: Thu, 23 Apr 2026 09:30:32 -0700 Subject: [PATCH 1/3] fix: use issubclass instead of isinstance in _set_signature_type --- .../internal/crypto/authentication.py | 8 +- .../unit/test_crypto_authentication_signer.py | 98 ++++++++++++++++--- .../test_crypto_authentication_verifier.py | 17 ++-- .../test_crypto_prehashing_authenticator.py | 12 ++- 4 files changed, 105 insertions(+), 30 deletions(-) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index abfa01928..3b734a4da 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -36,10 +36,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.""" - if not isinstance(self.algorithm.signing_algorithm_info, type(ec.EllipticCurve)): - raise NotSupportedError("Unsupported signing algorithm info") - return ec.EllipticCurve - + try: + if not issubclass(self.algorithm.signing_algorithm_info, ec.EllipticCurve): + raise NotSupportedError("Unsupported signing algorithm info") + except TypeError: def _build_hasher(self): """Builds the hasher instance which will calculate the digest of all passed data. diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index 6ecedf404..c3520ba82 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -1,8 +1,10 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto.authentication.Signer``.""" +import cryptography.hazmat.primitives.serialization import pytest -from mock import MagicMock, sentinel +from cryptography.hazmat.primitives.asymmetric import ec +from mock import MagicMock, patch, sentinel from pytest_mock import mocker # noqa pylint: disable=unused-import import aws_encryption_sdk.internal.crypto.authentication @@ -67,23 +69,87 @@ 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, patch_ec): - 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) +def test_GIVEN_no_encoding_WHEN_signer_from_key_bytes_THEN_load_der_private_key( + patch_default_backend, + patch_build_hasher, + patch_ec +): + patch_ec.EllipticCurve = ec.EllipticCurve + _algorithm = MagicMock(signing_algorithm_info=ec.SECP256R1) + + # Make a new patched serialization module for this test. + # The default patch introduces serialization as `serialization.Encoding.DER` + # from within the src, but is `Encoding.DER` in the test. + # This namespace change causes the src's `isinstance` checks to fail. + # Mock the `serialization.Encoding.DER` + with patch.object(cryptography.hazmat.primitives, "serialization"): + # Mock the `serialization.load_der_private_key` + with patch.object( + aws_encryption_sdk.internal.crypto.authentication.serialization, + "load_der_private_key" + ) as mock_der: + # When: from_key_bytes + Signer.from_key_bytes( + algorithm=_algorithm, + key_bytes=sentinel.key_bytes, + # Given: No encoding provided => default arg + ) + + # Then: calls load_der_private_key + mock_der.assert_called_once_with( + data=sentinel.key_bytes, password=None, backend=patch_default_backend.return_value + ) + + +def test_GIVEN_PEM_encoding_WHEN_signer_from_key_bytes_THEN_load_pem_private_key( + patch_default_backend, + patch_serialization, + patch_build_hasher, + patch_ec +): + patch_ec.EllipticCurve = ec.EllipticCurve + _algorithm = MagicMock(signing_algorithm_info=ec.SECP256R1) + + # When: from_key_bytes + signer = Signer.from_key_bytes( + algorithm=_algorithm, + key_bytes=sentinel.key_bytes, + # Given: PEM encoding + encoding=patch_serialization.Encoding.PEM + ) - patch_serialization.load_der_private_key.assert_called_once_with( + # Then: calls load_pem_private_key + patch_serialization.load_pem_private_key.assert_called_once_with( data=sentinel.key_bytes, password=None, backend=patch_default_backend.return_value ) assert isinstance(signer, Signer) assert signer.algorithm is _algorithm - assert signer.key is patch_serialization.load_der_private_key.return_value + assert signer.key is patch_serialization.load_pem_private_key.return_value + + +def test_GIVEN_unrecognized_encoding_WHEN_signer_from_key_bytes_THEN_raise_ValueError( + patch_default_backend, + patch_serialization, + patch_build_hasher, + patch_ec +): + patch_ec.EllipticCurve = ec.EllipticCurve + _algorithm = MagicMock(signing_algorithm_info=ec.SECP256R1) + + # Then: Raises ValueError + with pytest.raises(ValueError): + # When: from_key_bytes + Signer.from_key_bytes( + algorithm=_algorithm, + key_bytes=sentinel.key_bytes, + # Given: Invalid encoding + encoding="not an encoding" + ) def test_signer_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) - algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + patch_ec.EllipticCurve = ec.EllipticCurve + algorithm = MagicMock(signing_algorithm_info=ec.SECP256R1) private_key = MagicMock() signer = Signer(algorithm, key=private_key) @@ -109,8 +175,8 @@ def test_signer_encoded_public_key( patch_base64.b64encode.return_value = sentinel.encoded_point private_key = MagicMock() - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) - algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + patch_ec.EllipticCurve = ec.EllipticCurve + algorithm = MagicMock(signing_algorithm_info=ec.SECP256R1) signer = Signer(algorithm, key=private_key) test_key = signer.encoded_public_key() @@ -121,8 +187,8 @@ def test_signer_encoded_public_key( def test_signer_update(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) - algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + patch_ec.EllipticCurve = ec.EllipticCurve + algorithm = MagicMock(signing_algorithm_info=ec.SECP256R1) signer = Signer(algorithm, key=MagicMock()) signer.update(sentinel.data) patch_build_hasher.return_value.update.assert_called_once_with(sentinel.data) @@ -131,8 +197,8 @@ def test_signer_update(patch_default_backend, patch_serialization, patch_build_h def test_signer_finalize( patch_default_backend, patch_serialization, patch_build_hasher, patch_ecc_static_length_signature, patch_ec ): - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) - algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + patch_ec.EllipticCurve = ec.EllipticCurve + algorithm = MagicMock(signing_algorithm_info=ec.SECP256R1) 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 7562fa762..4b29aa3ab 100644 --- a/test/unit/test_crypto_authentication_verifier.py +++ b/test/unit/test_crypto_authentication_verifier.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto.authentication.Verifier``.""" import pytest +from cryptography.hazmat.primitives.asymmetric import ec from mock import MagicMock, sentinel from pytest_mock import mocker # noqa pylint: disable=unused-import @@ -85,23 +86,23 @@ def test_verifier_from_encoded_point( 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 - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) - mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + mock_algorithm = MagicMock(signing_algorithm_info=ec.SECP256R1) + patch_ec.EllipticCurve = ec.EllipticCurve verifier = Verifier.from_encoded_point(algorithm=mock_algorithm, encoded_point=sentinel.encoded_point) patch_base64.b64decode.assert_called_once_with(sentinel.encoded_point) - mock_algorithm.signing_algorithm_info.assert_called_once_with() - patch_ecc_public_numbers_from_compressed_point.assert_called_once_with( - curve=mock_algorithm.signing_algorithm_info.return_value, compressed_point=sentinel.compressed_point - ) + patch_ecc_public_numbers_from_compressed_point.assert_called_once() + call_kwargs = patch_ecc_public_numbers_from_compressed_point.call_args + assert isinstance(call_kwargs[1]["curve"], ec.SECP256R1) + assert call_kwargs[1]["compressed_point"] is sentinel.compressed_point mock_point_instance.public_key.assert_called_once_with(patch_default_backend.return_value) assert isinstance(verifier, Verifier) def test_verifier_update(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) - mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + patch_ec.EllipticCurve = ec.EllipticCurve + mock_algorithm = MagicMock(signing_algorithm_info=ec.SECP256R1) verifier = Verifier(algorithm=mock_algorithm, key=MagicMock()) verifier.update(sentinel.data) verifier._hasher.update.assert_called_once_with(sentinel.data) diff --git a/test/unit/test_crypto_prehashing_authenticator.py b/test/unit/test_crypto_prehashing_authenticator.py index 631391f53..79deb158d 100644 --- a/test/unit/test_crypto_prehashing_authenticator.py +++ b/test/unit/test_crypto_prehashing_authenticator.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto._PrehashingAuthenticater``.""" import pytest +from cryptography.hazmat.primitives.asymmetric import ec from mock import MagicMock, sentinel from pytest_mock import mocker # noqa pylint: disable=unused-import @@ -56,13 +57,20 @@ def test_init(patch_set_signature_type, patch_build_hasher): def test_set_signature_type_elliptic_curve( patch_build_hasher, patch_cryptography_ec ): - mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_cryptography_ec.EllipticCurve) - mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + patch_cryptography_ec.EllipticCurve = ec.EllipticCurve + mock_algorithm = MagicMock(signing_algorithm_info=ec.SECP256R1) test = _PrehashingAuthenticator(algorithm=mock_algorithm, key=sentinel.key) assert test._signature_type is patch_cryptography_ec.EllipticCurve +def test_set_signature_type_elliptic_curve_known_value(patch_build_hasher): + mock_algorithm = MagicMock(signing_algorithm_info=ec.SECP384R1) + test = _PrehashingAuthenticator(algorithm=mock_algorithm, key=sentinel.key) + + assert test._signature_type is ec.EllipticCurve + + def test_set_signature_type_unknown( patch_build_hasher, patch_cryptography_ec ): From d278075cb53fd4f8295f1f32f15360488a2b75eb Mon Sep 17 00:00:00 2001 From: Jose Corella Date: Thu, 23 Apr 2026 09:34:32 -0700 Subject: [PATCH 2/3] fix --- src/aws_encryption_sdk/internal/crypto/authentication.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index 3b734a4da..01b1e9287 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -40,6 +40,8 @@ def _set_signature_type(self): if not issubclass(self.algorithm.signing_algorithm_info, ec.EllipticCurve): raise NotSupportedError("Unsupported signing algorithm info") except TypeError: + raise NotSupportedError("Unsupported signing algorithm info") + def _build_hasher(self): """Builds the hasher instance which will calculate the digest of all passed data. From fb5b7ea8151d559a855ee7c5225c04ea6386a7f6 Mon Sep 17 00:00:00 2001 From: Jose Corella Date: Thu, 23 Apr 2026 09:39:16 -0700 Subject: [PATCH 3/3] m --- src/aws_encryption_sdk/internal/crypto/authentication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index 01b1e9287..035adea88 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -41,7 +41,7 @@ def _set_signature_type(self): raise NotSupportedError("Unsupported signing algorithm info") except TypeError: raise NotSupportedError("Unsupported signing algorithm info") - + def _build_hasher(self): """Builds the hasher instance which will calculate the digest of all passed data.