Skip to content

Commit ff13d9c

Browse files
authored
refactor: verify_interface -> verify_ec_interface (#470)
1 parent c5cf054 commit ff13d9c

12 files changed

+137
-92
lines changed
+6-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
bandit==1.7.0
2-
black==21.12b0
1+
bandit==1.7.4
2+
black==22.3.0
33
doc8==0.10.1
44
flake8==4.0.1
5-
flake8-bugbear==21.11.29
5+
flake8-bugbear==22.1.11
66
flake8-docstrings==1.6.0
77
flake8-print==4.0.0
88
isort==5.10.1
99
pyflakes==2.4.0
10-
pylint==2.12.2
11-
readme_renderer==32.0
10+
pylint==2.13.5
11+
readme_renderer==34.0
1212
seed-isort-config==2.2.0
13-
vulture==2.3
14-
sphinx==4.4.0
13+
vulture==2.3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pypi-parker==0.1.2
2+
setuptools==62.0.0
3+
twine==3.8.0
4+
wheel==0.37.1

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
boto3>=1.10.0
2-
cryptography>=2.5.0
2+
cryptography>=2.5.0,<=3.3.2
33
attrs>=17.4.0
44
wrapt>=1.10.11

src/aws_encryption_sdk/internal/crypto/authentication.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
from cryptography.hazmat.primitives import hashes, serialization
1919
from cryptography.hazmat.primitives.asymmetric import ec
2020
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
21-
from cryptography.utils import InterfaceNotImplemented, verify_interface
21+
22+
from aws_encryption_sdk.internal.utils import verify_ec_interface
2223

2324
from ...exceptions import NotSupportedError
2425
from .elliptic_curve import (
@@ -47,11 +48,9 @@ def __init__(self, algorithm, key):
4748

4849
def _set_signature_type(self):
4950
"""Ensures that the algorithm signature type is a known type and sets a reference value."""
50-
try:
51-
verify_interface(ec.EllipticCurve, self.algorithm.signing_algorithm_info)
52-
return ec.EllipticCurve
53-
except InterfaceNotImplemented:
51+
if not verify_ec_interface(self.algorithm):
5452
raise NotSupportedError("Unsupported signing algorithm info")
53+
return ec.EllipticCurve
5554

5655
def _build_hasher(self):
5756
"""Builds the hasher instance which will calculate the digest of all passed data.

src/aws_encryption_sdk/internal/crypto/elliptic_curve.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
from cryptography.hazmat.backends import default_backend
1919
from cryptography.hazmat.primitives.asymmetric import ec
2020
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed, decode_dss_signature, encode_dss_signature
21-
from cryptography.utils import InterfaceNotImplemented, int_from_bytes, int_to_bytes, verify_interface
21+
from cryptography.utils import int_from_bytes, int_to_bytes
22+
23+
from aws_encryption_sdk.internal.utils import verify_ec_interface
2224

2325
from ...exceptions import NotSupportedError
2426
from ..str_ops import to_bytes
@@ -183,8 +185,6 @@ def generate_ecc_signing_key(algorithm):
183185
:returns: Generated signing key
184186
:raises NotSupportedError: if signing algorithm is not supported on this platform
185187
"""
186-
try:
187-
verify_interface(ec.EllipticCurve, algorithm.signing_algorithm_info)
188-
return ec.generate_private_key(curve=algorithm.signing_algorithm_info(), backend=default_backend())
189-
except InterfaceNotImplemented:
188+
if not verify_ec_interface(algorithm):
190189
raise NotSupportedError("Unsupported signing algorithm info")
190+
return ec.generate_private_key(curve=algorithm.signing_algorithm_info(), backend=default_backend())

src/aws_encryption_sdk/internal/utils/__init__.py

+15
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import os
1717

1818
import six
19+
from cryptography.hazmat.primitives.asymmetric import ec
1920

2021
import aws_encryption_sdk.internal.defaults
2122
from aws_encryption_sdk.exceptions import InvalidDataKeyError, SerializationError, UnknownIdentityError
@@ -163,3 +164,17 @@ def source_data_key_length_check(source_data_key, algorithm):
163164
actual=len(source_data_key.data_key), required=algorithm.kdf_input_len
164165
)
165166
)
167+
168+
169+
def verify_ec_interface(algorithm):
170+
"""Validates that the provided EllipticCurve Algorithm is correctly implemented by
171+
checking if it implements both of the abstract methods from the EllipticCurve
172+
abstract class.
173+
174+
:param algorithm: Algorithm object which directs which Elliptic Curve will be used
175+
:type algorithm: aws_encryption_sdk.identifiers.Algorithm
176+
"""
177+
for method in ec.EllipticCurve.__abstractmethods__:
178+
if not hasattr(algorithm.signing_algorithm_info(), method):
179+
return False
180+
return True

test/unit/test_crypto_authentication_signer.py

+39-10
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ def patch_default_backend(mocker):
3030
yield aws_encryption_sdk.internal.crypto.authentication.default_backend
3131

3232

33+
@pytest.fixture
34+
def patch_ec(mocker):
35+
mocker.patch.object(aws_encryption_sdk.internal.crypto.authentication, "ec")
36+
yield aws_encryption_sdk.internal.crypto.authentication.ec
37+
38+
3339
@pytest.fixture
3440
def patch_serialization(mocker):
3541
mocker.patch.object(aws_encryption_sdk.internal.crypto.authentication, "serialization")
@@ -71,8 +77,13 @@ def test_f_signer_key_bytes():
7177
assert test.key_bytes() == VALUES["ecc_private_key_prime_private_bytes"]
7278

7379

74-
def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher):
75-
_algorithm = MagicMock()
80+
def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec):
81+
patch_ec.EllipticCurve.__abstractmethods__ = set()
82+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
83+
mock_algorithm_info.return_value.name = True
84+
mock_algorithm_info.return_value.key_size = True
85+
_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
86+
7687
signer = Signer.from_key_bytes(algorithm=_algorithm, key_bytes=sentinel.key_bytes)
7788

7889
patch_serialization.load_der_private_key.assert_called_once_with(
@@ -83,9 +94,12 @@ def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch
8394
assert signer.key is patch_serialization.load_der_private_key.return_value
8495

8596

86-
def test_signer_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher):
97+
def test_signer_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec):
98+
patch_ec.EllipticCurve.__abstractmethods__ = set()
99+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
100+
algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
87101
private_key = MagicMock()
88-
signer = Signer(MagicMock(), key=private_key)
102+
signer = Signer(algorithm, key=private_key)
89103

90104
test = signer.key_bytes()
91105

@@ -98,30 +112,45 @@ def test_signer_key_bytes(patch_default_backend, patch_serialization, patch_buil
98112

99113

100114
def test_signer_encoded_public_key(
101-
patch_default_backend, patch_serialization, patch_build_hasher, patch_ecc_encode_compressed_point, patch_base64
115+
patch_default_backend,
116+
patch_serialization,
117+
patch_build_hasher,
118+
patch_ecc_encode_compressed_point,
119+
patch_base64,
120+
patch_ec
102121
):
103122
patch_ecc_encode_compressed_point.return_value = sentinel.compressed_point
104123
patch_base64.b64encode.return_value = sentinel.encoded_point
105124
private_key = MagicMock()
106125

107-
signer = Signer(MagicMock(), key=private_key)
126+
patch_ec.EllipticCurve.__abstractmethods__ = set()
127+
128+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
129+
algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
130+
131+
signer = Signer(algorithm, key=private_key)
108132
test_key = signer.encoded_public_key()
109133

110134
patch_ecc_encode_compressed_point.assert_called_once_with(private_key)
111135
patch_base64.b64encode.assert_called_once_with(sentinel.compressed_point)
112136
assert test_key == sentinel.encoded_point
113137

114138

115-
def test_signer_update(patch_default_backend, patch_serialization, patch_build_hasher):
116-
signer = Signer(MagicMock(), key=MagicMock())
139+
def test_signer_update(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec):
140+
patch_ec.EllipticCurve.__abstractmethods__ = set()
141+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
142+
algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
143+
signer = Signer(algorithm, key=MagicMock())
117144
signer.update(sentinel.data)
118145
patch_build_hasher.return_value.update.assert_called_once_with(sentinel.data)
119146

120147

121148
def test_signer_finalize(
122-
patch_default_backend, patch_serialization, patch_build_hasher, patch_ecc_static_length_signature
149+
patch_default_backend, patch_serialization, patch_build_hasher, patch_ecc_static_length_signature, patch_ec
123150
):
124-
algorithm = MagicMock()
151+
patch_ec.EllipticCurve.__abstractmethods__ = set()
152+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
153+
algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
125154
private_key = MagicMock()
126155

127156
signer = Signer(algorithm, key=private_key)

test/unit/test_crypto_authentication_verifier.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,22 @@ def test_verifier_from_encoded_point(
8989
patch_ecc_public_numbers_from_compressed_point,
9090
patch_base64,
9191
patch_build_hasher,
92+
patch_ec
9293
):
9394
mock_point_instance = MagicMock()
9495
mock_point_instance.public_key.return_value = sentinel.public_key
9596
patch_ecc_public_numbers_from_compressed_point.return_value = mock_point_instance
9697
patch_base64.b64decode.return_value = sentinel.compressed_point
97-
algorithm = MagicMock()
98+
99+
patch_ec.EllipticCurve.__abstractmethods__ = set()
100+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
101+
mock_algorithm_info.return_value.name = True
102+
mock_algorithm_info.return_value.key_size = True
103+
algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
98104

99105
verifier = Verifier.from_encoded_point(algorithm=algorithm, encoded_point=sentinel.encoded_point)
100106

101107
patch_base64.b64decode.assert_called_once_with(sentinel.encoded_point)
102-
algorithm.signing_algorithm_info.assert_called_once_with()
103108
patch_ecc_public_numbers_from_compressed_point.assert_called_once_with(
104109
curve=algorithm.signing_algorithm_info.return_value, compressed_point=sentinel.compressed_point
105110
)

test/unit/test_crypto_elliptic_curve.py

+11-13
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515

1616
import pytest
1717
from cryptography.hazmat.primitives.asymmetric import ec
18-
from cryptography.utils import InterfaceNotImplemented
1918
from mock import MagicMock, sentinel
2019
from pytest_mock import mocker # noqa pylint: disable=unused-import
2120

2221
import aws_encryption_sdk.internal.crypto.elliptic_curve
22+
import aws_encryption_sdk.internal.defaults
2323
from aws_encryption_sdk.exceptions import NotSupportedError
2424
from aws_encryption_sdk.internal.crypto.elliptic_curve import (
2525
_ECC_CURVE_PARAMETERS,
@@ -72,12 +72,6 @@ def patch_ecc_decode_compressed_point(mocker):
7272
yield aws_encryption_sdk.internal.crypto.elliptic_curve._ecc_decode_compressed_point
7373

7474

75-
@pytest.fixture
76-
def patch_verify_interface(mocker):
77-
mocker.patch.object(aws_encryption_sdk.internal.crypto.elliptic_curve, "verify_interface")
78-
yield aws_encryption_sdk.internal.crypto.elliptic_curve.verify_interface
79-
80-
8175
@pytest.fixture
8276
def patch_ecc_curve_parameters(mocker):
8377
mocker.patch.object(aws_encryption_sdk.internal.crypto.elliptic_curve, "_ECC_CURVE_PARAMETERS")
@@ -374,23 +368,27 @@ def test_ecc_public_numbers_from_compressed_point(patch_ec, patch_ecc_decode_com
374368
assert test == sentinel.public_numbers_instance
375369

376370

377-
def test_generate_ecc_signing_key_supported(patch_default_backend, patch_ec, patch_verify_interface):
371+
def test_generate_ecc_signing_key_supported(patch_default_backend, patch_ec):
378372
patch_ec.generate_private_key.return_value = sentinel.raw_signing_key
379-
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info)
373+
patch_ec.EllipticCurve.__abstractmethods__ = {"key_size", "name"}
374+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
375+
mock_algorithm_info.return_value.name = MagicMock(return_value=True)
376+
mock_algorithm_info.return_value.key_size = MagicMock(return_value=True)
380377
mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
381378

382379
test_signing_key = generate_ecc_signing_key(algorithm=mock_algorithm)
383380

384-
patch_verify_interface.assert_called_once_with(patch_ec.EllipticCurve, mock_algorithm_info)
385381
patch_ec.generate_private_key.assert_called_once_with(
386382
curve=sentinel.algorithm_info, backend=patch_default_backend.return_value
387383
)
388384
assert test_signing_key is sentinel.raw_signing_key
389385

390386

391-
def test_generate_ecc_signing_key_unsupported(patch_default_backend, patch_ec, patch_verify_interface):
392-
patch_verify_interface.side_effect = InterfaceNotImplemented
393-
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info)
387+
def test_generate_ecc_signing_key_unsupported(patch_default_backend, patch_ec):
388+
patch_ec.generate_private_key.return_value = sentinel.raw_signing_key
389+
mock_algorithm_info = MagicMock(return_value=sentinel.invalid_algorithm_info, spec=patch_ec.EllipticCurve)
390+
mock_algorithm_info.return_value.not_name = MagicMock(return_value=True)
391+
mock_algorithm_info.return_value.not_key_size = MagicMock(return_value=True)
394392
mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
395393

396394
with pytest.raises(NotSupportedError) as excinfo:

test/unit/test_crypto_prehashing_authenticator.py

+13-15
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
# language governing permissions and limitations under the License.
1313
"""Unit test suite for ``aws_encryption_sdk.internal.crypto._PrehashingAuthenticater``."""
1414
import pytest
15-
from cryptography.utils import InterfaceNotImplemented
1615
from mock import MagicMock, sentinel
1716
from pytest_mock import mocker # noqa pylint: disable=unused-import
1817

@@ -35,12 +34,6 @@ def patch_build_hasher(mocker):
3534
yield _PrehashingAuthenticator._build_hasher
3635

3736

38-
@pytest.fixture
39-
def patch_cryptography_utils_verify_interface(mocker):
40-
mocker.patch.object(aws_encryption_sdk.internal.crypto.authentication, "verify_interface")
41-
yield aws_encryption_sdk.internal.crypto.authentication.verify_interface
42-
43-
4437
@pytest.fixture
4538
def patch_cryptography_ec(mocker):
4639
mocker.patch.object(aws_encryption_sdk.internal.crypto.authentication, "ec")
@@ -71,23 +64,28 @@ def test_init(patch_set_signature_type, patch_build_hasher):
7164

7265

7366
def test_set_signature_type_elliptic_curve(
74-
patch_build_hasher, patch_cryptography_utils_verify_interface, patch_cryptography_ec
67+
patch_build_hasher, patch_cryptography_ec
7568
):
76-
mock_algorithm = MagicMock()
69+
patch_cryptography_ec.generate_private_key.return_value = sentinel.raw_signing_key
70+
patch_cryptography_ec.EllipticCurve.__abstractmethods__ = set()
71+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_cryptography_ec.EllipticCurve)
72+
mock_algorithm_info.return_value.name = MagicMock(return_value=True)
73+
mock_algorithm_info.return_value.key_size = MagicMock(return_value=True)
74+
mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
7775
test = _PrehashingAuthenticator(algorithm=mock_algorithm, key=sentinel.key)
7876

79-
patch_cryptography_utils_verify_interface.assert_called_once_with(
80-
patch_cryptography_ec.EllipticCurve, mock_algorithm.signing_algorithm_info
81-
)
8277
assert test._signature_type is patch_cryptography_ec.EllipticCurve
8378

8479

8580
def test_set_signature_type_unknown(
86-
patch_build_hasher, patch_cryptography_utils_verify_interface, patch_cryptography_ec
81+
patch_build_hasher, patch_cryptography_ec
8782
):
88-
patch_cryptography_utils_verify_interface.side_effect = InterfaceNotImplemented
83+
mock_algorithm_info = MagicMock(return_value=sentinel.not_algorithm_info, spec=patch_cryptography_ec.EllipticCurve)
84+
mock_algorithm_info.return_value.not_name = MagicMock(return_value=True)
85+
mock_algorithm_info.return_value.not_key_size = MagicMock(return_value=True)
86+
mock_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
8987
with pytest.raises(NotSupportedError) as excinfo:
90-
_PrehashingAuthenticator(algorithm=MagicMock(), key=sentinel.key)
88+
_PrehashingAuthenticator(algorithm=mock_algorithm, key=sentinel.key)
9189

9290
excinfo.match(r"Unsupported signing algorithm info")
9391

test/unit/test_utils.py

+19
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import aws_encryption_sdk.internal.utils
2222
from aws_encryption_sdk.exceptions import InvalidDataKeyError, SerializationError, UnknownIdentityError
2323
from aws_encryption_sdk.internal.defaults import MAX_FRAME_SIZE
24+
from aws_encryption_sdk.internal.utils import verify_ec_interface
2425
from aws_encryption_sdk.structures import DataKey, EncryptedDataKey, MasterKeyInfo, RawDataKey
2526

2627
from .test_values import VALUES
@@ -29,12 +30,30 @@
2930
pytestmark = [pytest.mark.unit, pytest.mark.local]
3031

3132

33+
@pytest.yield_fixture
34+
def patch_ec(mocker):
35+
mocker.patch.object(aws_encryption_sdk.internal.crypto.authentication, "ec")
36+
yield aws_encryption_sdk.internal.crypto.authentication.ec
37+
38+
3239
def test_prep_stream_data_passthrough():
3340
test = aws_encryption_sdk.internal.utils.prep_stream_data(io.BytesIO(b"some data"))
3441

3542
assert_prepped_stream_identity(test, io.BytesIO)
3643

3744

45+
def test_verify_ec_interface(patch_ec):
46+
patch_ec.EllipticCurve.__abstractmethods__ = set(("key_size", "name"))
47+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
48+
mock_algorithm_info.return_value.name = True
49+
mock_algorithm_info.return_value.key_size = True
50+
_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
51+
52+
implemented = verify_ec_interface(algorithm=_algorithm)
53+
54+
assert implemented is True
55+
56+
3857
@pytest.mark.parametrize("source", (u"some unicode data ловие", b"\x00\x01\x02"))
3958
def test_prep_stream_data_wrap(source):
4059
test = aws_encryption_sdk.internal.utils.prep_stream_data(source)

0 commit comments

Comments
 (0)