Skip to content

Commit d99b666

Browse files
more unit tests
1 parent c4ca658 commit d99b666

File tree

6 files changed

+401
-28
lines changed

6 files changed

+401
-28
lines changed

src/aws_encryption_sdk/streaming_client.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@
7373
try:
7474
# pylint should pass even if the MPL isn't installed
7575
# noqa pylint: disable=import-error
76-
from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders
76+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
7777
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
78+
from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException
7879
from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput
7980
from aws_cryptographic_materialproviders.mpl.references import IKeyring
8081
_HAS_MPL = True
@@ -147,9 +148,6 @@ def _has_mpl_attrs_post_init(self):
147148
"""If the MPL is present in the runtime, perform MPL-specific post-init logic
148149
to validate the new object has a valid state.
149150
"""
150-
if not hasattr(self, "keyring"):
151-
self._no_mpl_attrs_post_init()
152-
return
153151
if not exactly_one_arg_is_not_none(self.materials_manager, self.key_provider, self.keyring):
154152
raise TypeError("Exactly one of keyring, materials_manager, or key_provider must be provided")
155153
if self.materials_manager is None:
@@ -159,21 +157,21 @@ def _has_mpl_attrs_post_init(self):
159157
master_key_provider=self.key_provider
160158
)
161159
elif self.keyring is not None:
162-
# No CMM, provided MPL keyring => create MPL's DefaultCryptographicMaterialsManager
163-
if not isinstance(self.keyring, IKeyring):
164-
raise ValueError(f"Argument provided to keyring MUST be a {IKeyring}. \
165-
Found {self.keyring.__class__.__name__}")
166-
167-
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
168-
config=MaterialProvidersConfig()
169-
)
170-
cmm = mat_prov.create_default_cryptographic_materials_manager(
171-
CreateDefaultCryptographicMaterialsManagerInput(
172-
keyring=self.keyring
160+
try:
161+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
162+
config=MaterialProvidersConfig()
173163
)
174-
)
175-
cmm_handler: CryptoMaterialsManager = CryptoMaterialsManagerFromMPL(cmm)
176-
self.materials_manager = cmm_handler
164+
cmm = mat_prov.create_default_cryptographic_materials_manager(
165+
CreateDefaultCryptographicMaterialsManagerInput(
166+
keyring=self.keyring
167+
)
168+
)
169+
cmm_handler: CryptoMaterialsManager = CryptoMaterialsManagerFromMPL(cmm)
170+
self.materials_manager = cmm_handler
171+
except AwsCryptographicMaterialProvidersException as mpl_exception:
172+
# Wrap MPL error into the ESDK error type
173+
# so customers only have to catch ESDK error types.
174+
raise AWSEncryptionSDKClientError(mpl_exception)
177175

178176
def _no_mpl_attrs_post_init(self):
179177
"""If the MPL is NOT present in the runtime, perform post-init logic

test/unit/test_crypto_authentication_signer.py

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
# language governing permissions and limitations under the License.
1313
"""Unit test suite for ``aws_encryption_sdk.internal.crypto.authentication.Signer``."""
1414
import pytest
15-
from mock import MagicMock, sentinel
15+
from mock import MagicMock, sentinel, patch
16+
import cryptography.hazmat.primitives.serialization
1617
from pytest_mock import mocker # noqa pylint: disable=unused-import
1718

1819
import aws_encryption_sdk.internal.crypto.authentication
@@ -75,28 +76,72 @@ def test_f_signer_from_key_bytes():
7576
def test_f_signer_key_bytes():
7677
test = Signer(algorithm=ALGORITHM, key=VALUES["ecc_private_key_prime"])
7778
assert test.key_bytes() == VALUES["ecc_private_key_prime_private_bytes"]
79+
7880

81+
def test_GIVEN_no_encoding_WHEN_signer_from_key_bytes_THEN_load_der_private_key(
82+
patch_default_backend,
83+
patch_build_hasher,
84+
patch_ec
85+
):
86+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
87+
_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
7988

80-
def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec):
89+
# Make a new patched serialization module for this test.
90+
# The default patch introduces serialization as `serialization.Encoding.DER`
91+
# from within the src, but is `Encoding.DER` in the test.
92+
# This namespace change causes the src's `isinstance` checks to fail.
93+
# Mock the `serialization.Encoding.DER`
94+
with patch.object(cryptography.hazmat.primitives, "serialization"):
95+
# Mock the `serialization.load_der_private_key`
96+
with patch.object(aws_encryption_sdk.internal.crypto.authentication.serialization, "load_der_private_key") as mock_der:
97+
Signer.from_key_bytes(
98+
algorithm=_algorithm,
99+
key_bytes=sentinel.key_bytes,
100+
)
101+
102+
mock_der.assert_called_once_with(
103+
data=sentinel.key_bytes, password=None, backend=patch_default_backend.return_value
104+
)
105+
106+
107+
def test_GIVEN_PEM_encoding_WHEN_signer_from_key_bytes_THEN_load_pem_private_key(
108+
patch_default_backend,
109+
patch_serialization,
110+
patch_build_hasher,
111+
patch_ec
112+
):
81113
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
82114
_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
83115

84-
# Explicitly pass in patched serialization module.
85-
# Patching the module introduces namespace issues
86-
# which causes the method's `isinstance` checks to fail
87-
# by changing the namespace from `serialization.Encoding.DER` to `Encoding.DER`.
88116
signer = Signer.from_key_bytes(
89117
algorithm=_algorithm,
90118
key_bytes=sentinel.key_bytes,
91-
encoding=patch_serialization.Encoding.DER
119+
encoding=patch_serialization.Encoding.PEM
92120
)
93121

94-
patch_serialization.load_der_private_key.assert_called_once_with(
122+
patch_serialization.load_pem_private_key.assert_called_once_with(
95123
data=sentinel.key_bytes, password=None, backend=patch_default_backend.return_value
96124
)
97125
assert isinstance(signer, Signer)
98126
assert signer.algorithm is _algorithm
99-
assert signer.key is patch_serialization.load_der_private_key.return_value
127+
assert signer.key is patch_serialization.load_pem_private_key.return_value
128+
129+
130+
def test_GIVEN_unrecognized_encoding_WHEN_signer_from_key_bytes_THEN_raise_ValueError(
131+
patch_default_backend,
132+
patch_serialization,
133+
patch_build_hasher,
134+
patch_ec
135+
):
136+
mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve)
137+
_algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info)
138+
139+
with pytest.raises(ValueError):
140+
signer = Signer.from_key_bytes(
141+
algorithm=_algorithm,
142+
key_bytes=sentinel.key_bytes,
143+
encoding="not an encoding"
144+
)
100145

101146

102147
def test_signer_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec):

test/unit/test_streaming_client_configs.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import pytest
1717
import six
18+
from mock import patch
1819

1920
from aws_encryption_sdk import CommitmentPolicy
2021
from aws_encryption_sdk.internal.defaults import ALGORITHM, FRAME_LENGTH, LINE_LENGTH
@@ -28,6 +29,22 @@
2829
pytestmark = [pytest.mark.unit, pytest.mark.local]
2930

3031

32+
# Check if MPL is installed, and skip tests based on its installation status
33+
# Ideally, this logic would be based on mocking imports and testing logic,
34+
# but doing that introduces errors that cause other tests to fail.
35+
try:
36+
from aws_cryptographic_materialproviders.mpl.references import (
37+
IKeyring,
38+
)
39+
HAS_MPL = True
40+
41+
from aws_encryption_sdk.materials_managers.mpl.cmm import (
42+
CryptoMaterialsManagerFromMPL,
43+
)
44+
except ImportError:
45+
HAS_MPL = False
46+
47+
3148
class FakeCryptoMaterialsManager(CryptoMaterialsManager):
3249
def get_encryption_materials(self, request):
3350
return
@@ -42,6 +59,14 @@ class FakeMasterKeyProvider(MasterKeyProvider):
4259

4360
def _new_master_key(self, key_id):
4461
return
62+
63+
if HAS_MPL:
64+
class FakeKeyring(IKeyring):
65+
def on_encrypt(self, param):
66+
return
67+
68+
def on_decrypt(self, param):
69+
return
4570

4671

4772
BASE_KWARGS = dict(
@@ -126,6 +151,18 @@ def test_client_config_defaults():
126151
assert test.max_encrypted_data_keys is None
127152

128153

154+
@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation")
155+
def test_client_config_with_mpl_attr():
156+
test = _ClientConfig(**BASE_KWARGS)
157+
assert hasattr(test, "keyring")
158+
159+
160+
@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation")
161+
def test_client_config_no_mpl():
162+
test = _ClientConfig(**BASE_KWARGS)
163+
assert not hasattr(test, "keyring")
164+
165+
129166
def test_encryptor_config_defaults():
130167
test = EncryptorConfig(**BASE_KWARGS)
131168
assert test.encryption_context == {}
@@ -154,3 +191,62 @@ def test_client_config_converts(kwargs, stream_type):
154191
assert isinstance(test.source, stream_type)
155192
if test.key_provider is not None:
156193
assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager)
194+
195+
196+
@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation")
197+
@patch.object(_ClientConfig, "_no_mpl_attrs_post_init")
198+
def test_GIVEN_no_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method(
199+
mock_no_mpl_attrs_post_init,
200+
):
201+
_ClientConfig(**BASE_KWARGS)
202+
mock_no_mpl_attrs_post_init.assert_called_once_with()
203+
204+
205+
@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation")
206+
@patch.object(_ClientConfig, "_has_mpl_attrs_post_init")
207+
def test_GIVEN_has_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method(
208+
_has_mpl_attrs_post_init,
209+
):
210+
_ClientConfig(**BASE_KWARGS)
211+
_has_mpl_attrs_post_init.assert_called_once_with()
212+
213+
214+
@pytest.mark.parametrize(
215+
"kwargs, stream_type",
216+
(
217+
(dict(source=b"", materials_manager=FakeCryptoMaterialsManager()), io.BytesIO),
218+
(dict(source=b"", key_provider=FakeMasterKeyProvider()), io.BytesIO),
219+
(dict(source="", materials_manager=FakeCryptoMaterialsManager()), io.BytesIO),
220+
(dict(source=io.BytesIO(), materials_manager=FakeCryptoMaterialsManager()), io.BytesIO),
221+
(dict(source=six.StringIO(), materials_manager=FakeCryptoMaterialsManager()), six.StringIO),
222+
(dict(source=b"", keyring=FakeKeyring()), io.BytesIO),
223+
),
224+
)
225+
@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation")
226+
def test_client_configs_with_mpl(
227+
kwargs,
228+
stream_type
229+
):
230+
kwargs["commitment_policy"] = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
231+
232+
test = _ClientConfig(**kwargs)
233+
234+
# In all cases, config should have a materials manager
235+
assert test.materials_manager is not None
236+
237+
# If materials manager was provided, it should be directly used
238+
if hasattr(kwargs, "materials_manager"):
239+
assert kwargs["materials_manager"] == test.materials_manager
240+
241+
# If MPL keyring was provided, it should be wrapped in MPL materials manager
242+
if hasattr(kwargs, "keyring"):
243+
assert test.keyring is not None
244+
assert test.keyring == kwargs["keyring"]
245+
assert isinstance(test.keyring, IKeyring)
246+
assert isinstance(test.materials_manager, CryptoMaterialsManagerFromMPL)
247+
248+
# If native key_provider was provided, it should be wrapped in native materials manager
249+
if hasattr(kwargs, "key_provider"):
250+
assert test.key_provider is not None
251+
assert test.key_provider == kwargs["key_provider"]
252+
assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager)

0 commit comments

Comments
 (0)