From 7360edd46964ae0a0d7127a1cf51bd180973af33 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 31 Jan 2024 15:18:21 -0800 Subject: [PATCH 001/172] passing hierarchy keyring example --- examples/src/basic_encryption.py | 98 +++++++++++++++++-- requirements.txt | 2 +- setup.py | 8 ++ .../internal/crypto/authentication.py | 8 +- src/aws_encryption_sdk/streaming_client.py | 83 +++++++++++++--- 5 files changed, 179 insertions(+), 20 deletions(-) diff --git a/examples/src/basic_encryption.py b/examples/src/basic_encryption.py index cfe8ac791..f48f7e4a1 100644 --- a/examples/src/basic_encryption.py +++ b/examples/src/basic_encryption.py @@ -13,7 +13,40 @@ """Example showing basic encryption and decryption of a value already in memory.""" import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy +import aws_cryptographic_materialproviders +import boto3 +from aws_encryption_sdk.cmm_handler import (CMMHandler) + +import sys + +module_root_dir = '/'.join(__file__.split("/")[:-1]) + +sys.path.append(module_root_dir) + +import aws_cryptographic_materialproviders + +from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.client import AwsCryptographicMaterialProviders +from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.config import MaterialProvidersConfig +from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.models import ( + CreateAwsKmsHierarchicalKeyringInput, + CacheTypeDefault, + DefaultCache, + GetBranchKeyIdInput, + GetBranchKeyIdOutput, + CreateDefaultCryptographicMaterialsManagerInput, +) +from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references import ( + IKeyring, + IBranchKeyIdSupplier, +) + +from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_keystore.client import KeyStore +from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_keystore.config import KeyStoreConfig +from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_keystore.models import ( + CreateKeyInput, + KMSConfigurationKmsKeyArn, +) def cycle_string(key_arn, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under a KMS customer master key (CMK). @@ -25,23 +58,72 @@ def cycle_string(key_arn, source_plaintext, botocore_session=None): """ # Set up an encryption client with an explicit commitment policy. Note that if you do not explicitly choose a # commitment policy, REQUIRE_ENCRYPT_REQUIRE_DECRYPT is used by default. - client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + client = aws_encryption_sdk.EncryptionSDKClient() # Create a KMS master key provider. Note that because we are planning on decrypting using this same provider, # we MUST provide the ARN of the KMS Key. If we provide a raw key id or a key alias, decryption will fail. kms_kwargs = dict(key_ids=[key_arn]) if botocore_session is not None: kms_kwargs["botocore_session"] = botocore_session - master_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**kms_kwargs) + # master_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**kms_kwargs) + + ##### + + + key_store_table_name="KeyStoreDdbTable" + logical_key_store_name="KeyStoreDdbTable" + keystore_kms_key_id="arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" + + ddb_client = boto3.client('dynamodb') + kms_client = boto3.client('kms') + + keystore: KeyStore = KeyStore( + config=KeyStoreConfig( + ddb_client=ddb_client, + ddb_table_name=key_store_table_name, + logical_key_store_name=logical_key_store_name, + kms_client=kms_client, + kms_configuration=KMSConfigurationKmsKeyArn(value=keystore_kms_key_id), + ) + ) + + new_branch_key_id: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier + print(f"DEBUG: {new_branch_key_id=}") + + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( + key_store=keystore, + branch_key_id=new_branch_key_id, + ttl_seconds=600, + cache=CacheTypeDefault(value=DefaultCache(entry_capacity=100)), + ) + + hierarchical_keyring: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring( + input=keyring_input + ) + # This is as far as we can go in the linked Java example without the ESDK. + # We can't use this keyring until it's integrated with the ESDK :( + # Peek at it with print statement for now + print(f"DEBUG: {hierarchical_keyring=}") + + ##### + + cmm = mat_prov.create_default_cryptographic_materials_manager(CreateDefaultCryptographicMaterialsManagerInput(keyring=hierarchical_keyring)) + + cmm_handler: CMMHandler = CMMHandler(cmm) # Encrypt the plaintext source data - ciphertext, encryptor_header = client.encrypt(source=source_plaintext, key_provider=master_key_provider) + ciphertext, encryptor_header = client.encrypt(source=source_plaintext, materials_manager=cmm_handler) # Decrypt the ciphertext - cycled_plaintext, decrypted_header = client.decrypt(source=ciphertext, key_provider=master_key_provider) + cycled_plaintext, decrypted_header = client.decrypt(source=ciphertext, materials_manager=cmm_handler) + cycled_plaintext_str = str(cycled_plaintext, encoding="ascii") # Verify that the "cycled" (encrypted, then decrypted) plaintext is identical to the source plaintext - assert cycled_plaintext == source_plaintext + assert cycled_plaintext_str == source_plaintext # Verify that the encryption context used in the decrypt operation includes all key pairs from # the encrypt operation. (The SDK can add pairs, so don't require an exact match.) @@ -49,5 +131,9 @@ def cycle_string(key_arn, source_plaintext, botocore_session=None): # In production, always use a meaningful encryption context. In this sample, we omit the # encryption context (no key pairs). assert all( - pair in decrypted_header.encryption_context.items() for pair in encryptor_header.encryption_context.items() + (str(k, encoding="ascii"), str(v, encoding="ascii")) in decrypted_header.encryption_context.items() for (k, v) in encryptor_header.encryption_context.items() ) + +# hack in a test +import botocore +cycle_string("arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f", "abcdefg", botocore_session=botocore.session.Session()) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2f4323845..13466216c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ boto3>=1.10.0 cryptography>=3.4.0 attrs>=17.4.0 -wrapt>=1.10.11 +wrapt>=1.16.0 diff --git a/setup.py b/setup.py index 7cc111bac..cafc16979 100644 --- a/setup.py +++ b/setup.py @@ -39,6 +39,11 @@ def get_requirements(): keywords="aws-encryption-sdk aws kms encryption", license="Apache License 2.0", install_requires=get_requirements(), + # TODO: Point at main once Python is merged into main. + # PyPI will not accept a package that declares dependencies using direct URLs. + extras_require={ + "MPL": ["aws-cryptographic-material-providers @ git+https://github.com/aws/aws-cryptographic-material-providers-library.git@lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python"], + }, classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", @@ -49,6 +54,9 @@ def get_requirements(): "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Security", "Topic :: Security :: Cryptography", diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index f90ac77e0..ad5cf1b2a 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -76,7 +76,8 @@ def from_key_bytes(cls, algorithm, key_bytes): :param bytes key_bytes: Raw signing key :rtype: aws_encryption_sdk.internal.crypto.Signer """ - key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) + # key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) + key = serialization.load_pem_private_key(data=key_bytes, password=None, backend=default_backend()) return cls(algorithm, key) def key_bytes(self): @@ -140,6 +141,7 @@ def from_encoded_point(cls, algorithm, encoded_point): :returns: Instance of Verifier generated from encoded point :rtype: aws_encryption_sdk.internal.crypto.Verifier """ + print(f"from_encoded_point {encoded_point=}") return cls( algorithm=algorithm, key=_ecc_public_numbers_from_compressed_point( @@ -157,8 +159,10 @@ def from_key_bytes(cls, algorithm, key_bytes): :returns: Instance of Verifier generated from encoded point :rtype: aws_encryption_sdk.internal.crypto.Verifier """ + print(f"{algorithm=}") + print(f"{key_bytes=}") return cls( - algorithm=algorithm, key=serialization.load_der_public_key(data=key_bytes, backend=default_backend()) + algorithm=algorithm, key=serialization.load_pem_public_key(data=key_bytes, backend=default_backend()) ) def key_bytes(self): diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 1119cb740..afed52e0f 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -66,6 +66,21 @@ from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import MessageHeader +try: + import aws_cryptographic_materialproviders + from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.client import AwsCryptographicMaterialProviders + from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.config import MaterialProvidersConfig + from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.models import ( + CreateDefaultCryptographicMaterialsManagerInput + ) + from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references import ( + IKeyring, + ) + from aws_encryption_sdk.cmm_handler import CMMHandler + + _has_mpl = True +except ImportError as e: + _has_mpl = False _LOGGER = logging.getLogger(__name__) @@ -113,6 +128,10 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes key_provider = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(MasterKeyProvider)) ) + if _has_mpl: + keyring = attr.ib( + hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(IKeyring)) + ) source_length = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(six.integer_types)) ) @@ -122,13 +141,38 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes def __attrs_post_init__(self): """Normalize inputs to crypto material manager.""" - both_cmm_and_mkp_defined = self.materials_manager is not None and self.key_provider is not None - neither_cmm_nor_mkp_defined = self.materials_manager is None and self.key_provider is None + if _has_mpl: + all_cmm_and_mkp_and_keyring_defined = all([ + self.materials_manager is not None, + self.key_provider is not None, + self.keyring is not None, + ]) + none_cmm_nor_mkp_nor_keyring_defined = all([ + self.materials_manager is None, + self.key_provider is None, + self.keyring is None, + ]) + + if all_cmm_and_mkp_and_keyring_defined or none_cmm_nor_mkp_nor_keyring_defined: + raise TypeError("Exactly one of keyring, materials_manager, or key_provider must be provided") + if self.materials_manager is None: + if self.key_provider is not None: + self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) + elif self.keyring is not None: + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + cmm = mat_prov.create_default_cryptographic_materials_manager(CreateDefaultCryptographicMaterialsManagerInput(keyring=self.keyring)) + cmm_handler: CryptoMaterialsManager = CMMHandler(cmm) + self.materials_manager = cmm_handler + elif not _has_mpl: + both_cmm_and_mkp_defined = self.materials_manager is not None and self.key_provider is not None + neither_cmm_nor_mkp_defined = self.materials_manager is None and self.key_provider is None - if both_cmm_and_mkp_defined or neither_cmm_nor_mkp_defined: - raise TypeError("Exactly one of materials_manager or key_provider must be provided") - if self.materials_manager is None: - self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) + if both_cmm_and_mkp_defined or neither_cmm_nor_mkp_defined: + raise TypeError("Exactly one of materials_manager or key_provider must be provided") + if self.materials_manager is None: + self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) class _EncryptionStream(io.IOBase): @@ -343,6 +387,8 @@ class EncryptorConfig(_ClientConfig): :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param keyring: `IKeyring` TODO-MPL content + :type keyring: TODO-MPL :param int source_length: Length of source data (optional) .. note:: @@ -394,6 +440,8 @@ class StreamEncryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param keyring: `IKeyring` TODO-MPL content + :type keyring: TODO-MPL :param int source_length: Length of source data (optional) .. note:: @@ -729,11 +777,13 @@ class DecryptorConfig(_ClientConfig): :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file :param materials_manager: `CryptoMaterialsManager` from which to obtain cryptographic materials - (either `materials_manager` or `key_provider` required) + (either `keyring`, `materials_manager` or `key_provider` required) :type materials_manager: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption - (either `materials_manager` or `key_provider` required) + (either `keyring`, `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param keyring: `IKeyring` TODO-MPL content + :type keyring: TODO-MPL :param int source_length: Length of source data (optional) .. note:: @@ -770,6 +820,8 @@ class StreamDecryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param keyring: `IKeyring` TODO-MPL content + :type keyring: TODO-MPL :param int source_length: Length of source data (optional) .. note:: @@ -831,9 +883,18 @@ def _read_header(self): if decryption_materials.verification_key is None: self.verifier = None else: - self.verifier = Verifier.from_key_bytes( - algorithm=header.algorithm, key_bytes=decryption_materials.verification_key - ) + # MPL verification key is NOT key bytes, it is bytes of the compressed point + # TODO-MPL: clean this up, least-privilege violation + import base64 + if hasattr(self.config.materials_manager, "mpl_cmm"): + self.verifier = Verifier.from_encoded_point( + algorithm=header.algorithm, + encoded_point=base64.b64encode(decryption_materials.verification_key) + ) + else: + self.verifier = Verifier.from_key_bytes( + algorithm=header.algorithm, key_bytes=decryption_materials.verification_key + ) if self.verifier is not None: self.verifier.update(raw_header) From 53c46ece22fec60a2d4d653a4720f66fe706ccca Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 2 Feb 2024 09:19:14 -0800 Subject: [PATCH 002/172] cleanup --- examples/src/basic_encryption.py | 98 +--------------- requirements.txt | 2 +- setup.py | 1 - .../internal/crypto/authentication.py | 3 - src/aws_encryption_sdk/streaming_client.py | 105 +++++++++++------- 5 files changed, 74 insertions(+), 135 deletions(-) diff --git a/examples/src/basic_encryption.py b/examples/src/basic_encryption.py index f48f7e4a1..cfe8ac791 100644 --- a/examples/src/basic_encryption.py +++ b/examples/src/basic_encryption.py @@ -13,40 +13,7 @@ """Example showing basic encryption and decryption of a value already in memory.""" import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy -import aws_cryptographic_materialproviders -import boto3 -from aws_encryption_sdk.cmm_handler import (CMMHandler) - -import sys - -module_root_dir = '/'.join(__file__.split("/")[:-1]) - -sys.path.append(module_root_dir) - -import aws_cryptographic_materialproviders - -from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.client import AwsCryptographicMaterialProviders -from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.config import MaterialProvidersConfig -from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.models import ( - CreateAwsKmsHierarchicalKeyringInput, - CacheTypeDefault, - DefaultCache, - GetBranchKeyIdInput, - GetBranchKeyIdOutput, - CreateDefaultCryptographicMaterialsManagerInput, -) -from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references import ( - IKeyring, - IBranchKeyIdSupplier, -) - -from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_keystore.client import KeyStore -from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_keystore.config import KeyStoreConfig -from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_keystore.models import ( - CreateKeyInput, - KMSConfigurationKmsKeyArn, -) def cycle_string(key_arn, source_plaintext, botocore_session=None): """Encrypts and then decrypts a string under a KMS customer master key (CMK). @@ -58,72 +25,23 @@ def cycle_string(key_arn, source_plaintext, botocore_session=None): """ # Set up an encryption client with an explicit commitment policy. Note that if you do not explicitly choose a # commitment policy, REQUIRE_ENCRYPT_REQUIRE_DECRYPT is used by default. - client = aws_encryption_sdk.EncryptionSDKClient() + client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) # Create a KMS master key provider. Note that because we are planning on decrypting using this same provider, # we MUST provide the ARN of the KMS Key. If we provide a raw key id or a key alias, decryption will fail. kms_kwargs = dict(key_ids=[key_arn]) if botocore_session is not None: kms_kwargs["botocore_session"] = botocore_session - # master_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**kms_kwargs) - - ##### - - - key_store_table_name="KeyStoreDdbTable" - logical_key_store_name="KeyStoreDdbTable" - keystore_kms_key_id="arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" - - ddb_client = boto3.client('dynamodb') - kms_client = boto3.client('kms') - - keystore: KeyStore = KeyStore( - config=KeyStoreConfig( - ddb_client=ddb_client, - ddb_table_name=key_store_table_name, - logical_key_store_name=logical_key_store_name, - kms_client=kms_client, - kms_configuration=KMSConfigurationKmsKeyArn(value=keystore_kms_key_id), - ) - ) - - new_branch_key_id: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier - print(f"DEBUG: {new_branch_key_id=}") - - mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( - config=MaterialProvidersConfig() - ) - - keyring_input: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( - key_store=keystore, - branch_key_id=new_branch_key_id, - ttl_seconds=600, - cache=CacheTypeDefault(value=DefaultCache(entry_capacity=100)), - ) - - hierarchical_keyring: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring( - input=keyring_input - ) - # This is as far as we can go in the linked Java example without the ESDK. - # We can't use this keyring until it's integrated with the ESDK :( - # Peek at it with print statement for now - print(f"DEBUG: {hierarchical_keyring=}") - - ##### - - cmm = mat_prov.create_default_cryptographic_materials_manager(CreateDefaultCryptographicMaterialsManagerInput(keyring=hierarchical_keyring)) - - cmm_handler: CMMHandler = CMMHandler(cmm) + master_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**kms_kwargs) # Encrypt the plaintext source data - ciphertext, encryptor_header = client.encrypt(source=source_plaintext, materials_manager=cmm_handler) + ciphertext, encryptor_header = client.encrypt(source=source_plaintext, key_provider=master_key_provider) # Decrypt the ciphertext - cycled_plaintext, decrypted_header = client.decrypt(source=ciphertext, materials_manager=cmm_handler) - cycled_plaintext_str = str(cycled_plaintext, encoding="ascii") + cycled_plaintext, decrypted_header = client.decrypt(source=ciphertext, key_provider=master_key_provider) # Verify that the "cycled" (encrypted, then decrypted) plaintext is identical to the source plaintext - assert cycled_plaintext_str == source_plaintext + assert cycled_plaintext == source_plaintext # Verify that the encryption context used in the decrypt operation includes all key pairs from # the encrypt operation. (The SDK can add pairs, so don't require an exact match.) @@ -131,9 +49,5 @@ def cycle_string(key_arn, source_plaintext, botocore_session=None): # In production, always use a meaningful encryption context. In this sample, we omit the # encryption context (no key pairs). assert all( - (str(k, encoding="ascii"), str(v, encoding="ascii")) in decrypted_header.encryption_context.items() for (k, v) in encryptor_header.encryption_context.items() + pair in decrypted_header.encryption_context.items() for pair in encryptor_header.encryption_context.items() ) - -# hack in a test -import botocore -cycle_string("arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f", "abcdefg", botocore_session=botocore.session.Session()) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 13466216c..2f4323845 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ boto3>=1.10.0 cryptography>=3.4.0 attrs>=17.4.0 -wrapt>=1.16.0 +wrapt>=1.10.11 diff --git a/setup.py b/setup.py index cafc16979..8ffd74015 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,6 @@ def get_requirements(): "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Security", "Topic :: Security :: Cryptography", diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index ad5cf1b2a..b9692eb16 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -141,7 +141,6 @@ def from_encoded_point(cls, algorithm, encoded_point): :returns: Instance of Verifier generated from encoded point :rtype: aws_encryption_sdk.internal.crypto.Verifier """ - print(f"from_encoded_point {encoded_point=}") return cls( algorithm=algorithm, key=_ecc_public_numbers_from_compressed_point( @@ -159,8 +158,6 @@ def from_key_bytes(cls, algorithm, key_bytes): :returns: Instance of Verifier generated from encoded point :rtype: aws_encryption_sdk.internal.crypto.Verifier """ - print(f"{algorithm=}") - print(f"{key_bytes=}") return cls( algorithm=algorithm, key=serialization.load_pem_public_key(data=key_bytes, backend=default_backend()) ) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index afed52e0f..176b92334 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -139,40 +139,61 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes hash=True, default=LINE_LENGTH, validator=attr.validators.instance_of(six.integer_types) ) # DEPRECATED: Value is no longer configurable here. Parameter left here to avoid breaking consumers. + def _has_mpl_attrs_post_init(self): + + def _exactly_one_arg_is_not_None(*args): + ''' + Private helper function. + Returns `True` if exactly one item in the list is not `None`. + Returns `False` otherwise. + ''' + # Have not found any `not None` + found_one = False + for arg in args: + if arg is not None: + if found_one == False: + # Have not already found a `not None`, found a `not None` => only one `not None` (so far) + found_one = True + else: + # Already found a `not None`, found another `not None` => not exactly one `not None` + return False + return found_one + + if not _exactly_one_arg_is_not_None(self.materials_manager, self.key_provider, self.keyring): + raise TypeError("Exactly one of keyring, materials_manager, or key_provider must be provided") + if self.materials_manager is None: + if self.key_provider is not None: + # No CMM, provided (legacy) native `key_provider` => create (legacy) native DefaultCryptoMaterialsManager + self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) + elif self.keyring is not None: + # No CMM, provided MPL keyring => create MPL's DefaultCryptographicMaterialsManager + try: + assert isinstance(self.keyring, IKeyring) + except AssertionError as e: + raise ValueError(f"Argument provided to keyring MUST be a {IKeyring}. Found {keyring.__class__.__name__=}") + + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + cmm = mat_prov.create_default_cryptographic_materials_manager(CreateDefaultCryptographicMaterialsManagerInput(keyring=self.keyring)) + cmm_handler: CryptoMaterialsManager = CMMHandler(cmm) + self.materials_manager = cmm_handler + + def _no_mpl_attrs_post_init(self): + both_cmm_and_mkp_defined = self.materials_manager is not None and self.key_provider is not None + neither_cmm_nor_mkp_defined = self.materials_manager is None and self.key_provider is None + + if both_cmm_and_mkp_defined or neither_cmm_nor_mkp_defined: + raise TypeError("Exactly one of materials_manager or key_provider must be provided") + if self.materials_manager is None: + self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) + def __attrs_post_init__(self): """Normalize inputs to crypto material manager.""" if _has_mpl: - all_cmm_and_mkp_and_keyring_defined = all([ - self.materials_manager is not None, - self.key_provider is not None, - self.keyring is not None, - ]) - none_cmm_nor_mkp_nor_keyring_defined = all([ - self.materials_manager is None, - self.key_provider is None, - self.keyring is None, - ]) - - if all_cmm_and_mkp_and_keyring_defined or none_cmm_nor_mkp_nor_keyring_defined: - raise TypeError("Exactly one of keyring, materials_manager, or key_provider must be provided") - if self.materials_manager is None: - if self.key_provider is not None: - self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) - elif self.keyring is not None: - mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( - config=MaterialProvidersConfig() - ) - cmm = mat_prov.create_default_cryptographic_materials_manager(CreateDefaultCryptographicMaterialsManagerInput(keyring=self.keyring)) - cmm_handler: CryptoMaterialsManager = CMMHandler(cmm) - self.materials_manager = cmm_handler + self._has_mpl_attrs_post_init() elif not _has_mpl: - both_cmm_and_mkp_defined = self.materials_manager is not None and self.key_provider is not None - neither_cmm_nor_mkp_defined = self.materials_manager is None and self.key_provider is None - - if both_cmm_and_mkp_defined or neither_cmm_nor_mkp_defined: - raise TypeError("Exactly one of materials_manager or key_provider must be provided") - if self.materials_manager is None: - self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) + self._no_mpl_attrs_post_init() class _EncryptionStream(io.IOBase): @@ -387,8 +408,10 @@ class EncryptorConfig(_ClientConfig): :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `IKeyring` TODO-MPL content - :type keyring: TODO-MPL + :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library + which handles encryption and decryption + :type keyring: + aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -440,8 +463,10 @@ class StreamEncryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `IKeyring` TODO-MPL content - :type keyring: TODO-MPL + :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library + which handles encryption and decryption + :type keyring: + aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -782,8 +807,10 @@ class DecryptorConfig(_ClientConfig): :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption (either `keyring`, `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `IKeyring` TODO-MPL content - :type keyring: TODO-MPL + :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library + which handles encryption and decryption + :type keyring: + aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -820,8 +847,10 @@ class StreamDecryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `IKeyring` TODO-MPL content - :type keyring: TODO-MPL + :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library + which handles encryption and decryption + :type keyring: + aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references.IKeyring :param int source_length: Length of source data (optional) .. note:: From 3f5a503ab7f866c7750cabdc79dbf7f8f75a34e6 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 2 Feb 2024 09:21:46 -0800 Subject: [PATCH 003/172] add --- setup.py | 3 +- src/aws_encryption_sdk/cmm_handler.py | 138 ++++++++++++++++ src/aws_encryption_sdk/materials_handlers.py | 165 +++++++++++++++++++ 3 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 src/aws_encryption_sdk/cmm_handler.py create mode 100644 src/aws_encryption_sdk/materials_handlers.py diff --git a/setup.py b/setup.py index 8ffd74015..c4c277096 100644 --- a/setup.py +++ b/setup.py @@ -39,8 +39,7 @@ def get_requirements(): keywords="aws-encryption-sdk aws kms encryption", license="Apache License 2.0", install_requires=get_requirements(), - # TODO: Point at main once Python is merged into main. - # PyPI will not accept a package that declares dependencies using direct URLs. + # TODO: Point at MPL main branch once Python MPL is merged into main. extras_require={ "MPL": ["aws-cryptographic-material-providers @ git+https://github.com/aws/aws-cryptographic-material-providers-library.git@lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python"], }, diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py new file mode 100644 index 000000000..82aad2248 --- /dev/null +++ b/src/aws_encryption_sdk/cmm_handler.py @@ -0,0 +1,138 @@ +# These dependencies are only loaded if you install the MPL. +try: + from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references import ( + ICryptographicMaterialsManager, + ) + from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.models import ( + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, + DecryptMaterialsInput, + DecryptMaterialsOutput, + EncryptedDataKey as MPL_EncryptedDataKey, + CommitmentPolicyESDK, + AlgorithmSuiteIdESDK, + ) +except ImportError as e: + print(f"WARNING: MPL import failed with {e=}") + +from aws_encryption_sdk.materials_managers import ( + DecryptionMaterialsRequest, + EncryptionMaterialsRequest, +) +from aws_encryption_sdk.materials_managers.base import ( + CryptoMaterialsManager, +) +from aws_encryption_sdk.materials_handlers import ( + EncryptionMaterialsHandler, + DecryptionMaterialsHandler, +) +from aws_encryption_sdk.structures import ( + EncryptedDataKey as Native_EncryptedDataKey, +) +from aws_encryption_sdk.identifiers import ( + Algorithm, + AlgorithmSuite, + CommitmentPolicy, +) + +# TODO-MPL Should this implement interface..? seems like yes since it implements all of interface methods +class CMMHandler(CryptoMaterialsManager): + native_cmm: CryptoMaterialsManager + mpl_cmm: 'ICryptographicMaterialsManager' + + def __init__( + self, + cmm: 'CryptoMaterialsManager | ICryptographicMaterialsManager' + ): + if isinstance(cmm, CryptoMaterialsManager): + self.native_cmm = cmm + elif isinstance(cmm, ICryptographicMaterialsManager): + self.mpl_cmm = cmm + else: + raise ValueError(f"Invalid CMM passed to CMMHander: {cmm=}") + + def get_encryption_materials( + self, + request: EncryptionMaterialsRequest + ) -> EncryptionMaterialsHandler: + ''' + Returns an EncryptionMaterialsHandler based on the configured CMM. + ''' + if (hasattr(self, "native_cmm") and not hasattr(self, "mpl_cmm")): + return EncryptionMaterialsHandler(self.native_cmm.get_encryption_materials(request)) + else: + input: GetEncryptionMaterialsInput = CMMHandler._create_mpl_get_encryption_materials_input_from_request(request) + print(f"get_encryption_materials {input=}") + output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(input) + print(f"get_encryption_materials {output=}") + return EncryptionMaterialsHandler(output.encryption_materials) + + @staticmethod + def _create_mpl_get_encryption_materials_input_from_request( + request: EncryptionMaterialsRequest + ) -> 'GetEncryptionMaterialsInput': + print(f"_create_mpl_get_encryption_materials_input_from_request {request=}") + print(f"{CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy(request.commitment_policy)=}") + print(f"_create_mpl_get_encryption_materials_input_from_request {request.encryption_context=}") + output: GetEncryptionMaterialsInput = GetEncryptionMaterialsInput( + encryption_context=request.encryption_context, + commitment_policy=CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy(request.commitment_policy), + # TODO double check this + # optional... maybe this needs to be kwargs?? + # algorithm_suite_id=request.algorithm.algorithm_id, + max_plaintext_length=request.plaintext_length, + ) + print(f"_create_mpl_get_encryption_materials_input_from_request {output=}") + return output + + @staticmethod + def _map_native_commitment_policy_to_mpl_commitment_policy( + native_commitment_policy: CommitmentPolicy + ) -> CommitmentPolicyESDK: + if native_commitment_policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT: + return CommitmentPolicyESDK(value="FORBID_ENCRYPT_ALLOW_DECRYPT") + elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT: + return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_ALLOW_DECRYPT") + elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT: + return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_REQUIRE_DECRYPT") + else: + raise ValueError(f"Invalid {native_commitment_policy=}") + + def decrypt_materials( + self, + request: DecryptionMaterialsRequest + ) -> DecryptionMaterialsHandler: + ''' + Returns a DecryptionMaterialsHandler based on the configured CMM. + ''' + print(f"decrypt_materials {request=}") + if (hasattr(self, "native_cmm") and not hasattr(self, "mpl_cmm")): + return DecryptionMaterialsHandler(self.native_cmm.decrypt_materials(request)) + else: + input: 'DecryptMaterialsInput' = CMMHandler._create_mpl_decrypt_materials_input_from_request(request) + output: 'DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(input) + print(f"decrypt_materials {output.decryption_materials.verification_key=}") + return DecryptionMaterialsHandler(output.decryption_materials) + + @staticmethod + def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> AlgorithmSuiteIdESDK: + # MPL algorithm suite ID = hexstr(native_algorithm_id) padded to 4 digits post-`x`. + return AlgorithmSuiteIdESDK(f"{native_algorithm_id:#0{6}x}") + + @staticmethod + def _create_mpl_decrypt_materials_input_from_request( + request: DecryptionMaterialsRequest + ) -> 'DecryptMaterialsInput': + key_blob_list: list[Native_EncryptedDataKey] = request.encrypted_data_keys + list_edks = [MPL_EncryptedDataKey( + key_provider_id=key_blob.key_provider.provider_id, + key_provider_info=key_blob.key_provider.key_info, + ciphertext=key_blob.encrypted_data_key, + ) for key_blob in key_blob_list] + output: DecryptMaterialsInput = DecryptMaterialsInput( + algorithm_suite_id=CMMHandler._native_algorithm_id_to_mpl_algorithm_id(request.algorithm.algorithm_id), + commitment_policy=CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy(request.commitment_policy), + encrypted_data_keys=list_edks, + encryption_context=request.encryption_context, + ) + return output diff --git a/src/aws_encryption_sdk/materials_handlers.py b/src/aws_encryption_sdk/materials_handlers.py new file mode 100644 index 000000000..bf3073ad3 --- /dev/null +++ b/src/aws_encryption_sdk/materials_handlers.py @@ -0,0 +1,165 @@ +# These dependencies are only loaded if you install the MPL. +try: + from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.models import ( + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptionMaterials as MPL_EncryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + ) +except ImportError as e: + pass + +from aws_encryption_sdk.materials_managers import ( + DecryptionMaterials as Native_DecryptionMaterials, + EncryptionMaterials as Native_EncryptionMaterials, +) +from aws_encryption_sdk.identifiers import ( + Algorithm, + AlgorithmSuite, +) +from aws_encryption_sdk.structures import ( + DataKey, + EncryptedDataKey as Native_EncryptedDataKey, + MasterKeyInfo, +) +from aws_encryption_sdk.internal.crypto.authentication import ( + Signer +) + +class EncryptionMaterialsHandler: + native_materials: Native_EncryptionMaterials + mpl_materials: 'MPL_EncryptionMaterials' + + @staticmethod + def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str): + # MPL algorithm suite ID == "ALG_" + native algorithm suite ID. + return int(mpl_algorithm_id, 16) + + def __init__( + self, + materials: 'Native_EncryptionMaterials | MPL_EncryptionMaterials' + ): + if isinstance(materials, Native_EncryptionMaterials): + self.native_materials = materials + elif isinstance(materials, MPL_EncryptionMaterials): + self.mpl_materials = materials + else: + raise ValueError(f"Invalid EncryptionMaterials passed to EncryptionMaterialsHandler: {materials=}") + @property + def algorithm(self) -> Algorithm: + if hasattr(self, "native_materials"): + return self.native_materials.algorithm + else: + print(f"algorithm {self.mpl_materials.algorithm_suite.id.value=}") + return AlgorithmSuite.get_by_id( + EncryptionMaterialsHandler._mpl_algorithm_id_to_native_algorithm_id( + self.mpl_materials.algorithm_suite.id.value + ) + ) + + @property + def encryption_context(self) -> dict[str, str]: + if hasattr(self, "native_materials"): + return self.native_materials.encryption_context + else: + return self.mpl_materials.encryption_context + + @property + def encrypted_data_keys(self) -> list[Native_EncryptedDataKey]: + if hasattr(self, "native_materials"): + return self.native_materials.encrypted_data_keys + else: + mpl_edk_list: list[MPL_EncryptedDataKey] = self.mpl_materials.encrypted_data_keys + key_blob_list: set[Native_EncryptedDataKey] = {Native_EncryptedDataKey( + key_provider=MasterKeyInfo( + provider_id=mpl_edk.key_provider_id, + key_info=mpl_edk.key_provider_info, + ), + encrypted_data_key=mpl_edk.ciphertext, + ) for mpl_edk in mpl_edk_list} + return key_blob_list + + @property + def data_encryption_key(self) -> DataKey: + if hasattr(self, "native_materials"): + return self.native_materials.data_encryption_key + else: + # TODO-MPL This impl is probably wrong + mpl_dek = self.mpl_materials.plaintext_data_key + return DataKey( + # key_provider=None, # No MasterKeyInfo object for plaintext data key + key_provider=MasterKeyInfo( + provider_id="", + key_info=b'' + ), + data_key=mpl_dek, + encrypted_data_key=b'', # No encrypted DEK + ) + + @property + def signing_key(self) -> bytes: + if hasattr(self, "native_materials"): + return self.native_materials.signing_key + else: + print(f"sign {self.mpl_materials.signing_key=}") + return self.mpl_materials.signing_key + # if self.mpl_materials.signing_key is None: + # return Signer.from_key_bytes( + # algorithm=AlgorithmSuite.get_by_id(self.mpl_materials.algorithm_suite.id.value), + # bytes=self.mpl_materials.signing_key + # ) + + def get_required_encryption_context_keys(self) -> list[str]: + if hasattr(self, "native_materials"): + return [] + else: + return self.mpl_materials.required_encryption_context_keys + +class DecryptionMaterialsHandler: + native_materials: Native_DecryptionMaterials + mpl_materials: 'MPL_DecryptionMaterials' + + def __init__( + self, + materials: 'Native_DecryptionMaterials | MPL_DecryptionMaterials' + ): + if isinstance(materials, Native_DecryptionMaterials): + self.native_materials = materials + elif isinstance(materials, MPL_DecryptionMaterials): + self.mpl_materials = materials + else: + raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler: {materials=}") + + def get_encryption_context(self) -> dict[str, str]: + if hasattr(self, "native_materials"): + return {} # TODO-MPL This impl is probably wrong + else: + return self.mpl_materials.encryption_context + + @property + def data_key(self) -> DataKey: + if hasattr(self, "native_materials"): + return self.native_materials.data_key + else: + # TODO-MPL This impl is probably wrong + return DataKey( + key_provider=MasterKeyInfo( + provider_id="", + key_info=b'' + ), + data_key=self.mpl_materials.plaintext_data_key, + encrypted_data_key=b'', + ) + + @property + def verification_key(self) -> bytes: + if hasattr(self, "native_materials"): + return self.native_materials.verification_key + else: + print(f"ver {self.mpl_materials.verification_key=}") + return self.mpl_materials.verification_key + + def get_required_encryption_context_keys(self) -> list[str]: + if hasattr(self, "native_materials"): + return [] + else: + return self.mpl_materials.required_encryption_context_keys \ No newline at end of file From 16cf5c1f38ae1aaa0e7e5f68fc58cd0c6f3532b2 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 2 Feb 2024 14:41:53 -0800 Subject: [PATCH 004/172] changes, cleanup: --- examples/src/basic_encryption.py | 5 + examples/src/keyrings/hierarchical_keyring.py | 248 ++++++++++++++++++ examples/src/keyrings/module_.py | 0 examples/src/module_.py | 0 src/aws_encryption_sdk/cmm_handler.py | 55 ++-- src/aws_encryption_sdk/materials_handlers.py | 56 ++-- src/aws_encryption_sdk/streaming_client.py | 18 +- .../test_streaming_client_stream_decryptor.py | 2 +- 8 files changed, 314 insertions(+), 70 deletions(-) create mode 100644 examples/src/keyrings/hierarchical_keyring.py create mode 100644 examples/src/keyrings/module_.py create mode 100644 examples/src/module_.py diff --git a/examples/src/basic_encryption.py b/examples/src/basic_encryption.py index cfe8ac791..7b729feab 100644 --- a/examples/src/basic_encryption.py +++ b/examples/src/basic_encryption.py @@ -51,3 +51,8 @@ def cycle_string(key_arn, source_plaintext, botocore_session=None): assert all( pair in decrypted_header.encryption_context.items() for pair in encryptor_header.encryption_context.items() ) + +cycle_string( + "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f", + "abcdefg", +) \ No newline at end of file diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py new file mode 100644 index 000000000..e8f662b73 --- /dev/null +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -0,0 +1,248 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + + + +"""Example showing basic encryption and decryption of a value already in memory.""" +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +import boto3 + +import sys + +from aws_encryption_sdk.exceptions import ( + AWSEncryptionSDKClientError, + SerializationError, +) + +module_root_dir = '/'.join(__file__.split("/")[:-1]) + +sys.path.append(module_root_dir) + +import aws_cryptographic_materialproviders + +from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders +from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig +from aws_cryptographic_materialproviders.mpl.models import ( + CreateAwsKmsHierarchicalKeyringInput, + CacheTypeDefault, + DefaultCache, + GetBranchKeyIdInput, + GetBranchKeyIdOutput, +) +from aws_cryptographic_materialproviders.mpl.references import ( + IKeyring, + IBranchKeyIdSupplier, +) + +from aws_cryptographic_materialproviders.keystore.client import KeyStore +from aws_cryptographic_materialproviders.keystore.config import KeyStoreConfig +from aws_cryptographic_materialproviders.keystore.models import ( + CreateKeyInput, + KMSConfigurationKmsKeyArn, +) + +EXAMPLE_DATA: bytes = b"Hello World" + +def encrypt_and_decrypt_with_keyring( + key_store_table_name: str, + logical_key_store_name: str, + kms_key_id: str + ): + + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create boto3 clients for DynamoDB and KMS. + ddb_client = boto3.client('dynamodb') + kms_client = boto3.client('kms') + + # 3. Configure your KeyStore resource. + # This SHOULD be the same configuration that you used + # to initially create and populate your KeyStore. + keystore: KeyStore = KeyStore( + config=KeyStoreConfig( + ddb_client=ddb_client, + ddb_table_name=key_store_table_name, + logical_key_store_name=logical_key_store_name, + kms_client=kms_client, + kms_configuration=KMSConfigurationKmsKeyArn( + value=kms_key_id + ), + ) + ) + + # 4. Call CreateKey to create two new active branch keys + branch_key_id_A: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier + branch_key_id_B: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier + + class ExampleBranchKeyIdSupplier(IBranchKeyIdSupplier): + branch_key_id_for_tenant_A: str + branch_key_id_for_tenant_B: str + + def __init__(self, tenant_1_id, tenant_2_id): + self.branch_key_id_for_tenant_A = tenant_1_id + self.branch_key_id_for_tenant_B = tenant_2_id + + def get_branch_key_id( + self, + input: GetBranchKeyIdInput + ) -> GetBranchKeyIdOutput: + encryption_context: dict[str, str] = input.encryption_context + + if b"tenant" not in encryption_context: + raise ValueError("EncryptionContext invalid, does not contain expected tenant key value pair.") + + tenant_key_id: str = encryption_context.get(b"tenant") + branch_key_id: str + + if tenant_key_id == b"TenantA": + branch_key_id = self.branch_key_id_for_tenant_A + elif tenant_key_id == b"TenantB": + branch_key_id = self.branch_key_id_for_tenant_B + else: + raise ValueError(f"Item does not contain valid tenant ID: {tenant_key_id=}") + + return GetBranchKeyIdOutput(branch_key_id=branch_key_id) + + # 5. Create a branch key supplier that maps the branch key id to a more readable format + branch_key_id_supplier: IBranchKeyIdSupplier = ExampleBranchKeyIdSupplier( + tenant_1_id=branch_key_id_A, + tenant_2_id=branch_key_id_B, + ) + + # 6. Create the Hierarchical Keyring. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( + key_store=keystore, + branch_key_id_supplier=branch_key_id_supplier, + ttl_seconds=600, + cache=CacheTypeDefault( + value=DefaultCache( + entry_capacity=100 + ) + ), + ) + + hierarchical_keyring: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring( + input=keyring_input + ) + + # The Branch Key Id supplier uses the encryption context to determine which branch key id will + # be used to encrypt data. + # Create encryption context for TenantA + encryption_context_A: dict[str, str] = { + "tenant": "TenantA", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Create encryption context for TenantB + encryption_context_B: dict[str, str] = { + "tenant": "TenantB", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Encrypt the data for encryptionContextA & encryptionContextB + ciphertext_A, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=hierarchical_keyring, + encryption_context=encryption_context_A + ) + ciphertext_B, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=hierarchical_keyring, + encryption_context=encryption_context_B + ) + + # To attest that TenantKeyB cannot decrypt a message written by TenantKeyA + # let's construct more restrictive hierarchical keyrings. + keyring_input_A: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( + key_store=keystore, + branch_key_id=branch_key_id_A, + ttl_seconds=600, + cache=CacheTypeDefault( + value=DefaultCache( + entry_capacity=100 + ) + ), + ) + + hierarchical_keyring_A: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring( + input=keyring_input_A + ) + + keyring_input_B: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( + key_store=keystore, + branch_key_id=branch_key_id_B, + ttl_seconds=600, + cache=CacheTypeDefault( + value=DefaultCache( + entry_capacity=100 + ) + ), + ) + + hierarchical_keyring_B: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring( + input=keyring_input_B + ) + + # TODO: Run the decrypt, get expected exception type + # This should fail + try: + client.decrypt( + source=ciphertext_A, + keyring=hierarchical_keyring_B + ) + except AWSEncryptionSDKClientError: + pass + + # # This should fail + try: + client.decrypt( + source=ciphertext_B, + keyring=hierarchical_keyring_A + ) + except AWSEncryptionSDKClientError: + pass + + # These should succeed + plaintext_bytes_A, _ = client.decrypt( + source=ciphertext_A, + keyring=hierarchical_keyring_A + ) + assert plaintext_bytes_A == EXAMPLE_DATA + plaintext_bytes_B, _ = client.decrypt( + source=ciphertext_B, + keyring=hierarchical_keyring_B + ) + assert plaintext_bytes_B == EXAMPLE_DATA + +# Also, a thread-safe example ig + +# hack in a test +import botocore +encrypt_and_decrypt_with_keyring( + "KeyStoreDdbTable", + "KeyStoreDdbTable", + "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" +) \ No newline at end of file diff --git a/examples/src/keyrings/module_.py b/examples/src/keyrings/module_.py new file mode 100644 index 000000000..e69de29bb diff --git a/examples/src/module_.py b/examples/src/module_.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py index 82aad2248..d634dd571 100644 --- a/src/aws_encryption_sdk/cmm_handler.py +++ b/src/aws_encryption_sdk/cmm_handler.py @@ -1,9 +1,12 @@ # These dependencies are only loaded if you install the MPL. try: - from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references import ( + from aws_cryptographic_materialproviders.mpl.errors import ( + AwsCryptographicMaterialProvidersException + ) + from aws_cryptographic_materialproviders.mpl.references import ( ICryptographicMaterialsManager, ) - from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.models import ( + from aws_cryptographic_materialproviders.mpl.models import ( GetEncryptionMaterialsInput, GetEncryptionMaterialsOutput, DecryptMaterialsInput, @@ -13,8 +16,11 @@ AlgorithmSuiteIdESDK, ) except ImportError as e: - print(f"WARNING: MPL import failed with {e=}") + pass +from aws_encryption_sdk.exceptions import ( + AWSEncryptionSDKClientError, +) from aws_encryption_sdk.materials_managers import ( DecryptionMaterialsRequest, EncryptionMaterialsRequest, @@ -30,8 +36,6 @@ EncryptedDataKey as Native_EncryptedDataKey, ) from aws_encryption_sdk.identifiers import ( - Algorithm, - AlgorithmSuite, CommitmentPolicy, ) @@ -40,6 +44,9 @@ class CMMHandler(CryptoMaterialsManager): native_cmm: CryptoMaterialsManager mpl_cmm: 'ICryptographicMaterialsManager' + def _is_using_native_cmm(self): + return hasattr(self, "native_cmm") and not hasattr(self, "mpl_cmm") + def __init__( self, cmm: 'CryptoMaterialsManager | ICryptographicMaterialsManager' @@ -56,24 +63,24 @@ def get_encryption_materials( request: EncryptionMaterialsRequest ) -> EncryptionMaterialsHandler: ''' - Returns an EncryptionMaterialsHandler based on the configured CMM. + Returns an EncryptionMaterialsHandler for the configured CMM. ''' - if (hasattr(self, "native_cmm") and not hasattr(self, "mpl_cmm")): + if (self._is_using_native_cmm()): return EncryptionMaterialsHandler(self.native_cmm.get_encryption_materials(request)) else: - input: GetEncryptionMaterialsInput = CMMHandler._create_mpl_get_encryption_materials_input_from_request(request) - print(f"get_encryption_materials {input=}") - output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(input) - print(f"get_encryption_materials {output=}") - return EncryptionMaterialsHandler(output.encryption_materials) + try: + input: GetEncryptionMaterialsInput = CMMHandler._create_mpl_get_encryption_materials_input_from_request(request) + output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(input) + return EncryptionMaterialsHandler(output.encryption_materials) + except AwsCryptographicMaterialProvidersException as e: + # Wrap MPL error into the ESDK error type + # so customers only have to catch ESDK error types. + raise AWSEncryptionSDKClientError(e) @staticmethod def _create_mpl_get_encryption_materials_input_from_request( request: EncryptionMaterialsRequest ) -> 'GetEncryptionMaterialsInput': - print(f"_create_mpl_get_encryption_materials_input_from_request {request=}") - print(f"{CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy(request.commitment_policy)=}") - print(f"_create_mpl_get_encryption_materials_input_from_request {request.encryption_context=}") output: GetEncryptionMaterialsInput = GetEncryptionMaterialsInput( encryption_context=request.encryption_context, commitment_policy=CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy(request.commitment_policy), @@ -82,7 +89,6 @@ def _create_mpl_get_encryption_materials_input_from_request( # algorithm_suite_id=request.algorithm.algorithm_id, max_plaintext_length=request.plaintext_length, ) - print(f"_create_mpl_get_encryption_materials_input_from_request {output=}") return output @staticmethod @@ -103,16 +109,19 @@ def decrypt_materials( request: DecryptionMaterialsRequest ) -> DecryptionMaterialsHandler: ''' - Returns a DecryptionMaterialsHandler based on the configured CMM. + Returns a DecryptionMaterialsHandler for the configured CMM. ''' - print(f"decrypt_materials {request=}") - if (hasattr(self, "native_cmm") and not hasattr(self, "mpl_cmm")): + if (self._is_using_native_cmm()): return DecryptionMaterialsHandler(self.native_cmm.decrypt_materials(request)) else: - input: 'DecryptMaterialsInput' = CMMHandler._create_mpl_decrypt_materials_input_from_request(request) - output: 'DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(input) - print(f"decrypt_materials {output.decryption_materials.verification_key=}") - return DecryptionMaterialsHandler(output.decryption_materials) + try: + input: 'DecryptMaterialsInput' = CMMHandler._create_mpl_decrypt_materials_input_from_request(request) + output: 'DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(input) + return DecryptionMaterialsHandler(output.decryption_materials) + except AwsCryptographicMaterialProvidersException as e: + # Wrap MPL error into the ESDK error type + # so customers only have to catch ESDK error types. + raise AWSEncryptionSDKClientError(e) @staticmethod def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> AlgorithmSuiteIdESDK: diff --git a/src/aws_encryption_sdk/materials_handlers.py b/src/aws_encryption_sdk/materials_handlers.py index bf3073ad3..1f34eba03 100644 --- a/src/aws_encryption_sdk/materials_handlers.py +++ b/src/aws_encryption_sdk/materials_handlers.py @@ -1,6 +1,6 @@ # These dependencies are only loaded if you install the MPL. try: - from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.models import ( + from aws_cryptographic_materialproviders.mpl.models import ( DecryptionMaterials as MPL_DecryptionMaterials, EncryptionMaterials as MPL_EncryptionMaterials, EncryptedDataKey as MPL_EncryptedDataKey, @@ -25,15 +25,19 @@ Signer ) +def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str): + # MPL algorithm suite ID == hex(native algorithm suite ID) + return int(mpl_algorithm_id, 16) + class EncryptionMaterialsHandler: + ''' + In instances where encryption materials may be provided by either + the native `aws_encryption_sdk.materials_managers.Native_EncryptionMaterials` + or the MPL's `aws_cryptographic_materialproviders.mpl.models` + ''' native_materials: Native_EncryptionMaterials mpl_materials: 'MPL_EncryptionMaterials' - @staticmethod - def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str): - # MPL algorithm suite ID == "ALG_" + native algorithm suite ID. - return int(mpl_algorithm_id, 16) - def __init__( self, materials: 'Native_EncryptionMaterials | MPL_EncryptionMaterials' @@ -49,9 +53,8 @@ def algorithm(self) -> Algorithm: if hasattr(self, "native_materials"): return self.native_materials.algorithm else: - print(f"algorithm {self.mpl_materials.algorithm_suite.id.value=}") return AlgorithmSuite.get_by_id( - EncryptionMaterialsHandler._mpl_algorithm_id_to_native_algorithm_id( + _mpl_algorithm_id_to_native_algorithm_id( self.mpl_materials.algorithm_suite.id.value ) ) @@ -83,10 +86,12 @@ def data_encryption_key(self) -> DataKey: if hasattr(self, "native_materials"): return self.native_materials.data_encryption_key else: - # TODO-MPL This impl is probably wrong + # TODO-MPL This impl is probably wrong, but works for for now + # If this works for all features, great! Remove this comment before launch. + # Otherwise, fix the implementation. mpl_dek = self.mpl_materials.plaintext_data_key return DataKey( - # key_provider=None, # No MasterKeyInfo object for plaintext data key + # key_provider is unused, but the return type is DataKey key_provider=MasterKeyInfo( provider_id="", key_info=b'' @@ -100,20 +105,8 @@ def signing_key(self) -> bytes: if hasattr(self, "native_materials"): return self.native_materials.signing_key else: - print(f"sign {self.mpl_materials.signing_key=}") return self.mpl_materials.signing_key - # if self.mpl_materials.signing_key is None: - # return Signer.from_key_bytes( - # algorithm=AlgorithmSuite.get_by_id(self.mpl_materials.algorithm_suite.id.value), - # bytes=self.mpl_materials.signing_key - # ) - def get_required_encryption_context_keys(self) -> list[str]: - if hasattr(self, "native_materials"): - return [] - else: - return self.mpl_materials.required_encryption_context_keys - class DecryptionMaterialsHandler: native_materials: Native_DecryptionMaterials mpl_materials: 'MPL_DecryptionMaterials' @@ -128,19 +121,15 @@ def __init__( self.mpl_materials = materials else: raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler: {materials=}") - - def get_encryption_context(self) -> dict[str, str]: - if hasattr(self, "native_materials"): - return {} # TODO-MPL This impl is probably wrong - else: - return self.mpl_materials.encryption_context @property def data_key(self) -> DataKey: if hasattr(self, "native_materials"): return self.native_materials.data_key else: - # TODO-MPL This impl is probably wrong + # TODO-MPL This impl is probably wrong, but works for for now + # If this works for all features, great! Remove this comment before launch. + # Otherwise, fix the implementation. return DataKey( key_provider=MasterKeyInfo( provider_id="", @@ -155,11 +144,4 @@ def verification_key(self) -> bytes: if hasattr(self, "native_materials"): return self.native_materials.verification_key else: - print(f"ver {self.mpl_materials.verification_key=}") - return self.mpl_materials.verification_key - - def get_required_encryption_context_keys(self) -> list[str]: - if hasattr(self, "native_materials"): - return [] - else: - return self.mpl_materials.required_encryption_context_keys \ No newline at end of file + return self.mpl_materials.verification_key \ No newline at end of file diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 176b92334..e6cf00635 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -68,12 +68,12 @@ from aws_encryption_sdk.structures import MessageHeader try: import aws_cryptographic_materialproviders - from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.client import AwsCryptographicMaterialProviders - from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.config import MaterialProvidersConfig - from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.models import ( + from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders + from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig + from aws_cryptographic_materialproviders.mpl.models import ( CreateDefaultCryptographicMaterialsManagerInput ) - from aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references import ( + from aws_cryptographic_materialproviders.mpl.references import ( IKeyring, ) from aws_encryption_sdk.cmm_handler import CMMHandler @@ -411,7 +411,7 @@ class EncryptorConfig(_ClientConfig): :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references.IKeyring + aws_cryptographic_materialproviders.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -466,7 +466,7 @@ class StreamEncryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references.IKeyring + aws_cryptographic_materialproviders.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -810,7 +810,7 @@ class DecryptorConfig(_ClientConfig): :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references.IKeyring + aws_cryptographic_materialproviders.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -850,7 +850,7 @@ class StreamDecryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.smithygenerated.aws_cryptography_materialproviders.references.IKeyring + aws_cryptographic_materialproviders.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -1082,7 +1082,7 @@ def close(self): """Closes out the stream.""" _LOGGER.debug("Closing stream") if not hasattr(self, "footer"): - raise SerializationError("Footer not read") + raise SerializationError("Footer not read, message may be corrupted or data key may be incorrect") super(StreamDecryptor, self).close() diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index 157755094..94b22b092 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -767,4 +767,4 @@ def test_close_no_footer(self, mock_close): ) with pytest.raises(SerializationError) as excinfo: test_decryptor.close() - excinfo.match("Footer not read") + excinfo.match("Footer not read, message may be corrupted or data key may be incorrect") From 5b5aa07af75e37d2ad42e7ba850345097d55fcc0 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 2 Feb 2024 15:04:57 -0800 Subject: [PATCH 005/172] changes, cleanup --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index e6cf00635..55be3b917 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -170,7 +170,7 @@ def _exactly_one_arg_is_not_None(*args): try: assert isinstance(self.keyring, IKeyring) except AssertionError as e: - raise ValueError(f"Argument provided to keyring MUST be a {IKeyring}. Found {keyring.__class__.__name__=}") + raise ValueError(f"Argument provided to keyring MUST be a {IKeyring}. Found {self.keyring.__class__.__name__=}") mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( config=MaterialProvidersConfig() From 03e19caff1eaa264873fab500beb8e62c890b583 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 2 Feb 2024 15:19:08 -0800 Subject: [PATCH 006/172] flake8 --- src/aws_encryption_sdk/cmm_handler.py | 31 ++++++---- src/aws_encryption_sdk/materials_handlers.py | 27 +++++---- src/aws_encryption_sdk/streaming_client.py | 62 +++++++++++--------- 3 files changed, 68 insertions(+), 52 deletions(-) diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py index d634dd571..f7f95b0c9 100644 --- a/src/aws_encryption_sdk/cmm_handler.py +++ b/src/aws_encryption_sdk/cmm_handler.py @@ -15,7 +15,7 @@ CommitmentPolicyESDK, AlgorithmSuiteIdESDK, ) -except ImportError as e: +except ImportError: pass from aws_encryption_sdk.exceptions import ( @@ -39,6 +39,7 @@ CommitmentPolicy, ) + # TODO-MPL Should this implement interface..? seems like yes since it implements all of interface methods class CMMHandler(CryptoMaterialsManager): native_cmm: CryptoMaterialsManager @@ -57,7 +58,7 @@ def __init__( self.mpl_cmm = cmm else: raise ValueError(f"Invalid CMM passed to CMMHander: {cmm=}") - + def get_encryption_materials( self, request: EncryptionMaterialsRequest @@ -69,28 +70,32 @@ def get_encryption_materials( return EncryptionMaterialsHandler(self.native_cmm.get_encryption_materials(request)) else: try: - input: GetEncryptionMaterialsInput = CMMHandler._create_mpl_get_encryption_materials_input_from_request(request) + input: GetEncryptionMaterialsInput = CMMHandler._create_mpl_get_encryption_materials_input_from_request( + request + ) output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(input) return EncryptionMaterialsHandler(output.encryption_materials) except AwsCryptographicMaterialProvidersException as e: # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. raise AWSEncryptionSDKClientError(e) - + @staticmethod def _create_mpl_get_encryption_materials_input_from_request( request: EncryptionMaterialsRequest ) -> 'GetEncryptionMaterialsInput': output: GetEncryptionMaterialsInput = GetEncryptionMaterialsInput( encryption_context=request.encryption_context, - commitment_policy=CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy(request.commitment_policy), + commitment_policy=CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy( + request.commitment_policy + ), # TODO double check this # optional... maybe this needs to be kwargs?? # algorithm_suite_id=request.algorithm.algorithm_id, max_plaintext_length=request.plaintext_length, ) return output - + @staticmethod def _map_native_commitment_policy_to_mpl_commitment_policy( native_commitment_policy: CommitmentPolicy @@ -103,7 +108,7 @@ def _map_native_commitment_policy_to_mpl_commitment_policy( return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_REQUIRE_DECRYPT") else: raise ValueError(f"Invalid {native_commitment_policy=}") - + def decrypt_materials( self, request: DecryptionMaterialsRequest @@ -122,12 +127,12 @@ def decrypt_materials( # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. raise AWSEncryptionSDKClientError(e) - + @staticmethod def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> AlgorithmSuiteIdESDK: # MPL algorithm suite ID = hexstr(native_algorithm_id) padded to 4 digits post-`x`. return AlgorithmSuiteIdESDK(f"{native_algorithm_id:#0{6}x}") - + @staticmethod def _create_mpl_decrypt_materials_input_from_request( request: DecryptionMaterialsRequest @@ -139,8 +144,12 @@ def _create_mpl_decrypt_materials_input_from_request( ciphertext=key_blob.encrypted_data_key, ) for key_blob in key_blob_list] output: DecryptMaterialsInput = DecryptMaterialsInput( - algorithm_suite_id=CMMHandler._native_algorithm_id_to_mpl_algorithm_id(request.algorithm.algorithm_id), - commitment_policy=CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy(request.commitment_policy), + algorithm_suite_id=CMMHandler._native_algorithm_id_to_mpl_algorithm_id( + request.algorithm.algorithm_id + ), + commitment_policy=CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy( + request.commitment_policy + ), encrypted_data_keys=list_edks, encryption_context=request.encryption_context, ) diff --git a/src/aws_encryption_sdk/materials_handlers.py b/src/aws_encryption_sdk/materials_handlers.py index 1f34eba03..a03138e78 100644 --- a/src/aws_encryption_sdk/materials_handlers.py +++ b/src/aws_encryption_sdk/materials_handlers.py @@ -5,7 +5,7 @@ EncryptionMaterials as MPL_EncryptionMaterials, EncryptedDataKey as MPL_EncryptedDataKey, ) -except ImportError as e: +except ImportError: pass from aws_encryption_sdk.materials_managers import ( @@ -21,14 +21,13 @@ EncryptedDataKey as Native_EncryptedDataKey, MasterKeyInfo, ) -from aws_encryption_sdk.internal.crypto.authentication import ( - Signer -) + def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str): # MPL algorithm suite ID == hex(native algorithm suite ID) return int(mpl_algorithm_id, 16) + class EncryptionMaterialsHandler: ''' In instances where encryption materials may be provided by either @@ -48,6 +47,7 @@ def __init__( self.mpl_materials = materials else: raise ValueError(f"Invalid EncryptionMaterials passed to EncryptionMaterialsHandler: {materials=}") + @property def algorithm(self) -> Algorithm: if hasattr(self, "native_materials"): @@ -58,14 +58,14 @@ def algorithm(self) -> Algorithm: self.mpl_materials.algorithm_suite.id.value ) ) - + @property def encryption_context(self) -> dict[str, str]: if hasattr(self, "native_materials"): return self.native_materials.encryption_context else: return self.mpl_materials.encryption_context - + @property def encrypted_data_keys(self) -> list[Native_EncryptedDataKey]: if hasattr(self, "native_materials"): @@ -80,7 +80,7 @@ def encrypted_data_keys(self) -> list[Native_EncryptedDataKey]: encrypted_data_key=mpl_edk.ciphertext, ) for mpl_edk in mpl_edk_list} return key_blob_list - + @property def data_encryption_key(self) -> DataKey: if hasattr(self, "native_materials"): @@ -97,16 +97,17 @@ def data_encryption_key(self) -> DataKey: key_info=b'' ), data_key=mpl_dek, - encrypted_data_key=b'', # No encrypted DEK + encrypted_data_key=b'', # No encrypted DEK ) - + @property def signing_key(self) -> bytes: if hasattr(self, "native_materials"): return self.native_materials.signing_key else: return self.mpl_materials.signing_key - + + class DecryptionMaterialsHandler: native_materials: Native_DecryptionMaterials mpl_materials: 'MPL_DecryptionMaterials' @@ -121,7 +122,7 @@ def __init__( self.mpl_materials = materials else: raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler: {materials=}") - + @property def data_key(self) -> DataKey: if hasattr(self, "native_materials"): @@ -138,10 +139,10 @@ def data_key(self) -> DataKey: data_key=self.mpl_materials.plaintext_data_key, encrypted_data_key=b'', ) - + @property def verification_key(self) -> bytes: if hasattr(self, "native_materials"): return self.native_materials.verification_key else: - return self.mpl_materials.verification_key \ No newline at end of file + return self.mpl_materials.verification_key diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 55be3b917..661b3fa21 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -67,7 +67,6 @@ from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import MessageHeader try: - import aws_cryptographic_materialproviders from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.models import ( @@ -77,14 +76,33 @@ IKeyring, ) from aws_encryption_sdk.cmm_handler import CMMHandler - + _has_mpl = True -except ImportError as e: +except ImportError: _has_mpl = False _LOGGER = logging.getLogger(__name__) +def _exactly_one_arg_is_not_None(*args): + ''' + Private helper function. + Returns `True` if exactly one item in the list is not `None`. + Returns `False` otherwise. + ''' + # Have not found any `not None` + found_one = False + for arg in args: + if arg is not None: + if found_one is False: + # Have not already found a `not None`, found a `not None` => only one `not None` (so far) + found_one = True + else: + # Already found a `not None`, found another `not None` => not exactly one `not None` + return False + return found_one + + @attr.s(hash=True) # pylint: disable=too-many-instance-attributes @six.add_metaclass(abc.ABCMeta) class _ClientConfig(object): # pylint: disable=too-many-instance-attributes @@ -140,42 +158,30 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes ) # DEPRECATED: Value is no longer configurable here. Parameter left here to avoid breaking consumers. def _has_mpl_attrs_post_init(self): - - def _exactly_one_arg_is_not_None(*args): - ''' - Private helper function. - Returns `True` if exactly one item in the list is not `None`. - Returns `False` otherwise. - ''' - # Have not found any `not None` - found_one = False - for arg in args: - if arg is not None: - if found_one == False: - # Have not already found a `not None`, found a `not None` => only one `not None` (so far) - found_one = True - else: - # Already found a `not None`, found another `not None` => not exactly one `not None` - return False - return found_one - if not _exactly_one_arg_is_not_None(self.materials_manager, self.key_provider, self.keyring): raise TypeError("Exactly one of keyring, materials_manager, or key_provider must be provided") if self.materials_manager is None: if self.key_provider is not None: - # No CMM, provided (legacy) native `key_provider` => create (legacy) native DefaultCryptoMaterialsManager - self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) + # No CMM, provided legacy native `key_provider` => create legacy native DefaultCryptoMaterialsManager + self.materials_manager = DefaultCryptoMaterialsManager( + master_key_provider=self.key_provider + ) elif self.keyring is not None: # No CMM, provided MPL keyring => create MPL's DefaultCryptographicMaterialsManager try: assert isinstance(self.keyring, IKeyring) - except AssertionError as e: - raise ValueError(f"Argument provided to keyring MUST be a {IKeyring}. Found {self.keyring.__class__.__name__=}") - + except AssertionError: + raise ValueError(f"Argument provided to keyring MUST be a {IKeyring}. \ + Found {self.keyring.__class__.__name__=}") + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( config=MaterialProvidersConfig() ) - cmm = mat_prov.create_default_cryptographic_materials_manager(CreateDefaultCryptographicMaterialsManagerInput(keyring=self.keyring)) + cmm = mat_prov.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=self.keyring + ) + ) cmm_handler: CryptoMaterialsManager = CMMHandler(cmm) self.materials_manager = cmm_handler From b5d33275a462e9311b08359a722496d0737a81be Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 2 Feb 2024 15:41:21 -0800 Subject: [PATCH 007/172] flake8 --- src/aws_encryption_sdk/cmm_handler.py | 25 +++++++-- src/aws_encryption_sdk/materials_handlers.py | 56 ++++++++++++++++++-- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py index f7f95b0c9..bb60a4fa1 100644 --- a/src/aws_encryption_sdk/cmm_handler.py +++ b/src/aws_encryption_sdk/cmm_handler.py @@ -1,3 +1,5 @@ +"""Retrieves encryption/decryption materials from an underlying materials provider.""" + # These dependencies are only loaded if you install the MPL. try: from aws_cryptographic_materialproviders.mpl.errors import ( @@ -42,6 +44,15 @@ # TODO-MPL Should this implement interface..? seems like yes since it implements all of interface methods class CMMHandler(CryptoMaterialsManager): + """ + In instances where encryption materials may be provided by either + an implementation of the native + `aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager` + or an implementation of the MPL's + `aws_cryptographic_materialproviders.mpl.references.ICryptographicMaterialsManager`, + this provides the correct materials based on the underlying materials manager. + """ + native_cmm: CryptoMaterialsManager mpl_cmm: 'ICryptographicMaterialsManager' @@ -57,15 +68,17 @@ def __init__( elif isinstance(cmm, ICryptographicMaterialsManager): self.mpl_cmm = cmm else: - raise ValueError(f"Invalid CMM passed to CMMHander: {cmm=}") + raise ValueError(f"Invalid CMM passed to CMMHandler: {cmm=}") def get_encryption_materials( self, request: EncryptionMaterialsRequest ) -> EncryptionMaterialsHandler: - ''' + """ Returns an EncryptionMaterialsHandler for the configured CMM. - ''' + :param request: Request for encryption materials + """ + if (self._is_using_native_cmm()): return EncryptionMaterialsHandler(self.native_cmm.get_encryption_materials(request)) else: @@ -113,9 +126,11 @@ def decrypt_materials( self, request: DecryptionMaterialsRequest ) -> DecryptionMaterialsHandler: - ''' + """ Returns a DecryptionMaterialsHandler for the configured CMM. - ''' + :param request: Request for decryption materials + """ + if (self._is_using_native_cmm()): return DecryptionMaterialsHandler(self.native_cmm.decrypt_materials(request)) else: diff --git a/src/aws_encryption_sdk/materials_handlers.py b/src/aws_encryption_sdk/materials_handlers.py index a03138e78..180dec3bb 100644 --- a/src/aws_encryption_sdk/materials_handlers.py +++ b/src/aws_encryption_sdk/materials_handlers.py @@ -1,3 +1,4 @@ +"""Provides encryption/decryption materials from an underlying materials provider.""" # These dependencies are only loaded if you install the MPL. try: from aws_cryptographic_materialproviders.mpl.models import ( @@ -29,11 +30,13 @@ def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str): class EncryptionMaterialsHandler: - ''' + """ In instances where encryption materials may be provided by either - the native `aws_encryption_sdk.materials_managers.Native_EncryptionMaterials` - or the MPL's `aws_cryptographic_materialproviders.mpl.models` - ''' + the native `aws_encryption_sdk.materials_managers.EncryptionMaterials` + or the MPL's `aws_cryptographic_materialproviders.mpl.models.EncryptionMaterials`, + this provides the correct materials based on the configured materials provider. + """ + native_materials: Native_EncryptionMaterials mpl_materials: 'MPL_EncryptionMaterials' @@ -41,6 +44,11 @@ def __init__( self, materials: 'Native_EncryptionMaterials | MPL_EncryptionMaterials' ): + """ + Create EncryptionMaterialsHandler. + :param materials: Underlying encryption materials + """ + if isinstance(materials, Native_EncryptionMaterials): self.native_materials = materials elif isinstance(materials, MPL_EncryptionMaterials): @@ -50,6 +58,10 @@ def __init__( @property def algorithm(self) -> Algorithm: + """ + Materials' native Algorithm. + """ + if hasattr(self, "native_materials"): return self.native_materials.algorithm else: @@ -61,6 +73,10 @@ def algorithm(self) -> Algorithm: @property def encryption_context(self) -> dict[str, str]: + """ + Materials' encryption context. + """ + if hasattr(self, "native_materials"): return self.native_materials.encryption_context else: @@ -68,6 +84,10 @@ def encryption_context(self) -> dict[str, str]: @property def encrypted_data_keys(self) -> list[Native_EncryptedDataKey]: + """ + Materials' encrypted data keys. + """ + if hasattr(self, "native_materials"): return self.native_materials.encrypted_data_keys else: @@ -83,6 +103,10 @@ def encrypted_data_keys(self) -> list[Native_EncryptedDataKey]: @property def data_encryption_key(self) -> DataKey: + """ + Materials' data encryption key. + """ + if hasattr(self, "native_materials"): return self.native_materials.data_encryption_key else: @@ -102,6 +126,10 @@ def data_encryption_key(self) -> DataKey: @property def signing_key(self) -> bytes: + """ + Materials' signing key. + """ + if hasattr(self, "native_materials"): return self.native_materials.signing_key else: @@ -109,6 +137,13 @@ def signing_key(self) -> bytes: class DecryptionMaterialsHandler: + """ + In instances where decryption materials may be provided by either + the native `aws_encryption_sdk.materials_managers.DecryptionMaterials` + or the MPL's `aws_cryptographic_materialproviders.mpl.models.DecryptionMaterials`, + this provides the correct materials based on the configured materials provider. + """ + native_materials: Native_DecryptionMaterials mpl_materials: 'MPL_DecryptionMaterials' @@ -116,6 +151,11 @@ def __init__( self, materials: 'Native_DecryptionMaterials | MPL_DecryptionMaterials' ): + """ + Create DecryptionMaterialsHandler. + :param materials: Underlying decryption materials + """ + if isinstance(materials, Native_DecryptionMaterials): self.native_materials = materials elif isinstance(materials, MPL_DecryptionMaterials): @@ -125,6 +165,10 @@ def __init__( @property def data_key(self) -> DataKey: + """ + Materials' data key. + """ + if hasattr(self, "native_materials"): return self.native_materials.data_key else: @@ -142,6 +186,10 @@ def data_key(self) -> DataKey: @property def verification_key(self) -> bytes: + """ + Materials' verification key. + """ + if hasattr(self, "native_materials"): return self.native_materials.verification_key else: From b13cd191a8874dc0091e844c4cb0789d81591764 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 2 Feb 2024 15:45:55 -0800 Subject: [PATCH 008/172] flake8 --- src/aws_encryption_sdk/cmm_handler.py | 7 ++-- src/aws_encryption_sdk/materials_handlers.py | 37 ++++---------------- src/aws_encryption_sdk/streaming_client.py | 4 +-- 3 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py index bb60a4fa1..17d59792a 100644 --- a/src/aws_encryption_sdk/cmm_handler.py +++ b/src/aws_encryption_sdk/cmm_handler.py @@ -63,6 +63,11 @@ def __init__( self, cmm: 'CryptoMaterialsManager | ICryptographicMaterialsManager' ): + """ + Create DecryptionMaterialsHandler. + :param cmm: Underlying cryptographic materials manager + """ + if isinstance(cmm, CryptoMaterialsManager): self.native_cmm = cmm elif isinstance(cmm, ICryptographicMaterialsManager): @@ -78,7 +83,6 @@ def get_encryption_materials( Returns an EncryptionMaterialsHandler for the configured CMM. :param request: Request for encryption materials """ - if (self._is_using_native_cmm()): return EncryptionMaterialsHandler(self.native_cmm.get_encryption_materials(request)) else: @@ -130,7 +134,6 @@ def decrypt_materials( Returns a DecryptionMaterialsHandler for the configured CMM. :param request: Request for decryption materials """ - if (self._is_using_native_cmm()): return DecryptionMaterialsHandler(self.native_cmm.decrypt_materials(request)) else: diff --git a/src/aws_encryption_sdk/materials_handlers.py b/src/aws_encryption_sdk/materials_handlers.py index 180dec3bb..d54e4517b 100644 --- a/src/aws_encryption_sdk/materials_handlers.py +++ b/src/aws_encryption_sdk/materials_handlers.py @@ -48,7 +48,6 @@ def __init__( Create EncryptionMaterialsHandler. :param materials: Underlying encryption materials """ - if isinstance(materials, Native_EncryptionMaterials): self.native_materials = materials elif isinstance(materials, MPL_EncryptionMaterials): @@ -58,10 +57,7 @@ def __init__( @property def algorithm(self) -> Algorithm: - """ - Materials' native Algorithm. - """ - + """Materials' native Algorithm.""" if hasattr(self, "native_materials"): return self.native_materials.algorithm else: @@ -73,10 +69,7 @@ def algorithm(self) -> Algorithm: @property def encryption_context(self) -> dict[str, str]: - """ - Materials' encryption context. - """ - + """Materials' encryption context.""" if hasattr(self, "native_materials"): return self.native_materials.encryption_context else: @@ -84,10 +77,7 @@ def encryption_context(self) -> dict[str, str]: @property def encrypted_data_keys(self) -> list[Native_EncryptedDataKey]: - """ - Materials' encrypted data keys. - """ - + """Materials' encrypted data keys.""" if hasattr(self, "native_materials"): return self.native_materials.encrypted_data_keys else: @@ -103,10 +93,7 @@ def encrypted_data_keys(self) -> list[Native_EncryptedDataKey]: @property def data_encryption_key(self) -> DataKey: - """ - Materials' data encryption key. - """ - + """Materials' data encryption key.""" if hasattr(self, "native_materials"): return self.native_materials.data_encryption_key else: @@ -126,10 +113,7 @@ def data_encryption_key(self) -> DataKey: @property def signing_key(self) -> bytes: - """ - Materials' signing key. - """ - + """Materials' signing key.""" if hasattr(self, "native_materials"): return self.native_materials.signing_key else: @@ -155,7 +139,6 @@ def __init__( Create DecryptionMaterialsHandler. :param materials: Underlying decryption materials """ - if isinstance(materials, Native_DecryptionMaterials): self.native_materials = materials elif isinstance(materials, MPL_DecryptionMaterials): @@ -165,10 +148,7 @@ def __init__( @property def data_key(self) -> DataKey: - """ - Materials' data key. - """ - + """Materials' data key.""" if hasattr(self, "native_materials"): return self.native_materials.data_key else: @@ -186,10 +166,7 @@ def data_key(self) -> DataKey: @property def verification_key(self) -> bytes: - """ - Materials' verification key. - """ - + """Materials' verification key.""" if hasattr(self, "native_materials"): return self.native_materials.verification_key else: diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 661b3fa21..ec19b6dd5 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -85,11 +85,11 @@ def _exactly_one_arg_is_not_None(*args): - ''' + """ Private helper function. Returns `True` if exactly one item in the list is not `None`. Returns `False` otherwise. - ''' + """ # Have not found any `not None` found_one = False for arg in args: From 51065cb186bb3e649117a3a9871b2b5943066a52 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 2 Feb 2024 15:48:34 -0800 Subject: [PATCH 009/172] flake8 --- setup.py | 4 +++- src/aws_encryption_sdk/cmm_handler.py | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index c4c277096..353781800 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,9 @@ def get_requirements(): install_requires=get_requirements(), # TODO: Point at MPL main branch once Python MPL is merged into main. extras_require={ - "MPL": ["aws-cryptographic-material-providers @ git+https://github.com/aws/aws-cryptographic-material-providers-library.git@lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python"], + "MPL": ["aws-cryptographic-material-providers @\ + git+https://github.com/aws/aws-cryptographic-material-providers-library.git@\ + lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python"], }, classifiers=[ "Development Status :: 5 - Production/Stable", diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py index 17d59792a..887d9d79e 100644 --- a/src/aws_encryption_sdk/cmm_handler.py +++ b/src/aws_encryption_sdk/cmm_handler.py @@ -67,7 +67,6 @@ def __init__( Create DecryptionMaterialsHandler. :param cmm: Underlying cryptographic materials manager """ - if isinstance(cmm, CryptoMaterialsManager): self.native_cmm = cmm elif isinstance(cmm, ICryptographicMaterialsManager): From fc4d254d7f7601d86fc9954ed69d0817869a43e2 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 2 Feb 2024 15:51:15 -0800 Subject: [PATCH 010/172] flake8 --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 353781800..4cd8027cd 100644 --- a/setup.py +++ b/setup.py @@ -41,9 +41,9 @@ def get_requirements(): install_requires=get_requirements(), # TODO: Point at MPL main branch once Python MPL is merged into main. extras_require={ - "MPL": ["aws-cryptographic-material-providers @\ - git+https://github.com/aws/aws-cryptographic-material-providers-library.git@\ - lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python"], + "MPL": ["aws-cryptographic-material-providers @" \ + "git+https://github.com/aws/aws-cryptographic-material-providers-library.git@" \ + "lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python"], }, classifiers=[ "Development Status :: 5 - Production/Stable", From a8e52d310c777905326fc98cbf667217116de303 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 5 Feb 2024 13:23:21 -0800 Subject: [PATCH 011/172] fix pem/der --- .../internal/crypto/authentication.py | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index b9692eb16..8c1b9af31 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -68,25 +68,31 @@ class Signer(_PrehashingAuthenticator): """ @classmethod - def from_key_bytes(cls, algorithm, key_bytes): + def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DER): """Builds a `Signer` from an algorithm suite and a raw signing key. :param algorithm: Algorithm on which to base signer :type algorithm: aws_encryption_sdk.identifiers.Algorithm :param bytes key_bytes: Raw signing key + :param encoding: Encoding used for key bytes + :type encoding: cryptography.hazmat.primitives.serialization.encoding :rtype: aws_encryption_sdk.internal.crypto.Signer """ - # key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) - key = serialization.load_pem_private_key(data=key_bytes, password=None, backend=default_backend()) + if encoding == serialization.Encoding.DER: + key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) + elif encoding == serialization.Encoding.PEM: + key = serialization.load_pem_private_key(data=key_bytes, password=None, backend=default_backend()) + else: + raise ValueError("Unsupported signing key encoding: {}".format(encoding)) return cls(algorithm, key) - def key_bytes(self): + def key_bytes(self, encoding=serialization.Encoding.DER): """Returns the raw signing key. :rtype: bytes """ return self.key.private_bytes( - encoding=serialization.Encoding.DER, + encoding=encoding, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ) @@ -149,19 +155,27 @@ def from_encoded_point(cls, algorithm, encoded_point): ) @classmethod - def from_key_bytes(cls, algorithm, key_bytes): + def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DER): """Creates a `Verifier` object based on the supplied algorithm and raw verification key. :param algorithm: Algorithm on which to base verifier :type algorithm: aws_encryption_sdk.identifiers.Algorithm :param bytes encoded_point: Raw verification key + :param encoding: Encoding used for key bytes + :type encoding: cryptography.hazmat.primitives.serialization.encoding :returns: Instance of Verifier generated from encoded point :rtype: aws_encryption_sdk.internal.crypto.Verifier """ + if encoding == serialization.Encoding.DER: + key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) + elif encoding == serialization.Encoding.PEM: + key = serialization.load_pem_private_key(data=key_bytes, password=None, backend=default_backend()) + else: + raise ValueError("Unsupported verification key encoding: {}".format(encoding)) return cls( algorithm=algorithm, key=serialization.load_pem_public_key(data=key_bytes, backend=default_backend()) ) - + def key_bytes(self): """Returns the raw verification key. From 6f5504741a4797ee1cce5986a5ba48718759b810 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 5 Feb 2024 13:30:49 -0800 Subject: [PATCH 012/172] fix pem/der --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index ec19b6dd5..f6b3529a9 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -172,7 +172,7 @@ def _has_mpl_attrs_post_init(self): assert isinstance(self.keyring, IKeyring) except AssertionError: raise ValueError(f"Argument provided to keyring MUST be a {IKeyring}. \ - Found {self.keyring.__class__.__name__=}") + Found {self.keyring.__class__.__name__}") mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( config=MaterialProvidersConfig() From 1b1b4e4bc61d7c74a3815d225e992c1cb7bce135 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 5 Feb 2024 16:11:08 -0800 Subject: [PATCH 013/172] debug --- src/aws_encryption_sdk/internal/crypto/authentication.py | 2 +- src/aws_encryption_sdk/streaming_client.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index 8c1b9af31..5e8dcd10c 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -173,7 +173,7 @@ def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DE else: raise ValueError("Unsupported verification key encoding: {}".format(encoding)) return cls( - algorithm=algorithm, key=serialization.load_pem_public_key(data=key_bytes, backend=default_backend()) + algorithm=algorithm, key=key ) def key_bytes(self): diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index f6b3529a9..83e4a9ecd 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -165,12 +165,10 @@ def _has_mpl_attrs_post_init(self): # No CMM, provided legacy native `key_provider` => create legacy native DefaultCryptoMaterialsManager self.materials_manager = DefaultCryptoMaterialsManager( master_key_provider=self.key_provider - ) + ) elif self.keyring is not None: # No CMM, provided MPL keyring => create MPL's DefaultCryptographicMaterialsManager - try: - assert isinstance(self.keyring, IKeyring) - except AssertionError: + if not isinstance(self.keyring, IKeyring): raise ValueError(f"Argument provided to keyring MUST be a {IKeyring}. \ Found {self.keyring.__class__.__name__}") From 38a4cc9c6808d5da025822634e8ec9bb0b9f960f Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 5 Feb 2024 16:14:15 -0800 Subject: [PATCH 014/172] debug --- src/aws_encryption_sdk/internal/crypto/authentication.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index 5e8dcd10c..80469ff1f 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -167,9 +167,9 @@ def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DE :rtype: aws_encryption_sdk.internal.crypto.Verifier """ if encoding == serialization.Encoding.DER: - key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) + key = serialization.load_der_private_key(data=key_bytes, backend=default_backend()) elif encoding == serialization.Encoding.PEM: - key = serialization.load_pem_private_key(data=key_bytes, password=None, backend=default_backend()) + key = serialization.load_pem_private_key(data=key_bytes, backend=default_backend()) else: raise ValueError("Unsupported verification key encoding: {}".format(encoding)) return cls( From 0cd0e2301fc7133e8023a825f128bc457af311aa Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 6 Feb 2024 15:47:48 -0800 Subject: [PATCH 015/172] fix --- .../internal/crypto/authentication.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index 80469ff1f..88d21a2ef 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -78,12 +78,7 @@ def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DE :type encoding: cryptography.hazmat.primitives.serialization.encoding :rtype: aws_encryption_sdk.internal.crypto.Signer """ - if encoding == serialization.Encoding.DER: - key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) - elif encoding == serialization.Encoding.PEM: - key = serialization.load_pem_private_key(data=key_bytes, password=None, backend=default_backend()) - else: - raise ValueError("Unsupported signing key encoding: {}".format(encoding)) + key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) return cls(algorithm, key) def key_bytes(self, encoding=serialization.Encoding.DER): @@ -166,14 +161,8 @@ def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DE :returns: Instance of Verifier generated from encoded point :rtype: aws_encryption_sdk.internal.crypto.Verifier """ - if encoding == serialization.Encoding.DER: - key = serialization.load_der_private_key(data=key_bytes, backend=default_backend()) - elif encoding == serialization.Encoding.PEM: - key = serialization.load_pem_private_key(data=key_bytes, backend=default_backend()) - else: - raise ValueError("Unsupported verification key encoding: {}".format(encoding)) return cls( - algorithm=algorithm, key=key + algorithm=algorithm, key=serialization.load_der_public_key(data=key_bytes, backend=default_backend()) ) def key_bytes(self): From 44826a2568fd3fa86d3031d11837c707d17850e0 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 6 Feb 2024 16:09:14 -0800 Subject: [PATCH 016/172] fix --- .../internal/crypto/authentication.py | 14 +++++--------- src/aws_encryption_sdk/streaming_client.py | 3 ++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index 88d21a2ef..f90ac77e0 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -68,26 +68,24 @@ class Signer(_PrehashingAuthenticator): """ @classmethod - def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DER): + def from_key_bytes(cls, algorithm, key_bytes): """Builds a `Signer` from an algorithm suite and a raw signing key. :param algorithm: Algorithm on which to base signer :type algorithm: aws_encryption_sdk.identifiers.Algorithm :param bytes key_bytes: Raw signing key - :param encoding: Encoding used for key bytes - :type encoding: cryptography.hazmat.primitives.serialization.encoding :rtype: aws_encryption_sdk.internal.crypto.Signer """ key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) return cls(algorithm, key) - def key_bytes(self, encoding=serialization.Encoding.DER): + def key_bytes(self): """Returns the raw signing key. :rtype: bytes """ return self.key.private_bytes( - encoding=encoding, + encoding=serialization.Encoding.DER, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ) @@ -150,21 +148,19 @@ def from_encoded_point(cls, algorithm, encoded_point): ) @classmethod - def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DER): + def from_key_bytes(cls, algorithm, key_bytes): """Creates a `Verifier` object based on the supplied algorithm and raw verification key. :param algorithm: Algorithm on which to base verifier :type algorithm: aws_encryption_sdk.identifiers.Algorithm :param bytes encoded_point: Raw verification key - :param encoding: Encoding used for key bytes - :type encoding: cryptography.hazmat.primitives.serialization.encoding :returns: Instance of Verifier generated from encoded point :rtype: aws_encryption_sdk.internal.crypto.Verifier """ return cls( algorithm=algorithm, key=serialization.load_der_public_key(data=key_bytes, backend=default_backend()) ) - + def key_bytes(self): """Returns the raw verification key. diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 83e4a9ecd..582472025 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -919,7 +919,8 @@ def _read_header(self): # MPL verification key is NOT key bytes, it is bytes of the compressed point # TODO-MPL: clean this up, least-privilege violation import base64 - if hasattr(self.config.materials_manager, "mpl_cmm"): + if (isinstance(self.config.materials_manager, CMMHandler) + and hasattr(self.config.materials_manager, "mpl_cmm")): self.verifier = Verifier.from_encoded_point( algorithm=header.algorithm, encoded_point=base64.b64encode(decryption_materials.verification_key) From 02e9f843826506597ca03a3139da1b58a88da2f6 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 6 Feb 2024 16:27:51 -0800 Subject: [PATCH 017/172] fix --- src/aws_encryption_sdk/cmm_handler.py | 13 ++++++++----- src/aws_encryption_sdk/materials_handlers.py | 12 +++++++----- src/aws_encryption_sdk/streaming_client.py | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py index 887d9d79e..fa1786837 100644 --- a/src/aws_encryption_sdk/cmm_handler.py +++ b/src/aws_encryption_sdk/cmm_handler.py @@ -17,9 +17,12 @@ CommitmentPolicyESDK, AlgorithmSuiteIdESDK, ) + except ImportError: pass +from typing import List + from aws_encryption_sdk.exceptions import ( AWSEncryptionSDKClientError, ) @@ -72,7 +75,7 @@ def __init__( elif isinstance(cmm, ICryptographicMaterialsManager): self.mpl_cmm = cmm else: - raise ValueError(f"Invalid CMM passed to CMMHandler: {cmm=}") + raise ValueError(f"Invalid CMM passed to CMMHandler. cmm: {cmm}") def get_encryption_materials( self, @@ -115,7 +118,7 @@ def _create_mpl_get_encryption_materials_input_from_request( @staticmethod def _map_native_commitment_policy_to_mpl_commitment_policy( native_commitment_policy: CommitmentPolicy - ) -> CommitmentPolicyESDK: + ) -> 'CommitmentPolicyESDK': if native_commitment_policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT: return CommitmentPolicyESDK(value="FORBID_ENCRYPT_ALLOW_DECRYPT") elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT: @@ -123,7 +126,7 @@ def _map_native_commitment_policy_to_mpl_commitment_policy( elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT: return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_REQUIRE_DECRYPT") else: - raise ValueError(f"Invalid {native_commitment_policy=}") + raise ValueError(f"Invalid native_commitment_policy: {native_commitment_policy}") def decrypt_materials( self, @@ -146,7 +149,7 @@ def decrypt_materials( raise AWSEncryptionSDKClientError(e) @staticmethod - def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> AlgorithmSuiteIdESDK: + def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> 'AlgorithmSuiteIdESDK': # MPL algorithm suite ID = hexstr(native_algorithm_id) padded to 4 digits post-`x`. return AlgorithmSuiteIdESDK(f"{native_algorithm_id:#0{6}x}") @@ -154,7 +157,7 @@ def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> Algori def _create_mpl_decrypt_materials_input_from_request( request: DecryptionMaterialsRequest ) -> 'DecryptMaterialsInput': - key_blob_list: list[Native_EncryptedDataKey] = request.encrypted_data_keys + key_blob_list: List[Native_EncryptedDataKey] = request.encrypted_data_keys list_edks = [MPL_EncryptedDataKey( key_provider_id=key_blob.key_provider.provider_id, key_provider_info=key_blob.key_provider.key_info, diff --git a/src/aws_encryption_sdk/materials_handlers.py b/src/aws_encryption_sdk/materials_handlers.py index d54e4517b..970963e10 100644 --- a/src/aws_encryption_sdk/materials_handlers.py +++ b/src/aws_encryption_sdk/materials_handlers.py @@ -9,6 +9,8 @@ except ImportError: pass +from typing import Dict, List + from aws_encryption_sdk.materials_managers import ( DecryptionMaterials as Native_DecryptionMaterials, EncryptionMaterials as Native_EncryptionMaterials, @@ -53,7 +55,7 @@ def __init__( elif isinstance(materials, MPL_EncryptionMaterials): self.mpl_materials = materials else: - raise ValueError(f"Invalid EncryptionMaterials passed to EncryptionMaterialsHandler: {materials=}") + raise ValueError(f"Invalid EncryptionMaterials passed to EncryptionMaterialsHandler. materials: {materials}") @property def algorithm(self) -> Algorithm: @@ -68,7 +70,7 @@ def algorithm(self) -> Algorithm: ) @property - def encryption_context(self) -> dict[str, str]: + def encryption_context(self) -> Dict[str, str]: """Materials' encryption context.""" if hasattr(self, "native_materials"): return self.native_materials.encryption_context @@ -76,12 +78,12 @@ def encryption_context(self) -> dict[str, str]: return self.mpl_materials.encryption_context @property - def encrypted_data_keys(self) -> list[Native_EncryptedDataKey]: + def encrypted_data_keys(self) -> List[Native_EncryptedDataKey]: """Materials' encrypted data keys.""" if hasattr(self, "native_materials"): return self.native_materials.encrypted_data_keys else: - mpl_edk_list: list[MPL_EncryptedDataKey] = self.mpl_materials.encrypted_data_keys + mpl_edk_list: List[MPL_EncryptedDataKey] = self.mpl_materials.encrypted_data_keys key_blob_list: set[Native_EncryptedDataKey] = {Native_EncryptedDataKey( key_provider=MasterKeyInfo( provider_id=mpl_edk.key_provider_id, @@ -144,7 +146,7 @@ def __init__( elif isinstance(materials, MPL_DecryptionMaterials): self.mpl_materials = materials else: - raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler: {materials=}") + raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler. materials: {materials}") @property def data_key(self) -> DataKey: diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 582472025..6a2dc1d27 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -66,6 +66,7 @@ from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import MessageHeader +from aws_encryption_sdk.cmm_handler import CMMHandler try: from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig @@ -75,7 +76,6 @@ from aws_cryptographic_materialproviders.mpl.references import ( IKeyring, ) - from aws_encryption_sdk.cmm_handler import CMMHandler _has_mpl = True except ImportError: From a3babfd2ef936cf4595bdaab041fc16e7a005868 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 6 Feb 2024 17:55:40 -0800 Subject: [PATCH 018/172] linter --- src/aws_encryption_sdk/cmm_handler.py | 36 +++++++++----------- src/aws_encryption_sdk/materials_handlers.py | 10 +++--- src/aws_encryption_sdk/streaming_client.py | 18 +++++----- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py index fa1786837..20298f801 100644 --- a/src/aws_encryption_sdk/cmm_handler.py +++ b/src/aws_encryption_sdk/cmm_handler.py @@ -85,38 +85,35 @@ def get_encryption_materials( Returns an EncryptionMaterialsHandler for the configured CMM. :param request: Request for encryption materials """ - if (self._is_using_native_cmm()): + if self._is_using_native_cmm(): return EncryptionMaterialsHandler(self.native_cmm.get_encryption_materials(request)) else: try: - input: GetEncryptionMaterialsInput = CMMHandler._create_mpl_get_encryption_materials_input_from_request( + mpl_input: GetEncryptionMaterialsInput = CMMHandler._native_to_mpl_get_encryption_materials( request ) - output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(input) - return EncryptionMaterialsHandler(output.encryption_materials) - except AwsCryptographicMaterialProvidersException as e: + mpl_output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) + return EncryptionMaterialsHandler(mpl_output.encryption_materials) + except AwsCryptographicMaterialProvidersException as mpl_exception: # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. - raise AWSEncryptionSDKClientError(e) + raise AWSEncryptionSDKClientError(mpl_exception) @staticmethod - def _create_mpl_get_encryption_materials_input_from_request( + def _native_to_mpl_get_encryption_materials( request: EncryptionMaterialsRequest ) -> 'GetEncryptionMaterialsInput': output: GetEncryptionMaterialsInput = GetEncryptionMaterialsInput( encryption_context=request.encryption_context, - commitment_policy=CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy( + commitment_policy=CMMHandler._native_to_mpl_commmitment_policy( request.commitment_policy ), - # TODO double check this - # optional... maybe this needs to be kwargs?? - # algorithm_suite_id=request.algorithm.algorithm_id, max_plaintext_length=request.plaintext_length, ) return output @staticmethod - def _map_native_commitment_policy_to_mpl_commitment_policy( + def _native_to_mpl_commmitment_policy( native_commitment_policy: CommitmentPolicy ) -> 'CommitmentPolicyESDK': if native_commitment_policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT: @@ -136,17 +133,18 @@ def decrypt_materials( Returns a DecryptionMaterialsHandler for the configured CMM. :param request: Request for decryption materials """ - if (self._is_using_native_cmm()): + if self._is_using_native_cmm(): return DecryptionMaterialsHandler(self.native_cmm.decrypt_materials(request)) else: try: - input: 'DecryptMaterialsInput' = CMMHandler._create_mpl_decrypt_materials_input_from_request(request) - output: 'DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(input) - return DecryptionMaterialsHandler(output.decryption_materials) - except AwsCryptographicMaterialProvidersException as e: + mpl_input: 'DecryptMaterialsInput' = \ + CMMHandler._create_mpl_decrypt_materials_input_from_request(request) + mpl_output: 'DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(mpl_input) + return DecryptionMaterialsHandler(mpl_output.decryption_materials) + except AwsCryptographicMaterialProvidersException as mpl_exception: # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. - raise AWSEncryptionSDKClientError(e) + raise AWSEncryptionSDKClientError(mpl_exception) @staticmethod def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> 'AlgorithmSuiteIdESDK': @@ -167,7 +165,7 @@ def _create_mpl_decrypt_materials_input_from_request( algorithm_suite_id=CMMHandler._native_algorithm_id_to_mpl_algorithm_id( request.algorithm.algorithm_id ), - commitment_policy=CMMHandler._map_native_commitment_policy_to_mpl_commitment_policy( + commitment_policy=CMMHandler._native_to_mpl_commmitment_policy( request.commitment_policy ), encrypted_data_keys=list_edks, diff --git a/src/aws_encryption_sdk/materials_handlers.py b/src/aws_encryption_sdk/materials_handlers.py index 970963e10..00d67ed71 100644 --- a/src/aws_encryption_sdk/materials_handlers.py +++ b/src/aws_encryption_sdk/materials_handlers.py @@ -9,7 +9,7 @@ except ImportError: pass -from typing import Dict, List +from typing import Dict, List, Set from aws_encryption_sdk.materials_managers import ( DecryptionMaterials as Native_DecryptionMaterials, @@ -55,7 +55,8 @@ def __init__( elif isinstance(materials, MPL_EncryptionMaterials): self.mpl_materials = materials else: - raise ValueError(f"Invalid EncryptionMaterials passed to EncryptionMaterialsHandler. materials: {materials}") + raise ValueError(f"Invalid EncryptionMaterials passed to EncryptionMaterialsHandler.\ + materials: {materials}") @property def algorithm(self) -> Algorithm: @@ -84,7 +85,7 @@ def encrypted_data_keys(self) -> List[Native_EncryptedDataKey]: return self.native_materials.encrypted_data_keys else: mpl_edk_list: List[MPL_EncryptedDataKey] = self.mpl_materials.encrypted_data_keys - key_blob_list: set[Native_EncryptedDataKey] = {Native_EncryptedDataKey( + key_blob_list: Set[Native_EncryptedDataKey] = {Native_EncryptedDataKey( key_provider=MasterKeyInfo( provider_id=mpl_edk.key_provider_id, key_info=mpl_edk.key_provider_info, @@ -146,7 +147,8 @@ def __init__( elif isinstance(materials, MPL_DecryptionMaterials): self.mpl_materials = materials else: - raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler. materials: {materials}") + raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler.\ + materials: {materials}") @property def data_key(self) -> DataKey: diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 6a2dc1d27..6b977e6e4 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -18,6 +18,7 @@ import io import logging import math +import base64 import attr import six @@ -77,14 +78,14 @@ IKeyring, ) - _has_mpl = True + HAS_MPL = True except ImportError: - _has_mpl = False + HAS_MPL = False _LOGGER = logging.getLogger(__name__) -def _exactly_one_arg_is_not_None(*args): +def _exactly_one_arg_is_not_none(*args): """ Private helper function. Returns `True` if exactly one item in the list is not `None`. @@ -146,7 +147,7 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes key_provider = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(MasterKeyProvider)) ) - if _has_mpl: + if HAS_MPL: keyring = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(IKeyring)) ) @@ -158,14 +159,14 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes ) # DEPRECATED: Value is no longer configurable here. Parameter left here to avoid breaking consumers. def _has_mpl_attrs_post_init(self): - if not _exactly_one_arg_is_not_None(self.materials_manager, self.key_provider, self.keyring): + if not _exactly_one_arg_is_not_none(self.materials_manager, self.key_provider, self.keyring): raise TypeError("Exactly one of keyring, materials_manager, or key_provider must be provided") if self.materials_manager is None: if self.key_provider is not None: # No CMM, provided legacy native `key_provider` => create legacy native DefaultCryptoMaterialsManager self.materials_manager = DefaultCryptoMaterialsManager( master_key_provider=self.key_provider - ) + ) elif self.keyring is not None: # No CMM, provided MPL keyring => create MPL's DefaultCryptographicMaterialsManager if not isinstance(self.keyring, IKeyring): @@ -194,9 +195,9 @@ def _no_mpl_attrs_post_init(self): def __attrs_post_init__(self): """Normalize inputs to crypto material manager.""" - if _has_mpl: + if HAS_MPL: self._has_mpl_attrs_post_init() - elif not _has_mpl: + elif not HAS_MPL: self._no_mpl_attrs_post_init() @@ -918,7 +919,6 @@ def _read_header(self): else: # MPL verification key is NOT key bytes, it is bytes of the compressed point # TODO-MPL: clean this up, least-privilege violation - import base64 if (isinstance(self.config.materials_manager, CMMHandler) and hasattr(self.config.materials_manager, "mpl_cmm")): self.verifier = Verifier.from_encoded_point( From d2c974afc11a0f11a3ab32ad44e17061d640c5d1 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 6 Feb 2024 17:59:21 -0800 Subject: [PATCH 019/172] linter --- setup.py | 1 + src/aws_encryption_sdk/cmm_handler.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/setup.py b/setup.py index 4cd8027cd..084edc09a 100644 --- a/setup.py +++ b/setup.py @@ -39,6 +39,7 @@ def get_requirements(): keywords="aws-encryption-sdk aws kms encryption", license="Apache License 2.0", install_requires=get_requirements(), + # pylint: disable=fixme # TODO: Point at MPL main branch once Python MPL is merged into main. extras_require={ "MPL": ["aws-cryptographic-material-providers @" \ diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py index 20298f801..2479038a1 100644 --- a/src/aws_encryption_sdk/cmm_handler.py +++ b/src/aws_encryption_sdk/cmm_handler.py @@ -2,6 +2,8 @@ # These dependencies are only loaded if you install the MPL. try: + # pylint seems to struggle with this condition import + # pylint: disable=unused-import from aws_cryptographic_materialproviders.mpl.errors import ( AwsCryptographicMaterialProvidersException ) From 55b24a83580f880278d0689f57fbfe1bdca285f6 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 09:44:02 -0800 Subject: [PATCH 020/172] isort --- examples/src/basic_encryption.py | 5 --- examples/src/keyrings/hierarchical_keyring.py | 32 +++++--------- src/aws_encryption_sdk/cmm_handler.py | 44 ++++++------------- src/aws_encryption_sdk/materials_handlers.py | 13 ++---- src/aws_encryption_sdk/streaming_client.py | 13 +++--- 5 files changed, 32 insertions(+), 75 deletions(-) diff --git a/examples/src/basic_encryption.py b/examples/src/basic_encryption.py index 7b729feab..cfe8ac791 100644 --- a/examples/src/basic_encryption.py +++ b/examples/src/basic_encryption.py @@ -51,8 +51,3 @@ def cycle_string(key_arn, source_plaintext, botocore_session=None): assert all( pair in decrypted_header.encryption_context.items() for pair in encryptor_header.encryption_context.items() ) - -cycle_string( - "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f", - "abcdefg", -) \ No newline at end of file diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index e8f662b73..20647bed6 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -4,43 +4,32 @@ """Example showing basic encryption and decryption of a value already in memory.""" -import aws_encryption_sdk -from aws_encryption_sdk import CommitmentPolicy -import boto3 - import sys -from aws_encryption_sdk.exceptions import ( - AWSEncryptionSDKClientError, - SerializationError, -) +import boto3 + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError, SerializationError module_root_dir = '/'.join(__file__.split("/")[:-1]) sys.path.append(module_root_dir) import aws_cryptographic_materialproviders - +from aws_cryptographic_materialproviders.keystore.client import KeyStore +from aws_cryptographic_materialproviders.keystore.config import KeyStoreConfig +from aws_cryptographic_materialproviders.keystore.models import CreateKeyInput, KMSConfigurationKmsKeyArn from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.models import ( - CreateAwsKmsHierarchicalKeyringInput, CacheTypeDefault, + CreateAwsKmsHierarchicalKeyringInput, DefaultCache, GetBranchKeyIdInput, GetBranchKeyIdOutput, ) -from aws_cryptographic_materialproviders.mpl.references import ( - IKeyring, - IBranchKeyIdSupplier, -) - -from aws_cryptographic_materialproviders.keystore.client import KeyStore -from aws_cryptographic_materialproviders.keystore.config import KeyStoreConfig -from aws_cryptographic_materialproviders.keystore.models import ( - CreateKeyInput, - KMSConfigurationKmsKeyArn, -) +from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier, IKeyring EXAMPLE_DATA: bytes = b"Hello World" @@ -241,6 +230,7 @@ def get_branch_key_id( # hack in a test import botocore + encrypt_and_decrypt_with_keyring( "KeyStoreDdbTable", "KeyStoreDdbTable", diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py index 2479038a1..5bac15b87 100644 --- a/src/aws_encryption_sdk/cmm_handler.py +++ b/src/aws_encryption_sdk/cmm_handler.py @@ -2,49 +2,31 @@ # These dependencies are only loaded if you install the MPL. try: - # pylint seems to struggle with this condition import + # pylint seems to struggle with this conditional import # pylint: disable=unused-import - from aws_cryptographic_materialproviders.mpl.errors import ( - AwsCryptographicMaterialProvidersException - ) - from aws_cryptographic_materialproviders.mpl.references import ( - ICryptographicMaterialsManager, - ) + from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException from aws_cryptographic_materialproviders.mpl.models import ( - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, DecryptMaterialsInput, DecryptMaterialsOutput, EncryptedDataKey as MPL_EncryptedDataKey, - CommitmentPolicyESDK, - AlgorithmSuiteIdESDK, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, ) + from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager except ImportError: pass from typing import List -from aws_encryption_sdk.exceptions import ( - AWSEncryptionSDKClientError, -) -from aws_encryption_sdk.materials_managers import ( - DecryptionMaterialsRequest, - EncryptionMaterialsRequest, -) -from aws_encryption_sdk.materials_managers.base import ( - CryptoMaterialsManager, -) -from aws_encryption_sdk.materials_handlers import ( - EncryptionMaterialsHandler, - DecryptionMaterialsHandler, -) -from aws_encryption_sdk.structures import ( - EncryptedDataKey as Native_EncryptedDataKey, -) -from aws_encryption_sdk.identifiers import ( - CommitmentPolicy, -) +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.identifiers import CommitmentPolicy +from aws_encryption_sdk.materials_handlers import DecryptionMaterialsHandler, EncryptionMaterialsHandler +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey # TODO-MPL Should this implement interface..? seems like yes since it implements all of interface methods diff --git a/src/aws_encryption_sdk/materials_handlers.py b/src/aws_encryption_sdk/materials_handlers.py index 00d67ed71..57f54144e 100644 --- a/src/aws_encryption_sdk/materials_handlers.py +++ b/src/aws_encryption_sdk/materials_handlers.py @@ -3,27 +3,20 @@ try: from aws_cryptographic_materialproviders.mpl.models import ( DecryptionMaterials as MPL_DecryptionMaterials, - EncryptionMaterials as MPL_EncryptionMaterials, EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, ) except ImportError: pass from typing import Dict, List, Set +from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite from aws_encryption_sdk.materials_managers import ( DecryptionMaterials as Native_DecryptionMaterials, EncryptionMaterials as Native_EncryptionMaterials, ) -from aws_encryption_sdk.identifiers import ( - Algorithm, - AlgorithmSuite, -) -from aws_encryption_sdk.structures import ( - DataKey, - EncryptedDataKey as Native_EncryptedDataKey, - MasterKeyInfo, -) +from aws_encryption_sdk.structures import DataKey, EncryptedDataKey as Native_EncryptedDataKey, MasterKeyInfo def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str): diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 6b977e6e4..afe9987ff 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -14,16 +14,17 @@ from __future__ import division import abc +import base64 import hmac import io import logging import math -import base64 import attr import six import aws_encryption_sdk.internal.utils +from aws_encryption_sdk.cmm_handler import CMMHandler from aws_encryption_sdk.exceptions import ( ActionNotAllowedError, AWSEncryptionSDKClientError, @@ -67,16 +68,12 @@ from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import MessageHeader -from aws_encryption_sdk.cmm_handler import CMMHandler + try: from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig - from aws_cryptographic_materialproviders.mpl.models import ( - CreateDefaultCryptographicMaterialsManagerInput - ) - from aws_cryptographic_materialproviders.mpl.references import ( - IKeyring, - ) + from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput + from aws_cryptographic_materialproviders.mpl.references import IKeyring HAS_MPL = True except ImportError: From 7e5fa4837f252f8b038efc51567d04f90d6510d8 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 09:54:14 -0800 Subject: [PATCH 021/172] flake8 examples --- examples/src/keyrings/hierarchical_keyring.py | 34 ++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index 20647bed6..81d02f786 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -1,8 +1,5 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 - - - """Example showing basic encryption and decryption of a value already in memory.""" import sys @@ -10,13 +7,8 @@ import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy -from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError, SerializationError - -module_root_dir = '/'.join(__file__.split("/")[:-1]) - -sys.path.append(module_root_dir) +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError -import aws_cryptographic_materialproviders from aws_cryptographic_materialproviders.keystore.client import KeyStore from aws_cryptographic_materialproviders.keystore.config import KeyStoreConfig from aws_cryptographic_materialproviders.keystore.models import CreateKeyInput, KMSConfigurationKmsKeyArn @@ -31,13 +23,18 @@ ) from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier, IKeyring +module_root_dir = '/'.join(__file__.split("/")[:-1]) + +sys.path.append(module_root_dir) + EXAMPLE_DATA: bytes = b"Hello World" + def encrypt_and_decrypt_with_keyring( - key_store_table_name: str, - logical_key_store_name: str, - kms_key_id: str - ): + key_store_table_name: str, + logical_key_store_name: str, + kms_key_id: str +): # 1. Instantiate the encryption SDK client. # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, @@ -90,7 +87,7 @@ def get_branch_key_id( if b"tenant" not in encryption_context: raise ValueError("EncryptionContext invalid, does not contain expected tenant key value pair.") - + tenant_key_id: str = encryption_context.get(b"tenant") branch_key_id: str @@ -227,12 +224,3 @@ def get_branch_key_id( assert plaintext_bytes_B == EXAMPLE_DATA # Also, a thread-safe example ig - -# hack in a test -import botocore - -encrypt_and_decrypt_with_keyring( - "KeyStoreDdbTable", - "KeyStoreDdbTable", - "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" -) \ No newline at end of file From 055deabd332af255f40df7fb52b80063df268f06 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 09:58:05 -0800 Subject: [PATCH 022/172] isort + flake8 --- examples/src/keyrings/hierarchical_keyring.py | 10 +++++----- examples/src/keyrings/module_.py | 1 + examples/src/module_.py | 1 + examples/test/examples_test_utils.py | 2 +- examples/test/test_i_basic_encryption.py | 1 - ..._i_basic_file_encryption_with_multiple_providers.py | 4 +--- ...st_i_basic_file_encryption_with_raw_key_provider.py | 1 - examples/test/test_i_data_key_caching_basic.py | 1 - examples/test/test_i_discovery_kms_provider.py | 4 +--- examples/test/test_i_mrk_aware_kms_provider.py | 4 +--- examples/test/test_i_multiple_kms_cmk.py | 4 +--- examples/test/test_i_one_kms_cmk.py | 4 +--- examples/test/test_i_one_kms_cmk_streaming_data.py | 1 - examples/test/test_i_one_kms_cmk_unsigned.py | 4 +--- examples/test/test_i_set_commitment.py | 4 +--- 15 files changed, 15 insertions(+), 31 deletions(-) diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index 81d02f786..acc594cc3 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -4,11 +4,6 @@ import sys import boto3 - -import aws_encryption_sdk -from aws_encryption_sdk import CommitmentPolicy -from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError - from aws_cryptographic_materialproviders.keystore.client import KeyStore from aws_cryptographic_materialproviders.keystore.config import KeyStoreConfig from aws_cryptographic_materialproviders.keystore.models import CreateKeyInput, KMSConfigurationKmsKeyArn @@ -23,6 +18,10 @@ ) from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier, IKeyring +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError + module_root_dir = '/'.join(__file__.split("/")[:-1]) sys.path.append(module_root_dir) @@ -35,6 +34,7 @@ def encrypt_and_decrypt_with_keyring( logical_key_store_name: str, kms_key_id: str ): + """Creates a hierarchical keyring using the provided resources, then encrypts and decrypts a string with it.""" # 1. Instantiate the encryption SDK client. # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, diff --git a/examples/src/keyrings/module_.py b/examples/src/keyrings/module_.py index e69de29bb..2f64c8e0f 100644 --- a/examples/src/keyrings/module_.py +++ b/examples/src/keyrings/module_.py @@ -0,0 +1 @@ +"""Should remove this.""" \ No newline at end of file diff --git a/examples/src/module_.py b/examples/src/module_.py index e69de29bb..2f64c8e0f 100644 --- a/examples/src/module_.py +++ b/examples/src/module_.py @@ -0,0 +1 @@ +"""Should remove this.""" \ No newline at end of file diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index 8a51f21c8..08e8cf2f5 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -49,7 +49,7 @@ from integration_test_utils import ( # noqa pylint: disable=unused-import,import-error get_cmk_arn, - get_second_cmk_arn, get_mrk_arn, + get_second_cmk_arn, get_second_mrk_arn, ) diff --git a/examples/test/test_i_basic_encryption.py b/examples/test/test_i_basic_encryption.py index f2a4fab51..aa32d61fa 100644 --- a/examples/test/test_i_basic_encryption.py +++ b/examples/test/test_i_basic_encryption.py @@ -17,7 +17,6 @@ from ..src.basic_encryption import cycle_string from .examples_test_utils import get_cmk_arn, static_plaintext - pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py b/examples/test/test_i_basic_file_encryption_with_multiple_providers.py index 282a272ab..0792f4958 100644 --- a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py +++ b/examples/test/test_i_basic_file_encryption_with_multiple_providers.py @@ -18,9 +18,7 @@ import pytest from ..src.basic_file_encryption_with_multiple_providers import cycle_file -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py b/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py index 710c0ccac..046b7f964 100644 --- a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py +++ b/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py @@ -19,7 +19,6 @@ from ..src.basic_file_encryption_with_raw_key_provider import cycle_file from .examples_test_utils import static_plaintext - pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_data_key_caching_basic.py b/examples/test/test_i_data_key_caching_basic.py index 734c35692..7a30f4e53 100644 --- a/examples/test/test_i_data_key_caching_basic.py +++ b/examples/test/test_i_data_key_caching_basic.py @@ -16,7 +16,6 @@ from ..src.data_key_caching_basic import encrypt_with_caching from .examples_test_utils import get_cmk_arn - pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_discovery_kms_provider.py b/examples/test/test_i_discovery_kms_provider.py index e9a1c6e71..0f64cbf59 100644 --- a/examples/test/test_i_discovery_kms_provider.py +++ b/examples/test/test_i_discovery_kms_provider.py @@ -16,9 +16,7 @@ import pytest from ..src.discovery_kms_provider import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_mrk_aware_kms_provider.py b/examples/test/test_i_mrk_aware_kms_provider.py index 8e7a003f8..a90101fa8 100644 --- a/examples/test/test_i_mrk_aware_kms_provider.py +++ b/examples/test/test_i_mrk_aware_kms_provider.py @@ -15,9 +15,7 @@ import pytest from ..src.mrk_aware_kms_provider import encrypt_decrypt -from .examples_test_utils import get_mrk_arn, get_second_mrk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_mrk_arn, get_second_mrk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_multiple_kms_cmk.py b/examples/test/test_i_multiple_kms_cmk.py index 39369cbc6..2915a0fd7 100644 --- a/examples/test/test_i_multiple_kms_cmk.py +++ b/examples/test/test_i_multiple_kms_cmk.py @@ -16,9 +16,7 @@ import pytest from ..src.multiple_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, get_second_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, get_second_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk.py b/examples/test/test_i_one_kms_cmk.py index 71ce74d3d..96dd48dae 100644 --- a/examples/test/test_i_one_kms_cmk.py +++ b/examples/test/test_i_one_kms_cmk.py @@ -16,9 +16,7 @@ import pytest from ..src.one_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk_streaming_data.py b/examples/test/test_i_one_kms_cmk_streaming_data.py index b22fa4232..f0a3094d0 100644 --- a/examples/test/test_i_one_kms_cmk_streaming_data.py +++ b/examples/test/test_i_one_kms_cmk_streaming_data.py @@ -20,7 +20,6 @@ from ..src.one_kms_cmk_streaming_data import encrypt_decrypt_stream from .examples_test_utils import get_cmk_arn, static_plaintext - pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk_unsigned.py b/examples/test/test_i_one_kms_cmk_unsigned.py index 8a2758c96..41f16473d 100644 --- a/examples/test/test_i_one_kms_cmk_unsigned.py +++ b/examples/test/test_i_one_kms_cmk_unsigned.py @@ -16,9 +16,7 @@ import pytest from ..src.one_kms_cmk_unsigned import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_set_commitment.py b/examples/test/test_i_set_commitment.py index 96247334b..c14a379bf 100644 --- a/examples/test/test_i_set_commitment.py +++ b/examples/test/test_i_set_commitment.py @@ -16,9 +16,7 @@ import pytest from ..src.set_commitment import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] From 6cf01d4c6cfd0b67656a1faba6af4894675caaba Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 10:08:03 -0800 Subject: [PATCH 023/172] flake8/pylint examples --- examples/src/keyrings/hierarchical_keyring.py | 1 - examples/src/keyrings/module_.py | 2 +- examples/src/module_.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index acc594cc3..76aef25e0 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -35,7 +35,6 @@ def encrypt_and_decrypt_with_keyring( kms_key_id: str ): """Creates a hierarchical keyring using the provided resources, then encrypts and decrypts a string with it.""" - # 1. Instantiate the encryption SDK client. # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, # which enforces that this client only encrypts using committing algorithm suites and enforces diff --git a/examples/src/keyrings/module_.py b/examples/src/keyrings/module_.py index 2f64c8e0f..d9a8c058f 100644 --- a/examples/src/keyrings/module_.py +++ b/examples/src/keyrings/module_.py @@ -1 +1 @@ -"""Should remove this.""" \ No newline at end of file +"""Should remove this.""" diff --git a/examples/src/module_.py b/examples/src/module_.py index 2f64c8e0f..d9a8c058f 100644 --- a/examples/src/module_.py +++ b/examples/src/module_.py @@ -1 +1 @@ -"""Should remove this.""" \ No newline at end of file +"""Should remove this.""" From 00cfed1f368752b872ebb25631331e04e4660893 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 10:12:24 -0800 Subject: [PATCH 024/172] reset tests --- examples/test/examples_test_utils.py | 2 +- examples/test/test_i_basic_encryption.py | 1 + .../test_i_basic_file_encryption_with_multiple_providers.py | 4 +++- .../test_i_basic_file_encryption_with_raw_key_provider.py | 1 + examples/test/test_i_data_key_caching_basic.py | 1 + examples/test/test_i_discovery_kms_provider.py | 4 +++- examples/test/test_i_mrk_aware_kms_provider.py | 4 +++- examples/test/test_i_multiple_kms_cmk.py | 4 +++- examples/test/test_i_one_kms_cmk.py | 4 +++- examples/test/test_i_one_kms_cmk_streaming_data.py | 1 + examples/test/test_i_one_kms_cmk_unsigned.py | 4 +++- examples/test/test_i_set_commitment.py | 4 +++- 12 files changed, 26 insertions(+), 8 deletions(-) diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index 08e8cf2f5..8a51f21c8 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -49,7 +49,7 @@ from integration_test_utils import ( # noqa pylint: disable=unused-import,import-error get_cmk_arn, - get_mrk_arn, get_second_cmk_arn, + get_mrk_arn, get_second_mrk_arn, ) diff --git a/examples/test/test_i_basic_encryption.py b/examples/test/test_i_basic_encryption.py index aa32d61fa..f2a4fab51 100644 --- a/examples/test/test_i_basic_encryption.py +++ b/examples/test/test_i_basic_encryption.py @@ -17,6 +17,7 @@ from ..src.basic_encryption import cycle_string from .examples_test_utils import get_cmk_arn, static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py b/examples/test/test_i_basic_file_encryption_with_multiple_providers.py index 0792f4958..282a272ab 100644 --- a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py +++ b/examples/test/test_i_basic_file_encryption_with_multiple_providers.py @@ -18,7 +18,9 @@ import pytest from ..src.basic_file_encryption_with_multiple_providers import cycle_file -from .examples_test_utils import get_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py b/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py index 046b7f964..710c0ccac 100644 --- a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py +++ b/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py @@ -19,6 +19,7 @@ from ..src.basic_file_encryption_with_raw_key_provider import cycle_file from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_data_key_caching_basic.py b/examples/test/test_i_data_key_caching_basic.py index 7a30f4e53..734c35692 100644 --- a/examples/test/test_i_data_key_caching_basic.py +++ b/examples/test/test_i_data_key_caching_basic.py @@ -16,6 +16,7 @@ from ..src.data_key_caching_basic import encrypt_with_caching from .examples_test_utils import get_cmk_arn + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_discovery_kms_provider.py b/examples/test/test_i_discovery_kms_provider.py index 0f64cbf59..e9a1c6e71 100644 --- a/examples/test/test_i_discovery_kms_provider.py +++ b/examples/test/test_i_discovery_kms_provider.py @@ -16,7 +16,9 @@ import pytest from ..src.discovery_kms_provider import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_mrk_aware_kms_provider.py b/examples/test/test_i_mrk_aware_kms_provider.py index a90101fa8..8e7a003f8 100644 --- a/examples/test/test_i_mrk_aware_kms_provider.py +++ b/examples/test/test_i_mrk_aware_kms_provider.py @@ -15,7 +15,9 @@ import pytest from ..src.mrk_aware_kms_provider import encrypt_decrypt -from .examples_test_utils import get_mrk_arn, get_second_mrk_arn, static_plaintext +from .examples_test_utils import get_mrk_arn, get_second_mrk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_multiple_kms_cmk.py b/examples/test/test_i_multiple_kms_cmk.py index 2915a0fd7..39369cbc6 100644 --- a/examples/test/test_i_multiple_kms_cmk.py +++ b/examples/test/test_i_multiple_kms_cmk.py @@ -16,7 +16,9 @@ import pytest from ..src.multiple_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, get_second_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn, get_second_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk.py b/examples/test/test_i_one_kms_cmk.py index 96dd48dae..71ce74d3d 100644 --- a/examples/test/test_i_one_kms_cmk.py +++ b/examples/test/test_i_one_kms_cmk.py @@ -16,7 +16,9 @@ import pytest from ..src.one_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk_streaming_data.py b/examples/test/test_i_one_kms_cmk_streaming_data.py index f0a3094d0..b22fa4232 100644 --- a/examples/test/test_i_one_kms_cmk_streaming_data.py +++ b/examples/test/test_i_one_kms_cmk_streaming_data.py @@ -20,6 +20,7 @@ from ..src.one_kms_cmk_streaming_data import encrypt_decrypt_stream from .examples_test_utils import get_cmk_arn, static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk_unsigned.py b/examples/test/test_i_one_kms_cmk_unsigned.py index 41f16473d..8a2758c96 100644 --- a/examples/test/test_i_one_kms_cmk_unsigned.py +++ b/examples/test/test_i_one_kms_cmk_unsigned.py @@ -16,7 +16,9 @@ import pytest from ..src.one_kms_cmk_unsigned import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_set_commitment.py b/examples/test/test_i_set_commitment.py index c14a379bf..96247334b 100644 --- a/examples/test/test_i_set_commitment.py +++ b/examples/test/test_i_set_commitment.py @@ -16,7 +16,9 @@ import pytest from ..src.set_commitment import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] From 61bbb3b474bbff6b360cbea867245a2c28405659 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 11:05:51 -0800 Subject: [PATCH 025/172] extend mpl --- .github/workflows/ci_tests.yaml | 12 ++++++++++ .../keyrings/test_i_hierarchical_keyring.py | 12 ++++++++++ tox.ini | 24 +++++++++++++++++-- 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 examples/test/keyrings/test_i_hierarchical_keyring.py diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 9d491203c..f1701de76 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -45,12 +45,24 @@ jobs: # Enable them once we sort how to provide them. # - integ # - examples + optional_dependency: + - "" + - mpl exclude: # x86 builds are only meaningful for Windows - os: ubuntu-latest architecture: x86 - os: macos-latest architecture: x86 + # MPL is not supported on <3.11 + - python: 3.7 + optional_dependency: mpl + - python: 3.8 + optional_dependency: mpl + - python: 3.9 + optional_dependency: mpl + - python: 3.10 + optional_dependency: mpl steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 diff --git a/examples/test/keyrings/test_i_hierarchical_keyring.py b/examples/test/keyrings/test_i_hierarchical_keyring.py new file mode 100644 index 000000000..5df72383f --- /dev/null +++ b/examples/test/keyrings/test_i_hierarchical_keyring.py @@ -0,0 +1,12 @@ +"""Unit test suite for the hierarchical keyring example.""" +import pytest + +from ..src.keyrings.hierarchical_keyring import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + key_store_table_name = "KeyStoreDdbTable" + key_arn = "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" + encrypt_and_decrypt_with_keyring(key_store_table_name, key_store_table_name, key_arn) diff --git a/tox.ini b/tox.ini index 9ca7a0cd6..c90a6fcd6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,10 @@ [tox] envlist = - py{37,38,39,310,311,312}-{local,integ,accept,examples}, nocmk, + # <3.11: run all non-MPL tests + py{37,38,39,310}-{local,integ,accept,examples}, + # >=3.11: run all MPL tests and non-MPL tests + py{311,312}-{local,integ,accept,examples}{,-mpl}, + nocmk, bandit, doc8, readme, docs, {flake8,pylint}{,-tests,-examples}, isort-check, black-check, @@ -61,12 +65,17 @@ passenv = # Pass through custom pip config file settings PIP_CONFIG_FILE sitepackages = False -deps = -rdev_requirements/test-requirements.txt +deps = + -rdev_requirements/test-requirements.txt + # install the MPL if in environment + mpl: aws-cryptographic-material-providers>=0.0.1 commands = local: {[testenv:base-command]commands} test/ -m local integ: {[testenv:base-command]commands} test/ -m integ accept: {[testenv:base-command]commands} test/ -m accept examples: {[testenv:base-command]commands} examples/test/ -m examples + # append MPL examples to base examples command + examples-mpl: {[testenv:examples]commands} examples/mpl/test/ all: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} @@ -134,6 +143,17 @@ sitepackages = {[testenv:test-upstream-requirements-base]sitepackages} recreate = {[testenv:test-upstream-requirements-base]recreate} commands = {[testenv:test-upstream-requirements-base]commands} +# Test MPL +[testenv:py311-local-mpl] +basepython = {[testenv:pylint]basepython} +deps = {[testenv:pylint]deps} +commands = + pylint \ + --rcfile=test/pylintrc \ + test/unit/ \ + test/functional/ \ + test/integration/ + # Linters [testenv:flake8] basepython = python3 From 4d53ad695908384c6e3705fc7ea5982ed7be9d8f Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 11:15:27 -0800 Subject: [PATCH 026/172] mpl gha --- .github/workflows/ci_tests.yaml | 16 ++++++++-------- tox.ini | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index f1701de76..3d22ab05f 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -45,9 +45,9 @@ jobs: # Enable them once we sort how to provide them. # - integ # - examples - optional_dependency: + optional_mpl_dependency: - "" - - mpl + - -mpl exclude: # x86 builds are only meaningful for Windows - os: ubuntu-latest @@ -56,13 +56,13 @@ jobs: architecture: x86 # MPL is not supported on <3.11 - python: 3.7 - optional_dependency: mpl + optional_mpl_dependency: mpl - python: 3.8 - optional_dependency: mpl + optional_mpl_dependency: mpl - python: 3.9 - optional_dependency: mpl + optional_mpl_dependency: mpl - python: 3.10 - optional_dependency: mpl + optional_mpl_dependency: mpl steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 @@ -74,7 +74,7 @@ jobs: pip install --upgrade -r dev_requirements/ci-requirements.txt - name: run test env: - TOXENV: ${{ matrix.category }} + TOXENV: ${{ matrix.category }}${{ matrix.optional_mpl_dependency }} run: tox -- -vv upstream-py37: runs-on: ubuntu-latest @@ -114,5 +114,5 @@ jobs: pip install --upgrade -r dev_requirements/ci-requirements.txt - name: run test env: - TOXENV: ${{ matrix.category }} + TOXENV: ${{ matrix.category }}${{ matrix.optional_mpl_dependency }} run: tox -- -vv diff --git a/tox.ini b/tox.ini index c90a6fcd6..8f12ab8e7 100644 --- a/tox.ini +++ b/tox.ini @@ -75,7 +75,7 @@ commands = accept: {[testenv:base-command]commands} test/ -m accept examples: {[testenv:base-command]commands} examples/test/ -m examples # append MPL examples to base examples command - examples-mpl: {[testenv:examples]commands} examples/mpl/test/ + examples-mpl: {[testenv:base-command]commands} examples/test/ examples/mpl/test -m examples all: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} From c1736d3e91d3d78cd3412ab5c9095eccd41340d6 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 11:26:21 -0800 Subject: [PATCH 027/172] debug --- .github/workflows/ci_tests.yaml | 8 ++++---- tox.ini | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 3d22ab05f..603f54371 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -56,13 +56,13 @@ jobs: architecture: x86 # MPL is not supported on <3.11 - python: 3.7 - optional_mpl_dependency: mpl + optional_mpl_dependency: -mpl - python: 3.8 - optional_mpl_dependency: mpl + optional_mpl_dependency: -mpl - python: 3.9 - optional_mpl_dependency: mpl + optional_mpl_dependency: -mpl - python: 3.10 - optional_mpl_dependency: mpl + optional_mpl_dependency: -mpl steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 diff --git a/tox.ini b/tox.ini index 8f12ab8e7..37fbbae51 100644 --- a/tox.ini +++ b/tox.ini @@ -68,7 +68,9 @@ sitepackages = False deps = -rdev_requirements/test-requirements.txt # install the MPL if in environment - mpl: aws-cryptographic-material-providers>=0.0.1 + mpl: "aws-cryptographic-material-providers @" \ + "git+https://github.com/aws/aws-cryptographic-material-providers-library.git@" \ + "lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python" commands = local: {[testenv:base-command]commands} test/ -m local integ: {[testenv:base-command]commands} test/ -m integ From 9991789b79842aa0812c84bcc3c33a6b36c2f182 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 12:40:53 -0800 Subject: [PATCH 028/172] debug --- tox.ini | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 37fbbae51..ef0f6fa29 100644 --- a/tox.ini +++ b/tox.ini @@ -68,9 +68,7 @@ sitepackages = False deps = -rdev_requirements/test-requirements.txt # install the MPL if in environment - mpl: "aws-cryptographic-material-providers @" \ - "git+https://github.com/aws/aws-cryptographic-material-providers-library.git@" \ - "lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python" + mpl: "aws-cryptographic-material-providers @git+https://github.com/aws/aws-cryptographic-material-providers-library.git@lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python" commands = local: {[testenv:base-command]commands} test/ -m local integ: {[testenv:base-command]commands} test/ -m integ From a501e8f07d1bd125fd77e6b5c28710eb19402e9a Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 12:53:41 -0800 Subject: [PATCH 029/172] debug --- .../keyrings/test_i_hierarchical_keyring.py | 2 +- tox.ini | 38 ++++++++++--------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/examples/test/keyrings/test_i_hierarchical_keyring.py b/examples/test/keyrings/test_i_hierarchical_keyring.py index 5df72383f..d80bb565d 100644 --- a/examples/test/keyrings/test_i_hierarchical_keyring.py +++ b/examples/test/keyrings/test_i_hierarchical_keyring.py @@ -1,7 +1,7 @@ """Unit test suite for the hierarchical keyring example.""" import pytest -from ..src.keyrings.hierarchical_keyring import encrypt_and_decrypt_with_keyring +from ...src.keyrings.hierarchical_keyring import encrypt_and_decrypt_with_keyring pytestmark = [pytest.mark.examples] diff --git a/tox.ini b/tox.ini index ef0f6fa29..d06cbab2e 100644 --- a/tox.ini +++ b/tox.ini @@ -10,6 +10,7 @@ envlist = isort-check, black-check, # prone to false positives vulture +ignore_base_python_conflict = true # Additional test environments: # @@ -47,28 +48,29 @@ envlist = commands = pytest --basetemp={envtmpdir} -l {posargs} [testenv] -passenv = - # Identifies AWS KMS key id to use in integration tests - AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID \ - # Identifies a second AWS KMS key id to use in integration tests - AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2 \ - # Identifies AWS KMS MRK key id to use in integration tests - AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1 \ - # Identifies a related AWS KMS MRK key id to use in integration tests - AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2 \ - # Pass through AWS credentials - AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ - # AWS Role access in CodeBuild is via the contaner URI - AWS_CONTAINER_CREDENTIALS_RELATIVE_URI \ - # Pass through AWS profile name (useful for local testing) - AWS_PROFILE \ - # Pass through custom pip config file settings - PIP_CONFIG_FILE +passenv = AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,AWS_PROFILE,PIP_CONFIG_FILE +; passenv = +; # Identifies AWS KMS key id to use in integration tests +; AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID \ +; # Identifies a second AWS KMS key id to use in integration tests +; AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2 \ +; # Identifies AWS KMS MRK key id to use in integration tests +; AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1 \ +; # Identifies a related AWS KMS MRK key id to use in integration tests +; AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2 \ +; # Pass through AWS credentials +; AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ +; # AWS Role access in CodeBuild is via the contaner URI +; AWS_CONTAINER_CREDENTIALS_RELATIVE_URI \ +; # Pass through AWS profile name (useful for local testing) +; AWS_PROFILE \ +; # Pass through custom pip config file settings +; PIP_CONFIG_FILE sitepackages = False deps = -rdev_requirements/test-requirements.txt # install the MPL if in environment - mpl: "aws-cryptographic-material-providers @git+https://github.com/aws/aws-cryptographic-material-providers-library.git@lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python" + mpl: -rrequirements_mpl.txt commands = local: {[testenv:base-command]commands} test/ -m local integ: {[testenv:base-command]commands} test/ -m integ From 6eb8f82edb6139e6df62ec21957ad89ef5efa58c Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 12:53:55 -0800 Subject: [PATCH 030/172] debug --- examples/src/keyrings/__init__.py | 13 +++++++++++++ examples/test/keyrings/__init__.py | 13 +++++++++++++ requirements_mpl.txt | 1 + 3 files changed, 27 insertions(+) create mode 100644 examples/src/keyrings/__init__.py create mode 100644 examples/test/keyrings/__init__.py create mode 100644 requirements_mpl.txt diff --git a/examples/src/keyrings/__init__.py b/examples/src/keyrings/__init__.py new file mode 100644 index 000000000..e8fd618b1 --- /dev/null +++ b/examples/src/keyrings/__init__.py @@ -0,0 +1,13 @@ +# 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. +"""Stub module indicator to make linter configuration simpler.""" diff --git a/examples/test/keyrings/__init__.py b/examples/test/keyrings/__init__.py new file mode 100644 index 000000000..e8fd618b1 --- /dev/null +++ b/examples/test/keyrings/__init__.py @@ -0,0 +1,13 @@ +# 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. +"""Stub module indicator to make linter configuration simpler.""" diff --git a/requirements_mpl.txt b/requirements_mpl.txt new file mode 100644 index 000000000..209e10f2c --- /dev/null +++ b/requirements_mpl.txt @@ -0,0 +1 @@ +aws-cryptographic-material-providers @ git+https://github.com/aws/aws-cryptographic-material-providers-library.git@lucmcdon/python-mpl#subdirectory=AwsCryptographicMaterialProviders/runtimes/python \ No newline at end of file From 5ccfa0cce6afdde5e598ceb35719feb109da1d48 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 13:14:29 -0800 Subject: [PATCH 031/172] codebuild mpl --- tox.ini | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tox.ini b/tox.ini index d06cbab2e..8eb141821 100644 --- a/tox.ini +++ b/tox.ini @@ -145,17 +145,6 @@ sitepackages = {[testenv:test-upstream-requirements-base]sitepackages} recreate = {[testenv:test-upstream-requirements-base]recreate} commands = {[testenv:test-upstream-requirements-base]commands} -# Test MPL -[testenv:py311-local-mpl] -basepython = {[testenv:pylint]basepython} -deps = {[testenv:pylint]deps} -commands = - pylint \ - --rcfile=test/pylintrc \ - test/unit/ \ - test/functional/ \ - test/integration/ - # Linters [testenv:flake8] basepython = python3 From 5e7ec9b94694ed4ace20aabd0ea87edd9e74f479 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 13:16:13 -0800 Subject: [PATCH 032/172] codebuild mpl --- codebuild/py311/examples_mpl.yml | 22 ++++++++++++++++++++++ codebuild/py311/integ_mpl.yml | 22 ++++++++++++++++++++++ codebuild/py312/examples_mpl.yml | 27 +++++++++++++++++++++++++++ codebuild/py312/integ_mpl.yml | 27 +++++++++++++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 codebuild/py311/examples_mpl.yml create mode 100644 codebuild/py311/integ_mpl.yml create mode 100644 codebuild/py312/examples_mpl.yml create mode 100644 codebuild/py312/integ_mpl.yml diff --git a/codebuild/py311/examples_mpl.yml b/codebuild/py311/examples_mpl.yml new file mode 100644 index 000000000..abea2ad8c --- /dev/null +++ b/codebuild/py311/examples_mpl.yml @@ -0,0 +1,22 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-examples-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + - pip install "tox < 4.0" + - tox diff --git a/codebuild/py311/integ_mpl.yml b/codebuild/py311/integ_mpl.yml new file mode 100644 index 000000000..ad969c621 --- /dev/null +++ b/codebuild/py311/integ_mpl.yml @@ -0,0 +1,22 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-integ-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + - pip install "tox < 4.0" + - tox diff --git a/codebuild/py312/examples_mpl.yml b/codebuild/py312/examples_mpl.yml new file mode 100644 index 000000000..8ffd24964 --- /dev/null +++ b/codebuild/py312/examples_mpl.yml @@ -0,0 +1,27 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-examples-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: latest + build: + commands: + - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - + - pyenv install 3.12.0 + - pyenv local 3.12.0 + - pip install --upgrade pip + - pip install setuptools + - pip install "tox < 4.0" + - tox diff --git a/codebuild/py312/integ_mpl.yml b/codebuild/py312/integ_mpl.yml new file mode 100644 index 000000000..085cb4660 --- /dev/null +++ b/codebuild/py312/integ_mpl.yml @@ -0,0 +1,27 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-integ-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: latest + build: + commands: + - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - + - pyenv install 3.12.0 + - pyenv local 3.12.0 + - pip install --upgrade pip + - pip install setuptools + - pip install "tox < 4.0" + - tox From cc48697824accd00df18e71877a4f888d0f32125 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 13:20:54 -0800 Subject: [PATCH 033/172] codebuild mpl --- buildspec.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/buildspec.yml b/buildspec.yml index f92d203a0..c718c3df5 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -58,27 +58,51 @@ batch: buildspec: codebuild/py311/integ.yml env: image: aws/codebuild/standard:7.0 + - identifier: py311_integ_mpl + buildspec: codebuild/py311/integ_mpl.yml + env: + image: aws/codebuild/standard:7.0 - identifier: py311_examples buildspec: codebuild/py311/examples.yml env: image: aws/codebuild/standard:7.0 + - identifier: py311_examples_mpl + buildspec: codebuild/py311/examples_mpl.yml + env: + image: aws/codebuild/standard:7.0 - identifier: py311_awses_latest buildspec: codebuild/py311/awses_local.yml env: image: aws/codebuild/standard:7.0 + - identifier: py311_awses_latest_mpl + buildspec: codebuild/py311/awses_local_mpl.yml + env: + image: aws/codebuild/standard:7.0 - identifier: py312_integ buildspec: codebuild/py312/integ.yml env: image: aws/codebuild/standard:7.0 + - identifier: py312_integ_mpl + buildspec: codebuild/py312/integ_mpl.yml + env: + image: aws/codebuild/standard:7.0 - identifier: py312_examples buildspec: codebuild/py312/examples.yml env: image: aws/codebuild/standard:7.0 + - identifier: py312_examples_mpl + buildspec: codebuild/py312/examples_mpl.yml + env: + image: aws/codebuild/standard:7.0 - identifier: py312_awses_latest buildspec: codebuild/py312/awses_local.yml env: image: aws/codebuild/standard:7.0 + - identifier: py312_awses_latest_mpl + buildspec: codebuild/py312/awses_local_mpl.yml + env: + image: aws/codebuild/standard:7.0 - identifier: code_coverage buildspec: codebuild/coverage/coverage.yml From fae43d14db29780616356893d3ae7da9ce996dab Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 13:21:05 -0800 Subject: [PATCH 034/172] codebuild mpl --- codebuild/py311/awses_local_mpl.yml | 25 ++++++++++++++++++++++++ codebuild/py312/awses_local_mpl.yml | 30 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 codebuild/py311/awses_local_mpl.yml create mode 100644 codebuild/py312/awses_local_mpl.yml diff --git a/codebuild/py311/awses_local_mpl.yml b/codebuild/py311/awses_local_mpl.yml new file mode 100644 index 000000000..f98859b40 --- /dev/null +++ b/codebuild/py311/awses_local_mpl.yml @@ -0,0 +1,25 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-awses_local-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_API_DEPLOYMENT_ID: "xi1mwx3ttb" + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_REGION: "us-west-2" + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - tox diff --git a/codebuild/py312/awses_local_mpl.yml b/codebuild/py312/awses_local_mpl.yml new file mode 100644 index 000000000..689d40da8 --- /dev/null +++ b/codebuild/py312/awses_local_mpl.yml @@ -0,0 +1,30 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-awses_local-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_API_DEPLOYMENT_ID: "xi1mwx3ttb" + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_REGION: "us-west-2" + +phases: + install: + runtime-versions: + python: latest + build: + commands: + - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - + - pyenv install 3.12.0 + - pyenv local 3.12.0 + - pip install --upgrade pip + - pip install setuptools + - pip install "tox < 4.0" + - cd test_vector_handlers + - tox From 263761678b201618c488874c040cfd7d50d0db7f Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 13:32:27 -0800 Subject: [PATCH 035/172] debug --- tox.ini | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tox.ini b/tox.ini index 8eb141821..8e6cf3f34 100644 --- a/tox.ini +++ b/tox.ini @@ -48,24 +48,24 @@ ignore_base_python_conflict = true commands = pytest --basetemp={envtmpdir} -l {posargs} [testenv] -passenv = AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,AWS_PROFILE,PIP_CONFIG_FILE -; passenv = -; # Identifies AWS KMS key id to use in integration tests -; AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID \ -; # Identifies a second AWS KMS key id to use in integration tests -; AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2 \ -; # Identifies AWS KMS MRK key id to use in integration tests -; AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1 \ -; # Identifies a related AWS KMS MRK key id to use in integration tests -; AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2 \ -; # Pass through AWS credentials -; AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ -; # AWS Role access in CodeBuild is via the contaner URI -; AWS_CONTAINER_CREDENTIALS_RELATIVE_URI \ -; # Pass through AWS profile name (useful for local testing) -; AWS_PROFILE \ -; # Pass through custom pip config file settings -; PIP_CONFIG_FILE +; passenv = AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,AWS_PROFILE,PIP_CONFIG_FILE +passenv = + # Identifies AWS KMS key id to use in integration tests + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID \ + # Identifies a second AWS KMS key id to use in integration tests + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2 \ + # Identifies AWS KMS MRK key id to use in integration tests + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1 \ + # Identifies a related AWS KMS MRK key id to use in integration tests + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2 \ + # Pass through AWS credentials + AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ + # AWS Role access in CodeBuild is via the contaner URI + AWS_CONTAINER_CREDENTIALS_RELATIVE_URI \ + # Pass through AWS profile name (useful for local testing) + AWS_PROFILE \ + # Pass through custom pip config file settings + PIP_CONFIG_FILE sitepackages = False deps = -rdev_requirements/test-requirements.txt From 2694932f5090404eaacf9c5d442b6acac98c0246 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 13:37:16 -0800 Subject: [PATCH 036/172] debug --- tox.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 8e6cf3f34..59a1dde74 100644 --- a/tox.ini +++ b/tox.ini @@ -65,7 +65,9 @@ passenv = # Pass through AWS profile name (useful for local testing) AWS_PROFILE \ # Pass through custom pip config file settings - PIP_CONFIG_FILE + PIP_CONFIG_FILE \ + # Pass through any configured AWS region + REGION sitepackages = False deps = -rdev_requirements/test-requirements.txt From f674d3e27a2f21f3a340c9c95b3ef60fc786dd3d Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 13:40:29 -0800 Subject: [PATCH 037/172] debug --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 59a1dde74..26e0e5433 100644 --- a/tox.ini +++ b/tox.ini @@ -67,7 +67,7 @@ passenv = # Pass through custom pip config file settings PIP_CONFIG_FILE \ # Pass through any configured AWS region - REGION + AWS_REGION sitepackages = False deps = -rdev_requirements/test-requirements.txt From 0b5e655b1100f4b236a14dd47ab7e7451a90eed1 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 13:44:55 -0800 Subject: [PATCH 038/172] debug --- codebuild/py311/awses_local_mpl.yml | 1 + codebuild/py311/examples_mpl.yml | 1 + codebuild/py311/integ_mpl.yml | 1 + codebuild/py312/awses_local_mpl.yml | 1 + codebuild/py312/examples_mpl.yml | 1 + codebuild/py312/integ_mpl.yml | 1 + tox.ini | 4 ++-- 7 files changed, 8 insertions(+), 2 deletions(-) diff --git a/codebuild/py311/awses_local_mpl.yml b/codebuild/py311/awses_local_mpl.yml index f98859b40..04d268d5a 100644 --- a/codebuild/py311/awses_local_mpl.yml +++ b/codebuild/py311/awses_local_mpl.yml @@ -3,6 +3,7 @@ version: 0.2 env: variables: TOXENV: "py311-awses_local-mpl" + AWS_REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/codebuild/py311/examples_mpl.yml b/codebuild/py311/examples_mpl.yml index abea2ad8c..05bdc07c0 100644 --- a/codebuild/py311/examples_mpl.yml +++ b/codebuild/py311/examples_mpl.yml @@ -3,6 +3,7 @@ version: 0.2 env: variables: TOXENV: "py311-examples-mpl" + AWS_REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/codebuild/py311/integ_mpl.yml b/codebuild/py311/integ_mpl.yml index ad969c621..e6766619c 100644 --- a/codebuild/py311/integ_mpl.yml +++ b/codebuild/py311/integ_mpl.yml @@ -3,6 +3,7 @@ version: 0.2 env: variables: TOXENV: "py311-integ-mpl" + AWS_REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/codebuild/py312/awses_local_mpl.yml b/codebuild/py312/awses_local_mpl.yml index 689d40da8..a504696ec 100644 --- a/codebuild/py312/awses_local_mpl.yml +++ b/codebuild/py312/awses_local_mpl.yml @@ -3,6 +3,7 @@ version: 0.2 env: variables: TOXENV: "py312-awses_local-mpl" + AWS_REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/codebuild/py312/examples_mpl.yml b/codebuild/py312/examples_mpl.yml index 8ffd24964..a947c67b3 100644 --- a/codebuild/py312/examples_mpl.yml +++ b/codebuild/py312/examples_mpl.yml @@ -3,6 +3,7 @@ version: 0.2 env: variables: TOXENV: "py312-examples-mpl" + AWS_REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/codebuild/py312/integ_mpl.yml b/codebuild/py312/integ_mpl.yml index 085cb4660..3cf473d08 100644 --- a/codebuild/py312/integ_mpl.yml +++ b/codebuild/py312/integ_mpl.yml @@ -3,6 +3,7 @@ version: 0.2 env: variables: TOXENV: "py312-integ-mpl" + AWS_REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/tox.ini b/tox.ini index 26e0e5433..8b50e6b01 100644 --- a/tox.ini +++ b/tox.ini @@ -77,9 +77,9 @@ commands = local: {[testenv:base-command]commands} test/ -m local integ: {[testenv:base-command]commands} test/ -m integ accept: {[testenv:base-command]commands} test/ -m accept - examples: {[testenv:base-command]commands} examples/test/ -m examples + examples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/keyrings/ # append MPL examples to base examples command - examples-mpl: {[testenv:base-command]commands} examples/test/ examples/mpl/test -m examples + examples-mpl: {[testenv:base-command]commands} examples/test/ -m examples all: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} From 831df1713823dd185883c87dfcab0e521b5fefcd Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 13:52:00 -0800 Subject: [PATCH 039/172] debug --- codebuild/py311/awses_local_mpl.yml | 2 +- codebuild/py311/examples_mpl.yml | 2 +- codebuild/py311/integ_mpl.yml | 2 +- codebuild/py312/awses_local_mpl.yml | 2 +- codebuild/py312/examples_mpl.yml | 2 +- codebuild/py312/integ_mpl.yml | 2 +- test_vector_handlers/tox.ini | 4 +++- tox.ini | 2 -- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/codebuild/py311/awses_local_mpl.yml b/codebuild/py311/awses_local_mpl.yml index 04d268d5a..859931aa3 100644 --- a/codebuild/py311/awses_local_mpl.yml +++ b/codebuild/py311/awses_local_mpl.yml @@ -3,7 +3,7 @@ version: 0.2 env: variables: TOXENV: "py311-awses_local-mpl" - AWS_REGION: "us-west-2" + REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/codebuild/py311/examples_mpl.yml b/codebuild/py311/examples_mpl.yml index 05bdc07c0..e29472507 100644 --- a/codebuild/py311/examples_mpl.yml +++ b/codebuild/py311/examples_mpl.yml @@ -3,7 +3,7 @@ version: 0.2 env: variables: TOXENV: "py311-examples-mpl" - AWS_REGION: "us-west-2" + REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/codebuild/py311/integ_mpl.yml b/codebuild/py311/integ_mpl.yml index e6766619c..694bc0850 100644 --- a/codebuild/py311/integ_mpl.yml +++ b/codebuild/py311/integ_mpl.yml @@ -3,7 +3,7 @@ version: 0.2 env: variables: TOXENV: "py311-integ-mpl" - AWS_REGION: "us-west-2" + REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/codebuild/py312/awses_local_mpl.yml b/codebuild/py312/awses_local_mpl.yml index a504696ec..f39bf8760 100644 --- a/codebuild/py312/awses_local_mpl.yml +++ b/codebuild/py312/awses_local_mpl.yml @@ -3,7 +3,7 @@ version: 0.2 env: variables: TOXENV: "py312-awses_local-mpl" - AWS_REGION: "us-west-2" + REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/codebuild/py312/examples_mpl.yml b/codebuild/py312/examples_mpl.yml index a947c67b3..d6bc3f440 100644 --- a/codebuild/py312/examples_mpl.yml +++ b/codebuild/py312/examples_mpl.yml @@ -3,7 +3,7 @@ version: 0.2 env: variables: TOXENV: "py312-examples-mpl" - AWS_REGION: "us-west-2" + REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/codebuild/py312/integ_mpl.yml b/codebuild/py312/integ_mpl.yml index 3cf473d08..8ffda4bd0 100644 --- a/codebuild/py312/integ_mpl.yml +++ b/codebuild/py312/integ_mpl.yml @@ -3,7 +3,7 @@ version: 0.2 env: variables: TOXENV: "py312-integ-mpl" - AWS_REGION: "us-west-2" + REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- diff --git a/test_vector_handlers/tox.ini b/test_vector_handlers/tox.ini index 643750cd2..b6711361e 100644 --- a/test_vector_handlers/tox.ini +++ b/test_vector_handlers/tox.ini @@ -2,7 +2,7 @@ envlist = # The test vectors depend on new features now, # so until release we can only effectively test the local version of the ESDK. - py{37,38,39,310}-awses_local, + py{37,38,39,310}-awses_local{,-mpl}, # 1.2.0 and 1.2.max are being difficult because of attrs bandit, doc8, readme, {flake8,pylint}{,-tests}, @@ -48,6 +48,8 @@ passenv = sitepackages = False deps = -rtest/requirements.txt + # install the MPL if in environment + mpl: -rrequirements_mpl.txt .. commands = {[testenv:base-command]commands} diff --git a/tox.ini b/tox.ini index 8b50e6b01..903ea5170 100644 --- a/tox.ini +++ b/tox.ini @@ -66,8 +66,6 @@ passenv = AWS_PROFILE \ # Pass through custom pip config file settings PIP_CONFIG_FILE \ - # Pass through any configured AWS region - AWS_REGION sitepackages = False deps = -rdev_requirements/test-requirements.txt From 477e3a097da42fddfded9894d48fad6923d96144 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 13:56:57 -0800 Subject: [PATCH 040/172] debug --- examples/src/keyrings/hierarchical_keyring.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index 76aef25e0..a99728b6e 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -48,8 +48,8 @@ def encrypt_and_decrypt_with_keyring( ) # 2. Create boto3 clients for DynamoDB and KMS. - ddb_client = boto3.client('dynamodb') - kms_client = boto3.client('kms') + ddb_client = boto3.client('dynamodb', region_name="us-west-2") + kms_client = boto3.client('kms', region_name="us-west-2") # 3. Configure your KeyStore resource. # This SHOULD be the same configuration that you used From 166c5ab6ff339f8fd6dafa6ffea071704eb484a5 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 14:24:13 -0800 Subject: [PATCH 041/172] debug --- test_vector_handlers/tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_vector_handlers/tox.ini b/test_vector_handlers/tox.ini index b6711361e..7004080e3 100644 --- a/test_vector_handlers/tox.ini +++ b/test_vector_handlers/tox.ini @@ -49,7 +49,7 @@ sitepackages = False deps = -rtest/requirements.txt # install the MPL if in environment - mpl: -rrequirements_mpl.txt + mpl: -r../requirements_mpl.txt .. commands = {[testenv:base-command]commands} From 7ac88805b863add77147cabedc08c5968480ad55 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 14:35:06 -0800 Subject: [PATCH 042/172] debug --- tox.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 903ea5170..4202979a4 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,6 @@ envlist = isort-check, black-check, # prone to false positives vulture -ignore_base_python_conflict = true # Additional test environments: # @@ -65,7 +64,7 @@ passenv = # Pass through AWS profile name (useful for local testing) AWS_PROFILE \ # Pass through custom pip config file settings - PIP_CONFIG_FILE \ + PIP_CONFIG_FILE sitepackages = False deps = -rdev_requirements/test-requirements.txt From 7e3ca151e85eb44d4d125f172f32ed70d3b6523b Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 14:41:54 -0800 Subject: [PATCH 043/172] fix --- codebuild/py312/awses_local_mpl.yml | 2 +- codebuild/py312/examples_mpl.yml | 2 +- codebuild/py312/integ_mpl.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/codebuild/py312/awses_local_mpl.yml b/codebuild/py312/awses_local_mpl.yml index f39bf8760..db25f4f57 100644 --- a/codebuild/py312/awses_local_mpl.yml +++ b/codebuild/py312/awses_local_mpl.yml @@ -22,7 +22,7 @@ phases: build: commands: - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - - - pyenv install 3.12.0 + - pyenv install --skip-existing 3.12.0 - pyenv local 3.12.0 - pip install --upgrade pip - pip install setuptools diff --git a/codebuild/py312/examples_mpl.yml b/codebuild/py312/examples_mpl.yml index d6bc3f440..ff2168cd5 100644 --- a/codebuild/py312/examples_mpl.yml +++ b/codebuild/py312/examples_mpl.yml @@ -20,7 +20,7 @@ phases: build: commands: - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - - - pyenv install 3.12.0 + - pyenv install --skip-existing 3.12.0 - pyenv local 3.12.0 - pip install --upgrade pip - pip install setuptools diff --git a/codebuild/py312/integ_mpl.yml b/codebuild/py312/integ_mpl.yml index 8ffda4bd0..553f41e8a 100644 --- a/codebuild/py312/integ_mpl.yml +++ b/codebuild/py312/integ_mpl.yml @@ -20,7 +20,7 @@ phases: build: commands: - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - - - pyenv install 3.12.0 + - pyenv install --skip-existing 3.12.0 - pyenv local 3.12.0 - pip install --upgrade pip - pip install setuptools From 4c6a1d00711352236d63b2adc44081dded3a1a65 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 15:08:53 -0800 Subject: [PATCH 044/172] fix --- src/aws_encryption_sdk/cmm_handler.py | 158 ----------------- src/aws_encryption_sdk/materials_handlers.py | 170 ------------------- 2 files changed, 328 deletions(-) delete mode 100644 src/aws_encryption_sdk/cmm_handler.py delete mode 100644 src/aws_encryption_sdk/materials_handlers.py diff --git a/src/aws_encryption_sdk/cmm_handler.py b/src/aws_encryption_sdk/cmm_handler.py deleted file mode 100644 index 5bac15b87..000000000 --- a/src/aws_encryption_sdk/cmm_handler.py +++ /dev/null @@ -1,158 +0,0 @@ -"""Retrieves encryption/decryption materials from an underlying materials provider.""" - -# These dependencies are only loaded if you install the MPL. -try: - # pylint seems to struggle with this conditional import - # pylint: disable=unused-import - from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException - from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, - DecryptMaterialsOutput, - EncryptedDataKey as MPL_EncryptedDataKey, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, - ) - from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager - -except ImportError: - pass - -from typing import List - -from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError -from aws_encryption_sdk.identifiers import CommitmentPolicy -from aws_encryption_sdk.materials_handlers import DecryptionMaterialsHandler, EncryptionMaterialsHandler -from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest -from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager -from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey - - -# TODO-MPL Should this implement interface..? seems like yes since it implements all of interface methods -class CMMHandler(CryptoMaterialsManager): - """ - In instances where encryption materials may be provided by either - an implementation of the native - `aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager` - or an implementation of the MPL's - `aws_cryptographic_materialproviders.mpl.references.ICryptographicMaterialsManager`, - this provides the correct materials based on the underlying materials manager. - """ - - native_cmm: CryptoMaterialsManager - mpl_cmm: 'ICryptographicMaterialsManager' - - def _is_using_native_cmm(self): - return hasattr(self, "native_cmm") and not hasattr(self, "mpl_cmm") - - def __init__( - self, - cmm: 'CryptoMaterialsManager | ICryptographicMaterialsManager' - ): - """ - Create DecryptionMaterialsHandler. - :param cmm: Underlying cryptographic materials manager - """ - if isinstance(cmm, CryptoMaterialsManager): - self.native_cmm = cmm - elif isinstance(cmm, ICryptographicMaterialsManager): - self.mpl_cmm = cmm - else: - raise ValueError(f"Invalid CMM passed to CMMHandler. cmm: {cmm}") - - def get_encryption_materials( - self, - request: EncryptionMaterialsRequest - ) -> EncryptionMaterialsHandler: - """ - Returns an EncryptionMaterialsHandler for the configured CMM. - :param request: Request for encryption materials - """ - if self._is_using_native_cmm(): - return EncryptionMaterialsHandler(self.native_cmm.get_encryption_materials(request)) - else: - try: - mpl_input: GetEncryptionMaterialsInput = CMMHandler._native_to_mpl_get_encryption_materials( - request - ) - mpl_output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) - return EncryptionMaterialsHandler(mpl_output.encryption_materials) - except AwsCryptographicMaterialProvidersException as mpl_exception: - # Wrap MPL error into the ESDK error type - # so customers only have to catch ESDK error types. - raise AWSEncryptionSDKClientError(mpl_exception) - - @staticmethod - def _native_to_mpl_get_encryption_materials( - request: EncryptionMaterialsRequest - ) -> 'GetEncryptionMaterialsInput': - output: GetEncryptionMaterialsInput = GetEncryptionMaterialsInput( - encryption_context=request.encryption_context, - commitment_policy=CMMHandler._native_to_mpl_commmitment_policy( - request.commitment_policy - ), - max_plaintext_length=request.plaintext_length, - ) - return output - - @staticmethod - def _native_to_mpl_commmitment_policy( - native_commitment_policy: CommitmentPolicy - ) -> 'CommitmentPolicyESDK': - if native_commitment_policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT: - return CommitmentPolicyESDK(value="FORBID_ENCRYPT_ALLOW_DECRYPT") - elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT: - return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_ALLOW_DECRYPT") - elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT: - return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_REQUIRE_DECRYPT") - else: - raise ValueError(f"Invalid native_commitment_policy: {native_commitment_policy}") - - def decrypt_materials( - self, - request: DecryptionMaterialsRequest - ) -> DecryptionMaterialsHandler: - """ - Returns a DecryptionMaterialsHandler for the configured CMM. - :param request: Request for decryption materials - """ - if self._is_using_native_cmm(): - return DecryptionMaterialsHandler(self.native_cmm.decrypt_materials(request)) - else: - try: - mpl_input: 'DecryptMaterialsInput' = \ - CMMHandler._create_mpl_decrypt_materials_input_from_request(request) - mpl_output: 'DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(mpl_input) - return DecryptionMaterialsHandler(mpl_output.decryption_materials) - except AwsCryptographicMaterialProvidersException as mpl_exception: - # Wrap MPL error into the ESDK error type - # so customers only have to catch ESDK error types. - raise AWSEncryptionSDKClientError(mpl_exception) - - @staticmethod - def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> 'AlgorithmSuiteIdESDK': - # MPL algorithm suite ID = hexstr(native_algorithm_id) padded to 4 digits post-`x`. - return AlgorithmSuiteIdESDK(f"{native_algorithm_id:#0{6}x}") - - @staticmethod - def _create_mpl_decrypt_materials_input_from_request( - request: DecryptionMaterialsRequest - ) -> 'DecryptMaterialsInput': - key_blob_list: List[Native_EncryptedDataKey] = request.encrypted_data_keys - list_edks = [MPL_EncryptedDataKey( - key_provider_id=key_blob.key_provider.provider_id, - key_provider_info=key_blob.key_provider.key_info, - ciphertext=key_blob.encrypted_data_key, - ) for key_blob in key_blob_list] - output: DecryptMaterialsInput = DecryptMaterialsInput( - algorithm_suite_id=CMMHandler._native_algorithm_id_to_mpl_algorithm_id( - request.algorithm.algorithm_id - ), - commitment_policy=CMMHandler._native_to_mpl_commmitment_policy( - request.commitment_policy - ), - encrypted_data_keys=list_edks, - encryption_context=request.encryption_context, - ) - return output diff --git a/src/aws_encryption_sdk/materials_handlers.py b/src/aws_encryption_sdk/materials_handlers.py deleted file mode 100644 index 57f54144e..000000000 --- a/src/aws_encryption_sdk/materials_handlers.py +++ /dev/null @@ -1,170 +0,0 @@ -"""Provides encryption/decryption materials from an underlying materials provider.""" -# These dependencies are only loaded if you install the MPL. -try: - from aws_cryptographic_materialproviders.mpl.models import ( - DecryptionMaterials as MPL_DecryptionMaterials, - EncryptedDataKey as MPL_EncryptedDataKey, - EncryptionMaterials as MPL_EncryptionMaterials, - ) -except ImportError: - pass - -from typing import Dict, List, Set - -from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite -from aws_encryption_sdk.materials_managers import ( - DecryptionMaterials as Native_DecryptionMaterials, - EncryptionMaterials as Native_EncryptionMaterials, -) -from aws_encryption_sdk.structures import DataKey, EncryptedDataKey as Native_EncryptedDataKey, MasterKeyInfo - - -def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str): - # MPL algorithm suite ID == hex(native algorithm suite ID) - return int(mpl_algorithm_id, 16) - - -class EncryptionMaterialsHandler: - """ - In instances where encryption materials may be provided by either - the native `aws_encryption_sdk.materials_managers.EncryptionMaterials` - or the MPL's `aws_cryptographic_materialproviders.mpl.models.EncryptionMaterials`, - this provides the correct materials based on the configured materials provider. - """ - - native_materials: Native_EncryptionMaterials - mpl_materials: 'MPL_EncryptionMaterials' - - def __init__( - self, - materials: 'Native_EncryptionMaterials | MPL_EncryptionMaterials' - ): - """ - Create EncryptionMaterialsHandler. - :param materials: Underlying encryption materials - """ - if isinstance(materials, Native_EncryptionMaterials): - self.native_materials = materials - elif isinstance(materials, MPL_EncryptionMaterials): - self.mpl_materials = materials - else: - raise ValueError(f"Invalid EncryptionMaterials passed to EncryptionMaterialsHandler.\ - materials: {materials}") - - @property - def algorithm(self) -> Algorithm: - """Materials' native Algorithm.""" - if hasattr(self, "native_materials"): - return self.native_materials.algorithm - else: - return AlgorithmSuite.get_by_id( - _mpl_algorithm_id_to_native_algorithm_id( - self.mpl_materials.algorithm_suite.id.value - ) - ) - - @property - def encryption_context(self) -> Dict[str, str]: - """Materials' encryption context.""" - if hasattr(self, "native_materials"): - return self.native_materials.encryption_context - else: - return self.mpl_materials.encryption_context - - @property - def encrypted_data_keys(self) -> List[Native_EncryptedDataKey]: - """Materials' encrypted data keys.""" - if hasattr(self, "native_materials"): - return self.native_materials.encrypted_data_keys - else: - mpl_edk_list: List[MPL_EncryptedDataKey] = self.mpl_materials.encrypted_data_keys - key_blob_list: Set[Native_EncryptedDataKey] = {Native_EncryptedDataKey( - key_provider=MasterKeyInfo( - provider_id=mpl_edk.key_provider_id, - key_info=mpl_edk.key_provider_info, - ), - encrypted_data_key=mpl_edk.ciphertext, - ) for mpl_edk in mpl_edk_list} - return key_blob_list - - @property - def data_encryption_key(self) -> DataKey: - """Materials' data encryption key.""" - if hasattr(self, "native_materials"): - return self.native_materials.data_encryption_key - else: - # TODO-MPL This impl is probably wrong, but works for for now - # If this works for all features, great! Remove this comment before launch. - # Otherwise, fix the implementation. - mpl_dek = self.mpl_materials.plaintext_data_key - return DataKey( - # key_provider is unused, but the return type is DataKey - key_provider=MasterKeyInfo( - provider_id="", - key_info=b'' - ), - data_key=mpl_dek, - encrypted_data_key=b'', # No encrypted DEK - ) - - @property - def signing_key(self) -> bytes: - """Materials' signing key.""" - if hasattr(self, "native_materials"): - return self.native_materials.signing_key - else: - return self.mpl_materials.signing_key - - -class DecryptionMaterialsHandler: - """ - In instances where decryption materials may be provided by either - the native `aws_encryption_sdk.materials_managers.DecryptionMaterials` - or the MPL's `aws_cryptographic_materialproviders.mpl.models.DecryptionMaterials`, - this provides the correct materials based on the configured materials provider. - """ - - native_materials: Native_DecryptionMaterials - mpl_materials: 'MPL_DecryptionMaterials' - - def __init__( - self, - materials: 'Native_DecryptionMaterials | MPL_DecryptionMaterials' - ): - """ - Create DecryptionMaterialsHandler. - :param materials: Underlying decryption materials - """ - if isinstance(materials, Native_DecryptionMaterials): - self.native_materials = materials - elif isinstance(materials, MPL_DecryptionMaterials): - self.mpl_materials = materials - else: - raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler.\ - materials: {materials}") - - @property - def data_key(self) -> DataKey: - """Materials' data key.""" - if hasattr(self, "native_materials"): - return self.native_materials.data_key - else: - # TODO-MPL This impl is probably wrong, but works for for now - # If this works for all features, great! Remove this comment before launch. - # Otherwise, fix the implementation. - return DataKey( - key_provider=MasterKeyInfo( - provider_id="", - key_info=b'' - ), - data_key=self.mpl_materials.plaintext_data_key, - encrypted_data_key=b'', - ) - - @property - def verification_key(self) -> bytes: - """Materials' verification key.""" - if hasattr(self, "native_materials"): - return self.native_materials.verification_key - else: - return self.mpl_materials.verification_key From e2e185844e0ac83f07dce346156ed9a7ab693275 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 15:09:10 -0800 Subject: [PATCH 045/172] fix --- src/aws_encryption_sdk/mpl/cmm_handler.py | 158 ++++++++++++++++ .../mpl/materials_handlers.py | 170 ++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 src/aws_encryption_sdk/mpl/cmm_handler.py create mode 100644 src/aws_encryption_sdk/mpl/materials_handlers.py diff --git a/src/aws_encryption_sdk/mpl/cmm_handler.py b/src/aws_encryption_sdk/mpl/cmm_handler.py new file mode 100644 index 000000000..5dfaab973 --- /dev/null +++ b/src/aws_encryption_sdk/mpl/cmm_handler.py @@ -0,0 +1,158 @@ +"""Retrieves encryption/decryption materials from an underlying materials provider.""" + +# These dependencies are only loaded if you install the MPL. +try: + # pylint seems to struggle with this conditional import + # pylint: disable=unused-import + from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException + from aws_cryptographic_materialproviders.mpl.models import ( + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, + DecryptMaterialsInput, + DecryptMaterialsOutput, + EncryptedDataKey as MPL_EncryptedDataKey, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, + ) + from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager + +except ImportError: + pass + +from typing import List + +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.identifiers import CommitmentPolicy +from aws_encryption_sdk.mpl.materials_handlers import DecryptionMaterialsHandler, EncryptionMaterialsHandler +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey + + +# TODO-MPL Should this implement interface...? seems like yes since it implements all of interface methods +class CMMHandler(CryptoMaterialsManager): + """ + In instances where encryption materials may be provided by either + an implementation of the native + `aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager` + or an implementation of the MPL's + `aws_cryptographic_materialproviders.mpl.references.ICryptographicMaterialsManager`, + this provides the correct materials based on the underlying materials manager. + """ + + native_cmm: CryptoMaterialsManager + mpl_cmm: 'ICryptographicMaterialsManager' + + def _is_using_native_cmm(self): + return hasattr(self, "native_cmm") and not hasattr(self, "mpl_cmm") + + def __init__( + self, + cmm: 'CryptoMaterialsManager | ICryptographicMaterialsManager' + ): + """ + Create DecryptionMaterialsHandler. + :param cmm: Underlying cryptographic materials manager + """ + if isinstance(cmm, CryptoMaterialsManager): + self.native_cmm = cmm + elif isinstance(cmm, ICryptographicMaterialsManager): + self.mpl_cmm = cmm + else: + raise ValueError(f"Invalid CMM passed to CMMHandler. cmm: {cmm}") + + def get_encryption_materials( + self, + request: EncryptionMaterialsRequest + ) -> EncryptionMaterialsHandler: + """ + Returns an EncryptionMaterialsHandler for the configured CMM. + :param request: Request for encryption materials + """ + if self._is_using_native_cmm(): + return EncryptionMaterialsHandler(self.native_cmm.get_encryption_materials(request)) + else: + try: + mpl_input: GetEncryptionMaterialsInput = CMMHandler._native_to_mpl_get_encryption_materials( + request + ) + mpl_output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) + return EncryptionMaterialsHandler(mpl_output.encryption_materials) + except AwsCryptographicMaterialProvidersException as mpl_exception: + # Wrap MPL error into the ESDK error type + # so customers only have to catch ESDK error types. + raise AWSEncryptionSDKClientError(mpl_exception) + + @staticmethod + def _native_to_mpl_get_encryption_materials( + request: EncryptionMaterialsRequest + ) -> 'GetEncryptionMaterialsInput': + output: GetEncryptionMaterialsInput = GetEncryptionMaterialsInput( + encryption_context=request.encryption_context, + commitment_policy=CMMHandler._native_to_mpl_commmitment_policy( + request.commitment_policy + ), + max_plaintext_length=request.plaintext_length, + ) + return output + + @staticmethod + def _native_to_mpl_commmitment_policy( + native_commitment_policy: CommitmentPolicy + ) -> 'CommitmentPolicyESDK': + if native_commitment_policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT: + return CommitmentPolicyESDK(value="FORBID_ENCRYPT_ALLOW_DECRYPT") + elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT: + return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_ALLOW_DECRYPT") + elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT: + return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_REQUIRE_DECRYPT") + else: + raise ValueError(f"Invalid native_commitment_policy: {native_commitment_policy}") + + def decrypt_materials( + self, + request: DecryptionMaterialsRequest + ) -> DecryptionMaterialsHandler: + """ + Returns a DecryptionMaterialsHandler for the configured CMM. + :param request: Request for decryption materials + """ + if self._is_using_native_cmm(): + return DecryptionMaterialsHandler(self.native_cmm.decrypt_materials(request)) + else: + try: + mpl_input: 'DecryptMaterialsInput' = \ + CMMHandler._create_mpl_decrypt_materials_input_from_request(request) + mpl_output: 'DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(mpl_input) + return DecryptionMaterialsHandler(mpl_output.decryption_materials) + except AwsCryptographicMaterialProvidersException as mpl_exception: + # Wrap MPL error into the ESDK error type + # so customers only have to catch ESDK error types. + raise AWSEncryptionSDKClientError(mpl_exception) + + @staticmethod + def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> 'AlgorithmSuiteIdESDK': + # MPL algorithm suite ID = hexstr(native_algorithm_id) padded to 4 digits post-`x`. + return AlgorithmSuiteIdESDK(f"{native_algorithm_id:#0{6}x}") + + @staticmethod + def _create_mpl_decrypt_materials_input_from_request( + request: DecryptionMaterialsRequest + ) -> 'DecryptMaterialsInput': + key_blob_list: List[Native_EncryptedDataKey] = request.encrypted_data_keys + list_edks = [MPL_EncryptedDataKey( + key_provider_id=key_blob.key_provider.provider_id, + key_provider_info=key_blob.key_provider.key_info, + ciphertext=key_blob.encrypted_data_key, + ) for key_blob in key_blob_list] + output: DecryptMaterialsInput = DecryptMaterialsInput( + algorithm_suite_id=CMMHandler._native_algorithm_id_to_mpl_algorithm_id( + request.algorithm.algorithm_id + ), + commitment_policy=CMMHandler._native_to_mpl_commmitment_policy( + request.commitment_policy + ), + encrypted_data_keys=list_edks, + encryption_context=request.encryption_context, + ) + return output diff --git a/src/aws_encryption_sdk/mpl/materials_handlers.py b/src/aws_encryption_sdk/mpl/materials_handlers.py new file mode 100644 index 000000000..57f54144e --- /dev/null +++ b/src/aws_encryption_sdk/mpl/materials_handlers.py @@ -0,0 +1,170 @@ +"""Provides encryption/decryption materials from an underlying materials provider.""" +# These dependencies are only loaded if you install the MPL. +try: + from aws_cryptographic_materialproviders.mpl.models import ( + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, + ) +except ImportError: + pass + +from typing import Dict, List, Set + +from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite +from aws_encryption_sdk.materials_managers import ( + DecryptionMaterials as Native_DecryptionMaterials, + EncryptionMaterials as Native_EncryptionMaterials, +) +from aws_encryption_sdk.structures import DataKey, EncryptedDataKey as Native_EncryptedDataKey, MasterKeyInfo + + +def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str): + # MPL algorithm suite ID == hex(native algorithm suite ID) + return int(mpl_algorithm_id, 16) + + +class EncryptionMaterialsHandler: + """ + In instances where encryption materials may be provided by either + the native `aws_encryption_sdk.materials_managers.EncryptionMaterials` + or the MPL's `aws_cryptographic_materialproviders.mpl.models.EncryptionMaterials`, + this provides the correct materials based on the configured materials provider. + """ + + native_materials: Native_EncryptionMaterials + mpl_materials: 'MPL_EncryptionMaterials' + + def __init__( + self, + materials: 'Native_EncryptionMaterials | MPL_EncryptionMaterials' + ): + """ + Create EncryptionMaterialsHandler. + :param materials: Underlying encryption materials + """ + if isinstance(materials, Native_EncryptionMaterials): + self.native_materials = materials + elif isinstance(materials, MPL_EncryptionMaterials): + self.mpl_materials = materials + else: + raise ValueError(f"Invalid EncryptionMaterials passed to EncryptionMaterialsHandler.\ + materials: {materials}") + + @property + def algorithm(self) -> Algorithm: + """Materials' native Algorithm.""" + if hasattr(self, "native_materials"): + return self.native_materials.algorithm + else: + return AlgorithmSuite.get_by_id( + _mpl_algorithm_id_to_native_algorithm_id( + self.mpl_materials.algorithm_suite.id.value + ) + ) + + @property + def encryption_context(self) -> Dict[str, str]: + """Materials' encryption context.""" + if hasattr(self, "native_materials"): + return self.native_materials.encryption_context + else: + return self.mpl_materials.encryption_context + + @property + def encrypted_data_keys(self) -> List[Native_EncryptedDataKey]: + """Materials' encrypted data keys.""" + if hasattr(self, "native_materials"): + return self.native_materials.encrypted_data_keys + else: + mpl_edk_list: List[MPL_EncryptedDataKey] = self.mpl_materials.encrypted_data_keys + key_blob_list: Set[Native_EncryptedDataKey] = {Native_EncryptedDataKey( + key_provider=MasterKeyInfo( + provider_id=mpl_edk.key_provider_id, + key_info=mpl_edk.key_provider_info, + ), + encrypted_data_key=mpl_edk.ciphertext, + ) for mpl_edk in mpl_edk_list} + return key_blob_list + + @property + def data_encryption_key(self) -> DataKey: + """Materials' data encryption key.""" + if hasattr(self, "native_materials"): + return self.native_materials.data_encryption_key + else: + # TODO-MPL This impl is probably wrong, but works for for now + # If this works for all features, great! Remove this comment before launch. + # Otherwise, fix the implementation. + mpl_dek = self.mpl_materials.plaintext_data_key + return DataKey( + # key_provider is unused, but the return type is DataKey + key_provider=MasterKeyInfo( + provider_id="", + key_info=b'' + ), + data_key=mpl_dek, + encrypted_data_key=b'', # No encrypted DEK + ) + + @property + def signing_key(self) -> bytes: + """Materials' signing key.""" + if hasattr(self, "native_materials"): + return self.native_materials.signing_key + else: + return self.mpl_materials.signing_key + + +class DecryptionMaterialsHandler: + """ + In instances where decryption materials may be provided by either + the native `aws_encryption_sdk.materials_managers.DecryptionMaterials` + or the MPL's `aws_cryptographic_materialproviders.mpl.models.DecryptionMaterials`, + this provides the correct materials based on the configured materials provider. + """ + + native_materials: Native_DecryptionMaterials + mpl_materials: 'MPL_DecryptionMaterials' + + def __init__( + self, + materials: 'Native_DecryptionMaterials | MPL_DecryptionMaterials' + ): + """ + Create DecryptionMaterialsHandler. + :param materials: Underlying decryption materials + """ + if isinstance(materials, Native_DecryptionMaterials): + self.native_materials = materials + elif isinstance(materials, MPL_DecryptionMaterials): + self.mpl_materials = materials + else: + raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler.\ + materials: {materials}") + + @property + def data_key(self) -> DataKey: + """Materials' data key.""" + if hasattr(self, "native_materials"): + return self.native_materials.data_key + else: + # TODO-MPL This impl is probably wrong, but works for for now + # If this works for all features, great! Remove this comment before launch. + # Otherwise, fix the implementation. + return DataKey( + key_provider=MasterKeyInfo( + provider_id="", + key_info=b'' + ), + data_key=self.mpl_materials.plaintext_data_key, + encrypted_data_key=b'', + ) + + @property + def verification_key(self) -> bytes: + """Materials' verification key.""" + if hasattr(self, "native_materials"): + return self.native_materials.verification_key + else: + return self.mpl_materials.verification_key From c790011ed67a16cfb6790ad56941c12099552bbe Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 15:12:52 -0800 Subject: [PATCH 046/172] mpl --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index afe9987ff..680784b1a 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -24,7 +24,7 @@ import six import aws_encryption_sdk.internal.utils -from aws_encryption_sdk.cmm_handler import CMMHandler +from aws_encryption_sdk.mpl.cmm_handler import CMMHandler from aws_encryption_sdk.exceptions import ( ActionNotAllowedError, AWSEncryptionSDKClientError, From 33ace5897bb532fbc15942c3317bee8e90215adc Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 15:18:27 -0800 Subject: [PATCH 047/172] fix --- src/aws_encryption_sdk/mpl/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/aws_encryption_sdk/mpl/__init__.py diff --git a/src/aws_encryption_sdk/mpl/__init__.py b/src/aws_encryption_sdk/mpl/__init__.py new file mode 100644 index 000000000..41497cc20 --- /dev/null +++ b/src/aws_encryption_sdk/mpl/__init__.py @@ -0,0 +1,13 @@ +# 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. +"""Modules related to the MPL.""" From cbf2cdf79d107bb371ed76646307f9e3ec038d9e Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 15:32:30 -0800 Subject: [PATCH 048/172] fix --- src/aws_encryption_sdk/mpl/cmm_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/mpl/cmm_handler.py b/src/aws_encryption_sdk/mpl/cmm_handler.py index 5dfaab973..2ccbcb5f3 100644 --- a/src/aws_encryption_sdk/mpl/cmm_handler.py +++ b/src/aws_encryption_sdk/mpl/cmm_handler.py @@ -29,7 +29,7 @@ from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey -# TODO-MPL Should this implement interface...? seems like yes since it implements all of interface methods +# TODO-MPL Should this implement interface..? seems like yes since it implements all of interface methods class CMMHandler(CryptoMaterialsManager): """ In instances where encryption materials may be provided by either From b2594771818c265c6f6ce31a56a4339b86840f38 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 7 Feb 2024 15:43:03 -0800 Subject: [PATCH 049/172] fix --- src/aws_encryption_sdk/mpl/cmm_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/mpl/cmm_handler.py b/src/aws_encryption_sdk/mpl/cmm_handler.py index 2ccbcb5f3..5dfaab973 100644 --- a/src/aws_encryption_sdk/mpl/cmm_handler.py +++ b/src/aws_encryption_sdk/mpl/cmm_handler.py @@ -29,7 +29,7 @@ from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey -# TODO-MPL Should this implement interface..? seems like yes since it implements all of interface methods +# TODO-MPL Should this implement interface...? seems like yes since it implements all of interface methods class CMMHandler(CryptoMaterialsManager): """ In instances where encryption materials may be provided by either From 9d52cf2ad37bf69fcc42bee1375365ea4e19bf15 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 8 Feb 2024 14:18:52 -0800 Subject: [PATCH 050/172] . --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 680784b1a..04d44334a 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -915,7 +915,7 @@ def _read_header(self): self.verifier = None else: # MPL verification key is NOT key bytes, it is bytes of the compressed point - # TODO-MPL: clean this up, least-privilege violation + # TODO-MPL: clean this up, least-privilege violation. if (isinstance(self.config.materials_manager, CMMHandler) and hasattr(self.config.materials_manager, "mpl_cmm")): self.verifier = Verifier.from_encoded_point( From 31b761616f7741293c8e6a33e2e9174c758f7c70 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 9 Feb 2024 09:45:18 -0800 Subject: [PATCH 051/172] debug tox mpl keystore env --- codebuild/py311/examples_mpl.yml | 6 ++++-- codebuild/py312/examples_mpl.yml | 5 +++-- tox.ini | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/codebuild/py311/examples_mpl.yml b/codebuild/py311/examples_mpl.yml index e29472507..b1afa5016 100644 --- a/codebuild/py311/examples_mpl.yml +++ b/codebuild/py311/examples_mpl.yml @@ -2,7 +2,7 @@ version: 0.2 env: variables: - TOXENV: "py311-examples-mpl" + # No TOXENV; examples using the MPL switch envs REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f @@ -20,4 +20,6 @@ phases: build: commands: - pip install "tox < 4.0" - - tox + - tox -e py311-examples-mpl + - tox -e py311-examples-mpl-keystore + diff --git a/codebuild/py312/examples_mpl.yml b/codebuild/py312/examples_mpl.yml index ff2168cd5..cf53585b4 100644 --- a/codebuild/py312/examples_mpl.yml +++ b/codebuild/py312/examples_mpl.yml @@ -2,7 +2,7 @@ version: 0.2 env: variables: - TOXENV: "py312-examples-mpl" + # No TOXENV; examples using the MPL switch envs REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f @@ -25,4 +25,5 @@ phases: - pip install --upgrade pip - pip install setuptools - pip install "tox < 4.0" - - tox + - tox -e py312-examples-mpl + - tox -e py312-examples-mpl-keystore diff --git a/tox.ini b/tox.ini index 4202979a4..d618cb030 100644 --- a/tox.ini +++ b/tox.ini @@ -75,8 +75,8 @@ commands = integ: {[testenv:base-command]commands} test/ -m integ accept: {[testenv:base-command]commands} test/ -m accept examples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/keyrings/ - # append MPL examples to base examples command - examples-mpl: {[testenv:base-command]commands} examples/test/ -m examples + # MPL keyring examples require a special IAM role; run these separately under a separate set of permissions + examples-mpl-keyring: {[testenv:base-command]commands} examples/test/keyrings -m examples all: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} From 353b8cfc944b437bfb86f24390c0019693b5b76f Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 9 Feb 2024 09:51:54 -0800 Subject: [PATCH 052/172] debug tox mpl keystore env --- codebuild/py311/examples_mpl.yml | 2 +- codebuild/py312/examples_mpl.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codebuild/py311/examples_mpl.yml b/codebuild/py311/examples_mpl.yml index b1afa5016..c5b1e1872 100644 --- a/codebuild/py311/examples_mpl.yml +++ b/codebuild/py311/examples_mpl.yml @@ -21,5 +21,5 @@ phases: commands: - pip install "tox < 4.0" - tox -e py311-examples-mpl - - tox -e py311-examples-mpl-keystore + - tox -e py311-examples-mpl-keyring diff --git a/codebuild/py312/examples_mpl.yml b/codebuild/py312/examples_mpl.yml index cf53585b4..97a19ad50 100644 --- a/codebuild/py312/examples_mpl.yml +++ b/codebuild/py312/examples_mpl.yml @@ -26,4 +26,4 @@ phases: - pip install setuptools - pip install "tox < 4.0" - tox -e py312-examples-mpl - - tox -e py312-examples-mpl-keystore + - tox -e py312-examples-mpl-keyring From fb64d950a31a82169cbedaf289367dc3001a0c68 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 9 Feb 2024 09:57:15 -0800 Subject: [PATCH 053/172] debug tox mpl keystore env --- codebuild/py311/examples_mpl.yml | 8 ++++++++ codebuild/py312/examples_mpl.yml | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/codebuild/py311/examples_mpl.yml b/codebuild/py311/examples_mpl.yml index c5b1e1872..f7d705923 100644 --- a/codebuild/py311/examples_mpl.yml +++ b/codebuild/py311/examples_mpl.yml @@ -21,5 +21,13 @@ phases: commands: - pip install "tox < 4.0" - tox -e py311-examples-mpl + # Assume special role + - TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-Py311ExamplesMpl") + - export TMP_ROLE + - export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId') + - export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey') + - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') + - aws sts get-caller-identity + # Run special role-specific examples - tox -e py311-examples-mpl-keyring diff --git a/codebuild/py312/examples_mpl.yml b/codebuild/py312/examples_mpl.yml index 97a19ad50..c95f606e6 100644 --- a/codebuild/py312/examples_mpl.yml +++ b/codebuild/py312/examples_mpl.yml @@ -26,4 +26,12 @@ phases: - pip install setuptools - pip install "tox < 4.0" - tox -e py312-examples-mpl + # Assume special role + - TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-Py311ExamplesMpl") + - export TMP_ROLE + - export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId') + - export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey') + - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') + - aws sts get-caller-identity + # Run special role-specific examples - tox -e py312-examples-mpl-keyring From 916ae8e00b6195ed91dcbfa0448aa0e2c23c49bc Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 9 Feb 2024 10:19:26 -0800 Subject: [PATCH 054/172] debug tox mpl keystore env --- .../internal/crypto/authentication.py | 9 +++++++-- src/aws_encryption_sdk/streaming_client.py | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index f90ac77e0..a6446981e 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -68,7 +68,7 @@ class Signer(_PrehashingAuthenticator): """ @classmethod - def from_key_bytes(cls, algorithm, key_bytes): + def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DER): """Builds a `Signer` from an algorithm suite and a raw signing key. :param algorithm: Algorithm on which to base signer @@ -76,7 +76,12 @@ def from_key_bytes(cls, algorithm, key_bytes): :param bytes key_bytes: Raw signing key :rtype: aws_encryption_sdk.internal.crypto.Signer """ - key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) + if encoding == serialization.Encoding.DER: + key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) + elif serialization.Encoding.PEM: + key = serialization.load_pem_private_key(data=key_bytes, password=None, backend=default_backend()) + else: + raise ValueError(f"Unsupported encoding for Signer: {encoding}") return cls(algorithm, key) def key_bytes(self): diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 04d44334a..e514337f5 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -23,6 +23,8 @@ import attr import six +from cryptography.hazmat.primitives import serialization + import aws_encryption_sdk.internal.utils from aws_encryption_sdk.mpl.cmm_handler import CMMHandler from aws_encryption_sdk.exceptions import ( @@ -555,9 +557,18 @@ def _prep_message(self): if self._encryption_materials.signing_key is None: self.signer = None else: - self.signer = Signer.from_key_bytes( - algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key - ) + # MPL verification key is NOT key bytes, it is bytes of the compressed point + # TODO-MPL: clean this up, least-privilege violation. + if (isinstance(self.config.materials_manager, CMMHandler) + and hasattr(self.config.materials_manager, "mpl_cmm")): + self.signer = Signer.from_key_bytes( + algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key, + encoding=serialization.Encoding.PEM, + ) + else: + self.signer = Signer.from_key_bytes( + algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key + ) aws_encryption_sdk.internal.utils.validate_frame_length( frame_length=self.config.frame_length, algorithm=self._encryption_materials.algorithm ) From 222b13549febbbfe144e7f865a89203b4f3789fd Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 9 Feb 2024 10:38:14 -0800 Subject: [PATCH 055/172] debug tox mpl keystore env --- codebuild/py311/examples_mpl.yml | 2 +- codebuild/py312/examples_mpl.yml | 2 +- tox.ini | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/codebuild/py311/examples_mpl.yml b/codebuild/py311/examples_mpl.yml index f7d705923..f8f2a6a01 100644 --- a/codebuild/py311/examples_mpl.yml +++ b/codebuild/py311/examples_mpl.yml @@ -29,5 +29,5 @@ phases: - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') - aws sts get-caller-identity # Run special role-specific examples - - tox -e py311-examples-mpl-keyring + - tox -e py311-mplexamples-mpl diff --git a/codebuild/py312/examples_mpl.yml b/codebuild/py312/examples_mpl.yml index c95f606e6..ba0660024 100644 --- a/codebuild/py312/examples_mpl.yml +++ b/codebuild/py312/examples_mpl.yml @@ -34,4 +34,4 @@ phases: - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') - aws sts get-caller-identity # Run special role-specific examples - - tox -e py312-examples-mpl-keyring + - tox -e py312-mplexamples-mpl diff --git a/tox.ini b/tox.ini index d618cb030..61d65b11d 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,11 @@ envlist = # <3.11: run all non-MPL tests py{37,38,39,310}-{local,integ,accept,examples}, - # >=3.11: run all MPL tests and non-MPL tests + # >=3.11: run all tests with MPL installed and without MPL installed + # The `-mpl` suffix tells tox to install the MPL py{311,312}-{local,integ,accept,examples}{,-mpl}, + # >=3.11: run ONLY the MPL-specific tests (requires a special IAM role) + py{311,312}-{mplexamples}-mpl nocmk, bandit, doc8, readme, docs, {flake8,pylint}{,-tests,-examples}, @@ -76,7 +79,7 @@ commands = accept: {[testenv:base-command]commands} test/ -m accept examples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/keyrings/ # MPL keyring examples require a special IAM role; run these separately under a separate set of permissions - examples-mpl-keyring: {[testenv:base-command]commands} examples/test/keyrings -m examples + mplexamples: {[testenv:base-command]commands} examples/test/keyrings -m examples all: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} From cab60167b76eb142258b97f2493879ad0028e818 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 9 Feb 2024 16:03:12 -0800 Subject: [PATCH 056/172] some unit tests --- src/aws_encryption_sdk/mpl/cmm_handler.py | 2 + .../mpl/materials_handlers.py | 4 +- .../mpl/mpl_import_handler.py | 14 +++ src/aws_encryption_sdk/streaming_client.py | 16 +-- test/unit/mpl/README.md | 1 + test/unit/mpl/test_cmm_handler.py | 111 ++++++++++++++++++ test/unit/test_mpl_import_handler.py | 34 ++++++ test/unit/test_streaming_client_configs.py | 3 + test/unit/test_streaming_client_mpl_import.py | 42 +++++++ tox.ini | 6 +- 10 files changed, 221 insertions(+), 12 deletions(-) create mode 100644 src/aws_encryption_sdk/mpl/mpl_import_handler.py create mode 100644 test/unit/mpl/README.md create mode 100644 test/unit/mpl/test_cmm_handler.py create mode 100644 test/unit/test_mpl_import_handler.py create mode 100644 test/unit/test_streaming_client_mpl_import.py diff --git a/src/aws_encryption_sdk/mpl/cmm_handler.py b/src/aws_encryption_sdk/mpl/cmm_handler.py index 5dfaab973..1f6c9ff41 100644 --- a/src/aws_encryption_sdk/mpl/cmm_handler.py +++ b/src/aws_encryption_sdk/mpl/cmm_handler.py @@ -76,7 +76,9 @@ def get_encryption_materials( mpl_input: GetEncryptionMaterialsInput = CMMHandler._native_to_mpl_get_encryption_materials( request ) + print(f"mpl_input: {mpl_input}") mpl_output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) + print(f"mpl_output: {mpl_output}") return EncryptionMaterialsHandler(mpl_output.encryption_materials) except AwsCryptographicMaterialProvidersException as mpl_exception: # Wrap MPL error into the ESDK error type diff --git a/src/aws_encryption_sdk/mpl/materials_handlers.py b/src/aws_encryption_sdk/mpl/materials_handlers.py index 57f54144e..df5b57d53 100644 --- a/src/aws_encryption_sdk/mpl/materials_handlers.py +++ b/src/aws_encryption_sdk/mpl/materials_handlers.py @@ -48,8 +48,8 @@ def __init__( elif isinstance(materials, MPL_EncryptionMaterials): self.mpl_materials = materials else: - raise ValueError(f"Invalid EncryptionMaterials passed to EncryptionMaterialsHandler.\ - materials: {materials}") + raise ValueError("Invalid EncryptionMaterials passed to EncryptionMaterialsHandler. " \ + f"materials: {materials}") @property def algorithm(self) -> Algorithm: diff --git a/src/aws_encryption_sdk/mpl/mpl_import_handler.py b/src/aws_encryption_sdk/mpl/mpl_import_handler.py new file mode 100644 index 000000000..40669da1e --- /dev/null +++ b/src/aws_encryption_sdk/mpl/mpl_import_handler.py @@ -0,0 +1,14 @@ +def has_mpl(): + """Returns True if the aws_cryptographic_materialproviders library is installed, False otherwise.""" + try: + _import_mpl() + return True + except ImportError: + return False + +def _import_mpl(): + """Private wrapper for import to help with unit test coverage. + + This is not directly tested. + """ + import aws_cryptographic_materialproviders \ No newline at end of file diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index e514337f5..106121377 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -71,15 +71,15 @@ from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import MessageHeader -try: +from aws_encryption_sdk.mpl import mpl_import_handler +if mpl_import_handler.has_mpl(): from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput from aws_cryptographic_materialproviders.mpl.references import IKeyring - - HAS_MPL = True -except ImportError: - HAS_MPL = False + _HAS_MPL = True +else: + _HAS_MPL = False _LOGGER = logging.getLogger(__name__) @@ -146,7 +146,7 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes key_provider = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(MasterKeyProvider)) ) - if HAS_MPL: + if _HAS_MPL: keyring = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(IKeyring)) ) @@ -194,9 +194,9 @@ def _no_mpl_attrs_post_init(self): def __attrs_post_init__(self): """Normalize inputs to crypto material manager.""" - if HAS_MPL: + if _HAS_MPL: self._has_mpl_attrs_post_init() - elif not HAS_MPL: + else: self._no_mpl_attrs_post_init() diff --git a/test/unit/mpl/README.md b/test/unit/mpl/README.md new file mode 100644 index 000000000..839feb7a2 --- /dev/null +++ b/test/unit/mpl/README.md @@ -0,0 +1 @@ +Tests in this file REQUIRE the aws-cryptographic-material-providers module to be installed in order to run. \ No newline at end of file diff --git a/test/unit/mpl/test_cmm_handler.py b/test/unit/mpl/test_cmm_handler.py new file mode 100644 index 000000000..45b49ed91 --- /dev/null +++ b/test/unit/mpl/test_cmm_handler.py @@ -0,0 +1,111 @@ +# 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. +"""Test suite to verify the mpl_import_handler module handles importing the MPL correctly.""" +import pytest +from mock import MagicMock, patch + +from aws_encryption_sdk.mpl.cmm_handler import CMMHandler + +from aws_encryption_sdk.mpl.materials_handlers import DecryptionMaterialsHandler, EncryptionMaterialsHandler +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager + +from aws_encryption_sdk.materials_managers import ( + DecryptionMaterials as Native_DecryptionMaterials, + EncryptionMaterials as Native_EncryptionMaterials, +) + +from aws_cryptographic_materialproviders.mpl.models import ( + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, + DecryptMaterialsInput, + DecryptMaterialsOutput, + EncryptedDataKey as MPL_EncryptedDataKey, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, + ) + +from aws_cryptographic_materialproviders.mpl.models import ( + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, + ) + +mock_native_cmm = MagicMock(__class__=CryptoMaterialsManager) +mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) +mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) +mock_encryption_materials_handler = MagicMock(__class__=EncryptionMaterialsHandler) +mock_native_encryption_materials = MagicMock(__class__=Native_EncryptionMaterials) +mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +def test_GIVEN_native_CMM_WHEN_create_CMMHandler_THEN_is_using_native_cmm_returns_True(): + cmm_handler = CMMHandler(cmm=mock_native_cmm) + assert cmm_handler._is_using_native_cmm() + + +def test_GIVEN_mpl_CMM_WHEN_create_CMMHandler_THEN_is_using_native_cmm_returns_False(): + cmm_handler = CMMHandler(cmm=mock_mpl_cmm) + assert not cmm_handler._is_using_native_cmm() + + +def test_GIVEN_unknown_CMM_WHEN_create_CMMHandler_THEN_raise_ValueError(): + with pytest.raises(ValueError): + CMMHandler(cmm="not a CMM") + + +@patch.object(mock_native_cmm, "get_encryption_materials") +def test_GIVEN_native_CMM_WHEN_get_encryption_materials_THEN_return_native_encryption_materials(mock_get_encryption_materials): + # Mock: native_cmm.get_encryption_materials returns mock native encryption materials + mock_get_encryption_materials.return_value = mock_native_encryption_materials + + cmm_handler = CMMHandler(cmm=mock_native_cmm) + test = cmm_handler.get_encryption_materials(mock_encryption_materials_request) + + # Verify cmm_handler returns EncryptionMaterialsHandler + assert isinstance(test, EncryptionMaterialsHandler) + # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` + assert test.native_materials == mock_native_encryption_materials + # Verify we actually called `get_encryption_materials` + mock_native_cmm.get_encryption_materials.assert_called_once_with(mock_encryption_materials_request) + + +@patch.object(mock_mpl_cmm, "get_encryption_materials") +@patch("aws_encryption_sdk.mpl.cmm_handler.CMMHandler._native_to_mpl_get_encryption_materials") +def test_GIVEN_mpl_CMM_WHEN_get_encryption_materials_THEN_return_mpl_encryption_materials( + mock_native_to_mpl_get_encryption_materials, + mock_get_encryption_materials, + +): + # Mock: mpl_cmm.get_encryption_materials returns mock MPL encryption materials + mock_get_encryption_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) + mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials + mock_get_encryption_materials.return_value = mock_get_encryption_materials_output + + # Mock: CMMHandler._native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput + mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) + mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input + + cmm_handler = CMMHandler(cmm=mock_mpl_cmm) + test = cmm_handler.get_encryption_materials(mock_encryption_materials_request) + + # Verify cmm_handler returns EncryptionMaterialsHandler + assert isinstance(test, EncryptionMaterialsHandler) + # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` + assert test.mpl_materials == mock_mpl_encryption_materials + # Verify we actually called `get_encryption_materials` + mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) + diff --git a/test/unit/test_mpl_import_handler.py b/test/unit/test_mpl_import_handler.py new file mode 100644 index 000000000..c17c358b4 --- /dev/null +++ b/test/unit/test_mpl_import_handler.py @@ -0,0 +1,34 @@ +# 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. +"""Test suite to verify the mpl_import_handler module handles importing the MPL correctly.""" +import pytest +from mock import patch + +from aws_encryption_sdk.mpl import mpl_import_handler + +pytestmark = [pytest.mark.unit, pytest.mark.local] + +@patch("aws_encryption_sdk.mpl.mpl_import_handler._import_mpl") +def test_GIVEN_import_mpl_succeeds_WHEN_call_has_mpl_THEN_return_True(import_mock): + # Mock a successful import of `aws_cryptographic_material_providers` + import_mock.return_value = None # No exception means successful import + + assert mpl_import_handler.has_mpl() is True + +@patch("aws_encryption_sdk.mpl.mpl_import_handler._import_mpl") +def test_GIVEN_import_mpl_fails_WHEN_call_has_mpl_THEN_return_False(import_mock): + # Mock not having a `aws_cryptographic_material_providers` module, + # even if it is installed in the Python environment + import_mock.side_effect = ImportError() + + assert not mpl_import_handler.has_mpl() \ No newline at end of file diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 426f8f85f..80b7fdb28 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -154,3 +154,6 @@ def test_client_config_converts(kwargs, stream_type): assert isinstance(test.source, stream_type) if test.key_provider is not None: assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) + +def test_GIVEN_has_mpl_WHEN_import_THEN_imports_mpl_modules(): + \ No newline at end of file diff --git a/test/unit/test_streaming_client_mpl_import.py b/test/unit/test_streaming_client_mpl_import.py new file mode 100644 index 000000000..8ce016caf --- /dev/null +++ b/test/unit/test_streaming_client_mpl_import.py @@ -0,0 +1,42 @@ +# 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 to validate aws_encryption_sdk.streaming_client MPL import logic.""" +import io + +import pytest +from mock import patch +from importlib import reload + +import aws_encryption_sdk.streaming_client + +pytestmark = [pytest.mark.unit, pytest.mark.local] + +@patch.object(aws_encryption_sdk.streaming_client.mpl_import_handler, "has_mpl") +def test_GIVEN_has_mpl_returns_True_WHEN_import_streaming_client_THEN_imports_mpl_modules(has_mpl_mock): + has_mpl_mock.return_value = True + + # Reload module given the mock + reload(aws_encryption_sdk.streaming_client) + + assert hasattr(aws_encryption_sdk.streaming_client, "_HAS_MPL") + assert aws_encryption_sdk.streaming_client._HAS_MPL is True + +@patch.object(aws_encryption_sdk.streaming_client.mpl_import_handler, "has_mpl") +def test_GIVEN_has_mpl_returns_False_WHEN_import_streaming_client_THEN_does_not_import_mpl_modules(has_mpl_mock): + has_mpl_mock.return_value = False + + # Reload module given the mock + reload(aws_encryption_sdk.streaming_client) + + assert hasattr(aws_encryption_sdk.streaming_client, "_HAS_MPL") + assert aws_encryption_sdk.streaming_client._HAS_MPL is False \ No newline at end of file diff --git a/tox.ini b/tox.ini index 61d65b11d..3cc7017e1 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,8 @@ envlist = # The `-mpl` suffix tells tox to install the MPL py{311,312}-{local,integ,accept,examples}{,-mpl}, # >=3.11: run ONLY the MPL-specific tests (requires a special IAM role) - py{311,312}-{mplexamples}-mpl + # the extra `-mpl` suffix tells tox to install the MPL + py{311,312}-mpl{local,examples}-mpl nocmk, bandit, doc8, readme, docs, {flake8,pylint}{,-tests,-examples}, @@ -74,7 +75,8 @@ deps = # install the MPL if in environment mpl: -rrequirements_mpl.txt commands = - local: {[testenv:base-command]commands} test/ -m local + local: {[testenv:base-command]commands} test/ -m local --ignore test/mpl/ + local: {[testenv:base-command]commands} test/mpl/ -m local integ: {[testenv:base-command]commands} test/ -m integ accept: {[testenv:base-command]commands} test/ -m accept examples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/keyrings/ From a7416b18f9afa367e5053f0bd936735da7ec6e01 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 13 Feb 2024 09:26:04 -0800 Subject: [PATCH 057/172] add mpl coverage --- buildspec.yml | 2 ++ codebuild/coverage/coverage_mpl.yml | 14 ++++++++++++++ tox.ini | 6 +++++- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 codebuild/coverage/coverage_mpl.yml diff --git a/buildspec.yml b/buildspec.yml index c718c3df5..3d70c144d 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -106,6 +106,8 @@ batch: - identifier: code_coverage buildspec: codebuild/coverage/coverage.yml + - identifier: code_coverage_mpl + buildspec: codebuild/coverage/coverage_mpl.yml - identifier: compliance buildspec: codebuild/compliance/compliance.yml diff --git a/codebuild/coverage/coverage_mpl.yml b/codebuild/coverage/coverage_mpl.yml new file mode 100644 index 000000000..5dcc65382 --- /dev/null +++ b/codebuild/coverage/coverage_mpl.yml @@ -0,0 +1,14 @@ +version: 0.2 + +env: + variables: + TOXENV: "mplcoverage-mpl" + +phases: + install: + runtime-versions: + python: latest + build: + commands: + - pip install "tox < 4.0" + - tox diff --git a/tox.ini b/tox.ini index 3cc7017e1..7aacb047a 100644 --- a/tox.ini +++ b/tox.ini @@ -6,6 +6,8 @@ envlist = # The `-mpl` suffix tells tox to install the MPL py{311,312}-{local,integ,accept,examples}{,-mpl}, # >=3.11: run ONLY the MPL-specific tests (requires a special IAM role) + # the `mpl` prefix runs only MPL-specific tests + # (non-MPL-specific tests are run from the line above) # the extra `-mpl` suffix tells tox to install the MPL py{311,312}-mpl{local,examples}-mpl nocmk, @@ -87,7 +89,9 @@ commands = # Run code coverage on the unit tests [testenv:coverage] -commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local +commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local --ignore test/unit/mpl/ +[testenv:mplcoverage] +commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/unit/mpl/ -m local # Verify that local tests work without environment variables present [testenv:nocmk] From 7b3dc5fc303afad5b0d3519b12de833135e3d326 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 13 Feb 2024 09:30:19 -0800 Subject: [PATCH 058/172] . --- tox.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tox.ini b/tox.ini index 7aacb047a..194b4d412 100644 --- a/tox.ini +++ b/tox.ini @@ -77,14 +77,14 @@ deps = # install the MPL if in environment mpl: -rrequirements_mpl.txt commands = - local: {[testenv:base-command]commands} test/ -m local --ignore test/mpl/ - local: {[testenv:base-command]commands} test/mpl/ -m local - integ: {[testenv:base-command]commands} test/ -m integ - accept: {[testenv:base-command]commands} test/ -m accept + local: {[testenv:base-command]commands} test/ -m local --ignore test/unit/mpl/ + mpllocal: {[testenv:base-command]commands} test/unit/mpl/ -m local + integ: {[testenv:base-command]commands} test/ -m integ --ignore test/unit/mpl/ + accept: {[testenv:base-command]commands} test/ -m accept --ignore test/unit/mpl/ examples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/keyrings/ # MPL keyring examples require a special IAM role; run these separately under a separate set of permissions mplexamples: {[testenv:base-command]commands} examples/test/keyrings -m examples - all: {[testenv:base-command]commands} test/ examples/test/ + all: {[testenv:base-command]commands} test/ examples/test/ --ignore test/unit/mpl/ manual: {[testenv:base-command]commands} # Run code coverage on the unit tests From 7a5e4eb9e6055576759d45950bd66ace921d65aa Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 13 Feb 2024 09:33:38 -0800 Subject: [PATCH 059/172] . --- test/unit/test_streaming_client_configs.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 80b7fdb28..426f8f85f 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -154,6 +154,3 @@ def test_client_config_converts(kwargs, stream_type): assert isinstance(test.source, stream_type) if test.key_provider is not None: assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) - -def test_GIVEN_has_mpl_WHEN_import_THEN_imports_mpl_modules(): - \ No newline at end of file From 0649995f59e6b3de7ad68d5ff7497a44dc021c31 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 13 Feb 2024 15:46:47 -0800 Subject: [PATCH 060/172] mock imports --- src/aws_encryption_sdk/streaming_client.py | 2 +- test/unit/test_streaming_client_mpl_import.py | 11 +++++++++++ test/unit/test_streaming_client_stream_decryptor.py | 2 +- tox.ini | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 106121377..044626c7f 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -1095,7 +1095,7 @@ def close(self): """Closes out the stream.""" _LOGGER.debug("Closing stream") if not hasattr(self, "footer"): - raise SerializationError("Footer not read, message may be corrupted or data key may be incorrect") + raise SerializationError("Footer not read") super(StreamDecryptor, self).close() diff --git a/test/unit/test_streaming_client_mpl_import.py b/test/unit/test_streaming_client_mpl_import.py index 8ce016caf..f71f337b7 100644 --- a/test/unit/test_streaming_client_mpl_import.py +++ b/test/unit/test_streaming_client_mpl_import.py @@ -17,6 +17,8 @@ from mock import patch from importlib import reload +from mock import Mock + import aws_encryption_sdk.streaming_client pytestmark = [pytest.mark.unit, pytest.mark.local] @@ -25,6 +27,15 @@ def test_GIVEN_has_mpl_returns_True_WHEN_import_streaming_client_THEN_imports_mpl_modules(has_mpl_mock): has_mpl_mock.return_value = True + # Mock any imports used in the try/catch block + # If more imports are added there, then this needs to be expanded + # This unit test should pass even if the MPL is not installed + import sys + sys.modules['aws_cryptographic_materialproviders.mpl.client'] = Mock() + sys.modules['aws_cryptographic_materialproviders.mpl.config'] = Mock() + sys.modules['aws_cryptographic_materialproviders.mpl.models'] = Mock() + sys.modules['aws_cryptographic_materialproviders.mpl.references'] = Mock() + # Reload module given the mock reload(aws_encryption_sdk.streaming_client) diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index 94b22b092..157755094 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -767,4 +767,4 @@ def test_close_no_footer(self, mock_close): ) with pytest.raises(SerializationError) as excinfo: test_decryptor.close() - excinfo.match("Footer not read, message may be corrupted or data key may be incorrect") + excinfo.match("Footer not read") diff --git a/tox.ini b/tox.ini index 194b4d412..9e2d95477 100644 --- a/tox.ini +++ b/tox.ini @@ -90,7 +90,7 @@ commands = # Run code coverage on the unit tests [testenv:coverage] commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local --ignore test/unit/mpl/ -[testenv:mplcoverage] +[testenv:mplcoverage-mpl] commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/unit/mpl/ -m local # Verify that local tests work without environment variables present From 6691fa2d81c211e7a97be9450f028ca89b47bcb6 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 15:19:17 -0800 Subject: [PATCH 061/172] refactor, fix --- .github/workflows/ci_static-analysis.yaml | 14 +++++- examples/src/keyrings/hierarchical_keyring.py | 50 ++++++++++--------- .../{ => internal}/mpl/__init__.py | 0 .../{ => internal}/mpl/cmm_handler.py | 2 +- .../{ => internal}/mpl/materials_handlers.py | 4 +- .../internal/mpl/mpl_import_handler.py | 21 ++++++++ .../mpl/mpl_import_handler.py | 14 ------ src/aws_encryption_sdk/streaming_client.py | 6 +-- test/unit/mpl/test_cmm_handler.py | 44 ++++++---------- test/unit/test_mpl_import_handler.py | 10 ++-- test/unit/test_streaming_client_mpl_import.py | 12 ++--- tox.ini | 22 +++++--- 12 files changed, 108 insertions(+), 91 deletions(-) rename src/aws_encryption_sdk/{ => internal}/mpl/__init__.py (100%) rename src/aws_encryption_sdk/{ => internal}/mpl/cmm_handler.py (98%) rename src/aws_encryption_sdk/{ => internal}/mpl/materials_handlers.py (98%) create mode 100644 src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py delete mode 100644 src/aws_encryption_sdk/mpl/mpl_import_handler.py diff --git a/.github/workflows/ci_static-analysis.yaml b/.github/workflows/ci_static-analysis.yaml index 0093ae9a9..85d7f4a62 100644 --- a/.github/workflows/ci_static-analysis.yaml +++ b/.github/workflows/ci_static-analysis.yaml @@ -13,6 +13,9 @@ jobs: strategy: fail-fast: false matrix: + python: + - 3.8 + - 3.11 category: - bandit - doc8 @@ -26,15 +29,22 @@ jobs: - pylint-examples - black-check - isort-check + optional_mpl_dependency: + - "" + - -mpl + exclude: + # MPL is not supported on <3.11 + - python: 3.8 + optional_mpl_dependency: -mpl steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: ${{ matrix.python }} - run: | python -m pip install --upgrade pip pip install --upgrade -r dev_requirements/ci-requirements.txt - name: run test env: - TOXENV: ${{ matrix.category }} + TOXENV: ${{ matrix.category }}${{ matrix.optional_mpl_dependency }} run: tox -- -vv diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index a99728b6e..21108d9a0 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -4,23 +4,28 @@ import sys import boto3 -from aws_cryptographic_materialproviders.keystore.client import KeyStore -from aws_cryptographic_materialproviders.keystore.config import KeyStoreConfig -from aws_cryptographic_materialproviders.keystore.models import CreateKeyInput, KMSConfigurationKmsKeyArn -from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders -from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig -from aws_cryptographic_materialproviders.mpl.models import ( - CacheTypeDefault, - CreateAwsKmsHierarchicalKeyringInput, - DefaultCache, - GetBranchKeyIdInput, - GetBranchKeyIdOutput, -) -from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier, IKeyring +from typing import Dict import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.internal.mpl import mpl_import_handler + +if mpl_import_handler.has_mpl(): + # noqa pylint: disable=import-error + from aws_cryptographic_materialproviders.keystore.client import KeyStore + from aws_cryptographic_materialproviders.keystore.config import KeyStoreConfig + from aws_cryptographic_materialproviders.keystore.models import CreateKeyInput, KMSConfigurationKmsKeyArn + from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders + from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig + from aws_cryptographic_materialproviders.mpl.models import ( + CacheTypeDefault, + CreateAwsKmsHierarchicalKeyringInput, + DefaultCache, + GetBranchKeyIdInput, + GetBranchKeyIdOutput, + ) + from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier, IKeyring module_root_dir = '/'.join(__file__.split("/")[:-1]) @@ -71,6 +76,7 @@ def encrypt_and_decrypt_with_keyring( branch_key_id_B: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier class ExampleBranchKeyIdSupplier(IBranchKeyIdSupplier): + """Example implementation of a branch key ID supplier.""" branch_key_id_for_tenant_A: str branch_key_id_for_tenant_B: str @@ -80,9 +86,11 @@ def __init__(self, tenant_1_id, tenant_2_id): def get_branch_key_id( self, - input: GetBranchKeyIdInput + # Change this to `native_input` + input: GetBranchKeyIdInput # noqa pylint: disable=redefined-builtin ) -> GetBranchKeyIdOutput: - encryption_context: dict[str, str] = input.encryption_context + """Returns branch key ID from the tenant ID in input's encryption context.""" + encryption_context: Dict[str, str] = input.encryption_context if b"tenant" not in encryption_context: raise ValueError("EncryptionContext invalid, does not contain expected tenant key value pair.") @@ -128,7 +136,7 @@ def get_branch_key_id( # The Branch Key Id supplier uses the encryption context to determine which branch key id will # be used to encrypt data. # Create encryption context for TenantA - encryption_context_A: dict[str, str] = { + encryption_context_A: Dict[str, str] = { "tenant": "TenantA", "encryption": "context", "is not": "secret", @@ -138,7 +146,7 @@ def get_branch_key_id( } # Create encryption context for TenantB - encryption_context_B: dict[str, str] = { + encryption_context_B: Dict[str, str] = { "tenant": "TenantB", "encryption": "context", "is not": "secret", @@ -191,8 +199,6 @@ def get_branch_key_id( input=keyring_input_B ) - # TODO: Run the decrypt, get expected exception type - # This should fail try: client.decrypt( source=ciphertext_A, @@ -201,7 +207,7 @@ def get_branch_key_id( except AWSEncryptionSDKClientError: pass - # # This should fail + # This should fail try: client.decrypt( source=ciphertext_B, @@ -220,6 +226,4 @@ def get_branch_key_id( source=ciphertext_B, keyring=hierarchical_keyring_B ) - assert plaintext_bytes_B == EXAMPLE_DATA - -# Also, a thread-safe example ig + assert plaintext_bytes_B == EXAMPLE_DATA \ No newline at end of file diff --git a/src/aws_encryption_sdk/mpl/__init__.py b/src/aws_encryption_sdk/internal/mpl/__init__.py similarity index 100% rename from src/aws_encryption_sdk/mpl/__init__.py rename to src/aws_encryption_sdk/internal/mpl/__init__.py diff --git a/src/aws_encryption_sdk/mpl/cmm_handler.py b/src/aws_encryption_sdk/internal/mpl/cmm_handler.py similarity index 98% rename from src/aws_encryption_sdk/mpl/cmm_handler.py rename to src/aws_encryption_sdk/internal/mpl/cmm_handler.py index 1f6c9ff41..c285afa04 100644 --- a/src/aws_encryption_sdk/mpl/cmm_handler.py +++ b/src/aws_encryption_sdk/internal/mpl/cmm_handler.py @@ -23,7 +23,7 @@ from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError from aws_encryption_sdk.identifiers import CommitmentPolicy -from aws_encryption_sdk.mpl.materials_handlers import DecryptionMaterialsHandler, EncryptionMaterialsHandler +from aws_encryption_sdk.internal.mpl.materials_handlers import DecryptionMaterialsHandler, EncryptionMaterialsHandler from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey diff --git a/src/aws_encryption_sdk/mpl/materials_handlers.py b/src/aws_encryption_sdk/internal/mpl/materials_handlers.py similarity index 98% rename from src/aws_encryption_sdk/mpl/materials_handlers.py rename to src/aws_encryption_sdk/internal/mpl/materials_handlers.py index df5b57d53..bf32c2718 100644 --- a/src/aws_encryption_sdk/mpl/materials_handlers.py +++ b/src/aws_encryption_sdk/internal/mpl/materials_handlers.py @@ -48,8 +48,8 @@ def __init__( elif isinstance(materials, MPL_EncryptionMaterials): self.mpl_materials = materials else: - raise ValueError("Invalid EncryptionMaterials passed to EncryptionMaterialsHandler. " \ - f"materials: {materials}") + raise ValueError("Invalid EncryptionMaterials passed to EncryptionMaterialsHandler. " + f"materials: {materials}") @property def algorithm(self) -> Algorithm: diff --git a/src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py b/src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py new file mode 100644 index 000000000..55319bc43 --- /dev/null +++ b/src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py @@ -0,0 +1,21 @@ +"""Detects whether the MPL is installed for use by internal ESDK code. +External customers should not need to interact with this. +""" + + +def has_mpl(): + """Returns True if the aws-cryptographic-material-providers library is installed, False otherwise.""" + try: + _import_mpl() + return True + except ImportError: + return False + + +def _import_mpl(): + """Private wrapper for import. + This only exists to help with unit test coverage. + This is not directly tested. + """ + # noqa pylint:disable=unused-import,import-outside-toplevel,import-error + import aws_cryptographic_materialproviders diff --git a/src/aws_encryption_sdk/mpl/mpl_import_handler.py b/src/aws_encryption_sdk/mpl/mpl_import_handler.py deleted file mode 100644 index 40669da1e..000000000 --- a/src/aws_encryption_sdk/mpl/mpl_import_handler.py +++ /dev/null @@ -1,14 +0,0 @@ -def has_mpl(): - """Returns True if the aws_cryptographic_materialproviders library is installed, False otherwise.""" - try: - _import_mpl() - return True - except ImportError: - return False - -def _import_mpl(): - """Private wrapper for import to help with unit test coverage. - - This is not directly tested. - """ - import aws_cryptographic_materialproviders \ No newline at end of file diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 044626c7f..c4c15559b 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -22,11 +22,9 @@ import attr import six - from cryptography.hazmat.primitives import serialization import aws_encryption_sdk.internal.utils -from aws_encryption_sdk.mpl.cmm_handler import CMMHandler from aws_encryption_sdk.exceptions import ( ActionNotAllowedError, AWSEncryptionSDKClientError, @@ -60,6 +58,8 @@ serialize_non_framed_close, serialize_non_framed_open, ) +from aws_encryption_sdk.internal.mpl import mpl_import_handler +from aws_encryption_sdk.internal.mpl.cmm_handler import CMMHandler from aws_encryption_sdk.internal.utils.commitment import ( validate_commitment_policy_on_decrypt, validate_commitment_policy_on_encrypt, @@ -71,8 +71,8 @@ from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import MessageHeader -from aws_encryption_sdk.mpl import mpl_import_handler if mpl_import_handler.has_mpl(): + # noqa pylint: disable=import-error from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput diff --git a/test/unit/mpl/test_cmm_handler.py b/test/unit/mpl/test_cmm_handler.py index 45b49ed91..343ac514b 100644 --- a/test/unit/mpl/test_cmm_handler.py +++ b/test/unit/mpl/test_cmm_handler.py @@ -12,35 +12,21 @@ # language governing permissions and limitations under the License. """Test suite to verify the mpl_import_handler module handles importing the MPL correctly.""" import pytest -from mock import MagicMock, patch - -from aws_encryption_sdk.mpl.cmm_handler import CMMHandler - -from aws_encryption_sdk.mpl.materials_handlers import DecryptionMaterialsHandler, EncryptionMaterialsHandler -from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest -from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_cryptographic_materialproviders.mpl.models import ( + EncryptionMaterials as MPL_EncryptionMaterials, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, +) from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager +from mock import MagicMock, patch +from aws_encryption_sdk.internal.mpl.cmm_handler import CMMHandler +from aws_encryption_sdk.internal.mpl.materials_handlers import EncryptionMaterialsHandler from aws_encryption_sdk.materials_managers import ( - DecryptionMaterials as Native_DecryptionMaterials, EncryptionMaterials as Native_EncryptionMaterials, + EncryptionMaterialsRequest, ) - -from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, - DecryptMaterialsOutput, - EncryptedDataKey as MPL_EncryptedDataKey, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, - ) - -from aws_cryptographic_materialproviders.mpl.models import ( - DecryptionMaterials as MPL_DecryptionMaterials, - EncryptedDataKey as MPL_EncryptedDataKey, - EncryptionMaterials as MPL_EncryptionMaterials, - ) +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager mock_native_cmm = MagicMock(__class__=CryptoMaterialsManager) mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) @@ -68,7 +54,9 @@ def test_GIVEN_unknown_CMM_WHEN_create_CMMHandler_THEN_raise_ValueError(): @patch.object(mock_native_cmm, "get_encryption_materials") -def test_GIVEN_native_CMM_WHEN_get_encryption_materials_THEN_return_native_encryption_materials(mock_get_encryption_materials): +def test_GIVEN_native_CMM_WHEN_get_encryption_materials_THEN_return_native_encryption_materials( + mock_get_encryption_materials +): # Mock: native_cmm.get_encryption_materials returns mock native encryption materials mock_get_encryption_materials.return_value = mock_native_encryption_materials @@ -84,17 +72,16 @@ def test_GIVEN_native_CMM_WHEN_get_encryption_materials_THEN_return_native_encry @patch.object(mock_mpl_cmm, "get_encryption_materials") -@patch("aws_encryption_sdk.mpl.cmm_handler.CMMHandler._native_to_mpl_get_encryption_materials") +@patch("aws_encryption_sdk.internal.mpl.cmm_handler.CMMHandler._native_to_mpl_get_encryption_materials") def test_GIVEN_mpl_CMM_WHEN_get_encryption_materials_THEN_return_mpl_encryption_materials( mock_native_to_mpl_get_encryption_materials, mock_get_encryption_materials, - ): # Mock: mpl_cmm.get_encryption_materials returns mock MPL encryption materials mock_get_encryption_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials mock_get_encryption_materials.return_value = mock_get_encryption_materials_output - + # Mock: CMMHandler._native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input @@ -108,4 +95,3 @@ def test_GIVEN_mpl_CMM_WHEN_get_encryption_materials_THEN_return_mpl_encryption_ assert test.mpl_materials == mock_mpl_encryption_materials # Verify we actually called `get_encryption_materials` mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) - diff --git a/test/unit/test_mpl_import_handler.py b/test/unit/test_mpl_import_handler.py index c17c358b4..b82c3092b 100644 --- a/test/unit/test_mpl_import_handler.py +++ b/test/unit/test_mpl_import_handler.py @@ -14,21 +14,23 @@ import pytest from mock import patch -from aws_encryption_sdk.mpl import mpl_import_handler +from aws_encryption_sdk.internal.mpl import mpl_import_handler pytestmark = [pytest.mark.unit, pytest.mark.local] -@patch("aws_encryption_sdk.mpl.mpl_import_handler._import_mpl") + +@patch("aws_encryption_sdk.internal.mpl.mpl_import_handler._import_mpl") def test_GIVEN_import_mpl_succeeds_WHEN_call_has_mpl_THEN_return_True(import_mock): # Mock a successful import of `aws_cryptographic_material_providers` import_mock.return_value = None # No exception means successful import assert mpl_import_handler.has_mpl() is True -@patch("aws_encryption_sdk.mpl.mpl_import_handler._import_mpl") + +@patch("aws_encryption_sdk.internal.mpl.mpl_import_handler._import_mpl") def test_GIVEN_import_mpl_fails_WHEN_call_has_mpl_THEN_return_False(import_mock): # Mock not having a `aws_cryptographic_material_providers` module, # even if it is installed in the Python environment import_mock.side_effect = ImportError() - assert not mpl_import_handler.has_mpl() \ No newline at end of file + assert not mpl_import_handler.has_mpl() diff --git a/test/unit/test_streaming_client_mpl_import.py b/test/unit/test_streaming_client_mpl_import.py index f71f337b7..594ef3478 100644 --- a/test/unit/test_streaming_client_mpl_import.py +++ b/test/unit/test_streaming_client_mpl_import.py @@ -11,18 +11,18 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Unit test suite to validate aws_encryption_sdk.streaming_client MPL import logic.""" -import io -import pytest -from mock import patch +import sys from importlib import reload -from mock import Mock +import pytest +from mock import Mock, patch import aws_encryption_sdk.streaming_client pytestmark = [pytest.mark.unit, pytest.mark.local] + @patch.object(aws_encryption_sdk.streaming_client.mpl_import_handler, "has_mpl") def test_GIVEN_has_mpl_returns_True_WHEN_import_streaming_client_THEN_imports_mpl_modules(has_mpl_mock): has_mpl_mock.return_value = True @@ -30,7 +30,6 @@ def test_GIVEN_has_mpl_returns_True_WHEN_import_streaming_client_THEN_imports_mp # Mock any imports used in the try/catch block # If more imports are added there, then this needs to be expanded # This unit test should pass even if the MPL is not installed - import sys sys.modules['aws_cryptographic_materialproviders.mpl.client'] = Mock() sys.modules['aws_cryptographic_materialproviders.mpl.config'] = Mock() sys.modules['aws_cryptographic_materialproviders.mpl.models'] = Mock() @@ -42,6 +41,7 @@ def test_GIVEN_has_mpl_returns_True_WHEN_import_streaming_client_THEN_imports_mp assert hasattr(aws_encryption_sdk.streaming_client, "_HAS_MPL") assert aws_encryption_sdk.streaming_client._HAS_MPL is True + @patch.object(aws_encryption_sdk.streaming_client.mpl_import_handler, "has_mpl") def test_GIVEN_has_mpl_returns_False_WHEN_import_streaming_client_THEN_does_not_import_mpl_modules(has_mpl_mock): has_mpl_mock.return_value = False @@ -50,4 +50,4 @@ def test_GIVEN_has_mpl_returns_False_WHEN_import_streaming_client_THEN_does_not_ reload(aws_encryption_sdk.streaming_client) assert hasattr(aws_encryption_sdk.streaming_client, "_HAS_MPL") - assert aws_encryption_sdk.streaming_client._HAS_MPL is False \ No newline at end of file + assert aws_encryption_sdk.streaming_client._HAS_MPL is False diff --git a/tox.ini b/tox.ini index 9e2d95477..20b4d9426 100644 --- a/tox.ini +++ b/tox.ini @@ -3,12 +3,18 @@ envlist = # <3.11: run all non-MPL tests py{37,38,39,310}-{local,integ,accept,examples}, # >=3.11: run all tests with MPL installed and without MPL installed - # The `-mpl` suffix tells tox to install the MPL + # The `-mpl` suffix tells tox to install the MPL. + # In the case where the suffix IS NOT appended, + # this runs tests for the target version WITHOUT the MPL installed. + # In the case where the suffix IS appended, + # this runs tests for the target version WITH the MPL installed. + # This does not run any MPL-specific tests; it only runs non-MPL-specific + # tests in a test environment that also has the MPL. py{311,312}-{local,integ,accept,examples}{,-mpl}, - # >=3.11: run ONLY the MPL-specific tests (requires a special IAM role) - # the `mpl` prefix runs only MPL-specific tests - # (non-MPL-specific tests are run from the line above) - # the extra `-mpl` suffix tells tox to install the MPL + # >=3.11: Run ONLY the MPL-specific tests. + # These must be separate from the above target. + # These require the `-mpl` suffix so tox installs the MPL. + # The `mpl` prefix runs only MPL-specific tests py{311,312}-mpl{local,examples}-mpl nocmk, bandit, doc8, readme, docs, @@ -78,6 +84,7 @@ deps = mpl: -rrequirements_mpl.txt commands = local: {[testenv:base-command]commands} test/ -m local --ignore test/unit/mpl/ + # MPL unit tests require the MPL to be installed mpllocal: {[testenv:base-command]commands} test/unit/mpl/ -m local integ: {[testenv:base-command]commands} test/ -m integ --ignore test/unit/mpl/ accept: {[testenv:base-command]commands} test/ -m accept --ignore test/unit/mpl/ @@ -194,13 +201,14 @@ commands = --max-module-lines=1500 \ src/aws_encryption_sdk/ \ setup.py + --ignore-paths=src/aws_encryption_sdk/internal/mpl/ [testenv:pylint-examples] basepython = {[testenv:pylint]basepython} deps = {[testenv:pylint]deps} commands = - pylint --rcfile=examples/src/pylintrc examples/src/ - pylint --rcfile=examples/test/pylintrc --disable R0801 examples/test/ + pylint --rcfile=examples/src/pylintrc examples/src/ --ignore-paths=examples/src/keyrings + pylint --rcfile=examples/test/pylintrc --disable R0801 examples/test/ --ignore-paths=examples/test/keyrings [testenv:pylint-tests] basepython = {[testenv:pylint]basepython} From 3ae1e069df8b2ea75762ce61ba4ea5f3798c24a0 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 15:22:21 -0800 Subject: [PATCH 062/172] refactor, fix --- .github/workflows/ci_static-analysis.yaml | 8 -------- examples/src/keyrings/hierarchical_keyring.py | 3 ++- src/aws_encryption_sdk/internal/mpl/cmm_handler.py | 2 -- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci_static-analysis.yaml b/.github/workflows/ci_static-analysis.yaml index 85d7f4a62..802bad2bc 100644 --- a/.github/workflows/ci_static-analysis.yaml +++ b/.github/workflows/ci_static-analysis.yaml @@ -15,7 +15,6 @@ jobs: matrix: python: - 3.8 - - 3.11 category: - bandit - doc8 @@ -29,13 +28,6 @@ jobs: - pylint-examples - black-check - isort-check - optional_mpl_dependency: - - "" - - -mpl - exclude: - # MPL is not supported on <3.11 - - python: 3.8 - optional_mpl_dependency: -mpl steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index 21108d9a0..56af60115 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -77,6 +77,7 @@ def encrypt_and_decrypt_with_keyring( class ExampleBranchKeyIdSupplier(IBranchKeyIdSupplier): """Example implementation of a branch key ID supplier.""" + branch_key_id_for_tenant_A: str branch_key_id_for_tenant_B: str @@ -226,4 +227,4 @@ def get_branch_key_id( source=ciphertext_B, keyring=hierarchical_keyring_B ) - assert plaintext_bytes_B == EXAMPLE_DATA \ No newline at end of file + assert plaintext_bytes_B == EXAMPLE_DATA diff --git a/src/aws_encryption_sdk/internal/mpl/cmm_handler.py b/src/aws_encryption_sdk/internal/mpl/cmm_handler.py index c285afa04..9789651e5 100644 --- a/src/aws_encryption_sdk/internal/mpl/cmm_handler.py +++ b/src/aws_encryption_sdk/internal/mpl/cmm_handler.py @@ -76,9 +76,7 @@ def get_encryption_materials( mpl_input: GetEncryptionMaterialsInput = CMMHandler._native_to_mpl_get_encryption_materials( request ) - print(f"mpl_input: {mpl_input}") mpl_output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) - print(f"mpl_output: {mpl_output}") return EncryptionMaterialsHandler(mpl_output.encryption_materials) except AwsCryptographicMaterialProvidersException as mpl_exception: # Wrap MPL error into the ESDK error type From 2b5fc7281f1ec263529f4b9c0c9635d62a7a4524 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 15:27:26 -0800 Subject: [PATCH 063/172] refactor, fix --- src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py b/src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py index 55319bc43..5dd0a7b3e 100644 --- a/src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py +++ b/src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py @@ -17,5 +17,5 @@ def _import_mpl(): This only exists to help with unit test coverage. This is not directly tested. """ - # noqa pylint:disable=unused-import,import-outside-toplevel,import-error - import aws_cryptographic_materialproviders + # pylint:disable=unused-import,import-outside-toplevel,import-error + import aws_cryptographic_materialproviders # noqa F401 From a940dc57a7c9a11296d5200446dde48f0def7d77 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 15:33:45 -0800 Subject: [PATCH 064/172] refactor, fix --- .github/workflows/ci_static-analysis.yaml | 2 +- src/aws_encryption_sdk/internal/crypto/authentication.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_static-analysis.yaml b/.github/workflows/ci_static-analysis.yaml index 802bad2bc..03fa62165 100644 --- a/.github/workflows/ci_static-analysis.yaml +++ b/.github/workflows/ci_static-analysis.yaml @@ -38,5 +38,5 @@ jobs: pip install --upgrade -r dev_requirements/ci-requirements.txt - name: run test env: - TOXENV: ${{ matrix.category }}${{ matrix.optional_mpl_dependency }} + TOXENV: ${{ matrix.category }} run: tox -- -vv diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index a6446981e..d7ff35278 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -78,7 +78,7 @@ def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DE """ if encoding == serialization.Encoding.DER: key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) - elif serialization.Encoding.PEM: + elif encoding == serialization.Encoding.PEM: key = serialization.load_pem_private_key(data=key_bytes, password=None, backend=default_backend()) else: raise ValueError(f"Unsupported encoding for Signer: {encoding}") From 708ab5e26f1227cc5f72b58abd5d92bcf27cc89a Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 15:55:36 -0800 Subject: [PATCH 065/172] it works locally but fails on gha --- test/unit/test_crypto_authentication_signer.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index 11271abfb..9584cf441 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -81,7 +81,13 @@ def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch 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) + # signer = Signer.from_key_bytes(algorithm=_algorithm, key_bytes=sentinel.key_bytes) + + signer = Signer.from_key_bytes( + algorithm=_algorithm, + key_bytes=sentinel.key_bytes, + encoding=patch_serialization.encoding.DER + ) patch_serialization.load_der_private_key.assert_called_once_with( data=sentinel.key_bytes, password=None, backend=patch_default_backend.return_value From ffd295c10253a7c0f1437309396eef28f076118f Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 15:57:57 -0800 Subject: [PATCH 066/172] it works locally but fails on gha --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 20b4d9426..1c133adaa 100644 --- a/tox.ini +++ b/tox.ini @@ -110,7 +110,7 @@ passenv = setenv = ######################################################### deps = -rdev_requirements/test-requirements.txt -commands = {[testenv:base-command]commands} test/ -m local +commands = {[testenv:base-command]commands} test/ -m local --ignore test/unit/mpl/ # Collect requirements for use in upstream tests [testenv:freeze-upstream-requirements-base] @@ -142,7 +142,7 @@ commands = {[testenv:freeze-upstream-requirements-base]commands} test/upstream-r [testenv:test-upstream-requirements-base] sitepackages = False recreate = True -commands = {[testenv:base-command]commands} test/ -m local +commands = {[testenv:base-command]commands} test/ -m local --ignore test/unit/mpl/ # Test frozen upstream requirements for Python 3.7 [testenv:test-upstream-requirements-py37] From 1ba175c5391b7cf673b5c8e56be661951a072730 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 16:01:21 -0800 Subject: [PATCH 067/172] it works locally but fails on gha --- test/unit/test_crypto_authentication_signer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index 9584cf441..2e5f5a4fd 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -86,7 +86,7 @@ def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch signer = Signer.from_key_bytes( algorithm=_algorithm, key_bytes=sentinel.key_bytes, - encoding=patch_serialization.encoding.DER + encoding=patch_serialization.Encoding.DER ) patch_serialization.load_der_private_key.assert_called_once_with( From fa175ba6ed28758222b39e08569b310bfa08e363 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 16:09:12 -0800 Subject: [PATCH 068/172] it works locally but fails on gha --- .github/workflows/ci_tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 603f54371..d46c19c48 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -24,8 +24,8 @@ jobs: fail-fast: true matrix: os: - - ubuntu-latest - - windows-latest + # - ubuntu-latest + # - windows-latest - macos-latest python: - 3.7 From 2f90a970683bb9cf17469c88d51a28b8d5bd8baf Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 16:15:22 -0800 Subject: [PATCH 069/172] it works locally but fails on gha --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index c4c15559b..4e9d5d07c 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -247,7 +247,7 @@ def __new__(cls, **kwargs): ): raise TypeError("Can't instantiate abstract class {}".format(cls.__name__)) - instance = super(_EncryptionStream, cls).__new__(cls) + instance = super().__new__(cls) config = kwargs.pop("config", None) if not isinstance(config, instance._config_class): # pylint: disable=protected-access From df9215f6e72695a69fc042e2d7d326c341c006ff Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 16:19:20 -0800 Subject: [PATCH 070/172] it works locally but fails on gha --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 4e9d5d07c..c4c15559b 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -247,7 +247,7 @@ def __new__(cls, **kwargs): ): raise TypeError("Can't instantiate abstract class {}".format(cls.__name__)) - instance = super().__new__(cls) + instance = super(_EncryptionStream, cls).__new__(cls) config = kwargs.pop("config", None) if not isinstance(config, instance._config_class): # pylint: disable=protected-access From b57e4a397cfa4ca28f0877df1f0dbc608fbf0cfe Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 16:22:47 -0800 Subject: [PATCH 071/172] it works locally but fails on gha --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index c4c15559b..4e9d5d07c 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -247,7 +247,7 @@ def __new__(cls, **kwargs): ): raise TypeError("Can't instantiate abstract class {}".format(cls.__name__)) - instance = super(_EncryptionStream, cls).__new__(cls) + instance = super().__new__(cls) config = kwargs.pop("config", None) if not isinstance(config, instance._config_class): # pylint: disable=protected-access From 9d7ec6d3a2baca380c2e7bdb466b554c36e4a5f2 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 16:25:37 -0800 Subject: [PATCH 072/172] it works locally but fails on gha --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 4e9d5d07c..c4c15559b 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -247,7 +247,7 @@ def __new__(cls, **kwargs): ): raise TypeError("Can't instantiate abstract class {}".format(cls.__name__)) - instance = super().__new__(cls) + instance = super(_EncryptionStream, cls).__new__(cls) config = kwargs.pop("config", None) if not isinstance(config, instance._config_class): # pylint: disable=protected-access From 2cbc8451756cedc0efde5a1c73141a8d8741748c Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 16:31:22 -0800 Subject: [PATCH 073/172] it works locally but fails on gha --- src/aws_encryption_sdk/streaming_client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index c4c15559b..3bda700e7 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -247,7 +247,10 @@ def __new__(cls, **kwargs): ): raise TypeError("Can't instantiate abstract class {}".format(cls.__name__)) - instance = super(_EncryptionStream, cls).__new__(cls) + if issubclass(StreamEncryptor, _EncryptionStream): + instance = super(_EncryptionStream, cls).__new__(cls) + else: + raise ValueError(f"issubclass {issubclass(StreamEncryptor, _EncryptionStream)}") config = kwargs.pop("config", None) if not isinstance(config, instance._config_class): # pylint: disable=protected-access From def946d841461aa19a5dc73aeaf1471de7f7e351 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 16:37:51 -0800 Subject: [PATCH 074/172] it works locally but fails on gha --- src/aws_encryption_sdk/streaming_client.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 3bda700e7..4e9d5d07c 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -247,10 +247,7 @@ def __new__(cls, **kwargs): ): raise TypeError("Can't instantiate abstract class {}".format(cls.__name__)) - if issubclass(StreamEncryptor, _EncryptionStream): - instance = super(_EncryptionStream, cls).__new__(cls) - else: - raise ValueError(f"issubclass {issubclass(StreamEncryptor, _EncryptionStream)}") + instance = super().__new__(cls) config = kwargs.pop("config", None) if not isinstance(config, instance._config_class): # pylint: disable=protected-access From dff6ac0134e29ed4b98d046701ee75b11cd26450 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 17:12:17 -0800 Subject: [PATCH 075/172] it works locally but fails on gha --- src/aws_encryption_sdk/streaming_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 4e9d5d07c..4b849b818 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -71,14 +71,14 @@ from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import MessageHeader -if mpl_import_handler.has_mpl(): +try: # noqa pylint: disable=import-error from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput from aws_cryptographic_materialproviders.mpl.references import IKeyring _HAS_MPL = True -else: +except ImportError: _HAS_MPL = False _LOGGER = logging.getLogger(__name__) From 78f0b0faed85977346c2bb6652899a8316c0e10e Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 17:22:16 -0800 Subject: [PATCH 076/172] it works locally but fails on gha --- src/aws_encryption_sdk/streaming_client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 4b849b818..1186faf2b 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -158,6 +158,9 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes ) # DEPRECATED: Value is no longer configurable here. Parameter left here to avoid breaking consumers. def _has_mpl_attrs_post_init(self): + if not hasattr(self, "keyring"): + self._no_mpl_attrs_post_init() + return if not _exactly_one_arg_is_not_none(self.materials_manager, self.key_provider, self.keyring): raise TypeError("Exactly one of keyring, materials_manager, or key_provider must be provided") if self.materials_manager is None: From 20a469e3b2e7fb8357096af9462856fa6b43604c Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 20 Feb 2024 17:24:24 -0800 Subject: [PATCH 077/172] it works locally but fails on gha --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 1186faf2b..f9b550e6d 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -250,7 +250,7 @@ def __new__(cls, **kwargs): ): raise TypeError("Can't instantiate abstract class {}".format(cls.__name__)) - instance = super().__new__(cls) + instance = super(_EncryptionStream, cls).__new__(cls) config = kwargs.pop("config", None) if not isinstance(config, instance._config_class): # pylint: disable=protected-access From 66859a7b5316013200b42a6b00c2ebb1a51fcc18 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:10:57 -0800 Subject: [PATCH 078/172] fix tests --- examples/src/keyrings/hierarchical_keyring.py | 33 +++++++++-------- .../internal/mpl/mpl_import_handler.py | 21 ----------- src/aws_encryption_sdk/streaming_client.py | 4 +-- test/unit/mpl/test_cmm_handler.py | 2 +- test/unit/test_mpl_import_handler.py | 36 ------------------- test/unit/test_streaming_client_mpl_import.py | 32 ++++++++--------- 6 files changed, 33 insertions(+), 95 deletions(-) delete mode 100644 src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py delete mode 100644 test/unit/test_mpl_import_handler.py diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index 56af60115..3e56d1e56 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -9,23 +9,22 @@ import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError -from aws_encryption_sdk.internal.mpl import mpl_import_handler - -if mpl_import_handler.has_mpl(): - # noqa pylint: disable=import-error - from aws_cryptographic_materialproviders.keystore.client import KeyStore - from aws_cryptographic_materialproviders.keystore.config import KeyStoreConfig - from aws_cryptographic_materialproviders.keystore.models import CreateKeyInput, KMSConfigurationKmsKeyArn - from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders - from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig - from aws_cryptographic_materialproviders.mpl.models import ( - CacheTypeDefault, - CreateAwsKmsHierarchicalKeyringInput, - DefaultCache, - GetBranchKeyIdInput, - GetBranchKeyIdOutput, - ) - from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier, IKeyring + +# ignore missing MPL for pylint, but the MPL is required for this example +# noqa pylint: disable=import-error +from aws_cryptographic_materialproviders.keystore.client import KeyStore +from aws_cryptographic_materialproviders.keystore.config import KeyStoreConfig +from aws_cryptographic_materialproviders.keystore.models import CreateKeyInput, KMSConfigurationKmsKeyArn +from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders +from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig +from aws_cryptographic_materialproviders.mpl.models import ( + CacheTypeDefault, + CreateAwsKmsHierarchicalKeyringInput, + DefaultCache, + GetBranchKeyIdInput, + GetBranchKeyIdOutput, +) +from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier, IKeyring module_root_dir = '/'.join(__file__.split("/")[:-1]) diff --git a/src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py b/src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py deleted file mode 100644 index 5dd0a7b3e..000000000 --- a/src/aws_encryption_sdk/internal/mpl/mpl_import_handler.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Detects whether the MPL is installed for use by internal ESDK code. -External customers should not need to interact with this. -""" - - -def has_mpl(): - """Returns True if the aws-cryptographic-material-providers library is installed, False otherwise.""" - try: - _import_mpl() - return True - except ImportError: - return False - - -def _import_mpl(): - """Private wrapper for import. - This only exists to help with unit test coverage. - This is not directly tested. - """ - # pylint:disable=unused-import,import-outside-toplevel,import-error - import aws_cryptographic_materialproviders # noqa F401 diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index f9b550e6d..8e004e84e 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -58,7 +58,6 @@ serialize_non_framed_close, serialize_non_framed_open, ) -from aws_encryption_sdk.internal.mpl import mpl_import_handler from aws_encryption_sdk.internal.mpl.cmm_handler import CMMHandler from aws_encryption_sdk.internal.utils.commitment import ( validate_commitment_policy_on_decrypt, @@ -72,13 +71,14 @@ from aws_encryption_sdk.structures import MessageHeader try: + # pylint should pass even if the MPL isn't installed # noqa pylint: disable=import-error from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput from aws_cryptographic_materialproviders.mpl.references import IKeyring _HAS_MPL = True -except ImportError: +except ImportError as e: _HAS_MPL = False _LOGGER = logging.getLogger(__name__) diff --git a/test/unit/mpl/test_cmm_handler.py b/test/unit/mpl/test_cmm_handler.py index 343ac514b..d16374899 100644 --- a/test/unit/mpl/test_cmm_handler.py +++ b/test/unit/mpl/test_cmm_handler.py @@ -10,7 +10,7 @@ # 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. -"""Test suite to verify the mpl_import_handler module handles importing the MPL correctly.""" +"""Test suite to verify the cmm_handler module delegates correctly.""" import pytest from aws_cryptographic_materialproviders.mpl.models import ( EncryptionMaterials as MPL_EncryptionMaterials, diff --git a/test/unit/test_mpl_import_handler.py b/test/unit/test_mpl_import_handler.py deleted file mode 100644 index b82c3092b..000000000 --- a/test/unit/test_mpl_import_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# 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. -"""Test suite to verify the mpl_import_handler module handles importing the MPL correctly.""" -import pytest -from mock import patch - -from aws_encryption_sdk.internal.mpl import mpl_import_handler - -pytestmark = [pytest.mark.unit, pytest.mark.local] - - -@patch("aws_encryption_sdk.internal.mpl.mpl_import_handler._import_mpl") -def test_GIVEN_import_mpl_succeeds_WHEN_call_has_mpl_THEN_return_True(import_mock): - # Mock a successful import of `aws_cryptographic_material_providers` - import_mock.return_value = None # No exception means successful import - - assert mpl_import_handler.has_mpl() is True - - -@patch("aws_encryption_sdk.internal.mpl.mpl_import_handler._import_mpl") -def test_GIVEN_import_mpl_fails_WHEN_call_has_mpl_THEN_return_False(import_mock): - # Mock not having a `aws_cryptographic_material_providers` module, - # even if it is installed in the Python environment - import_mock.side_effect = ImportError() - - assert not mpl_import_handler.has_mpl() diff --git a/test/unit/test_streaming_client_mpl_import.py b/test/unit/test_streaming_client_mpl_import.py index 594ef3478..ebafd199f 100644 --- a/test/unit/test_streaming_client_mpl_import.py +++ b/test/unit/test_streaming_client_mpl_import.py @@ -23,31 +23,27 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] -@patch.object(aws_encryption_sdk.streaming_client.mpl_import_handler, "has_mpl") -def test_GIVEN_has_mpl_returns_True_WHEN_import_streaming_client_THEN_imports_mpl_modules(has_mpl_mock): - has_mpl_mock.return_value = True +# Check if MPL is installed, and skip tests based on whether it is +try: + import aws_cryptographic_materialproviders + HAS_MPL = True +except ImportError as e: + HAS_MPL = False - # Mock any imports used in the try/catch block - # If more imports are added there, then this needs to be expanded - # This unit test should pass even if the MPL is not installed - sys.modules['aws_cryptographic_materialproviders.mpl.client'] = Mock() - sys.modules['aws_cryptographic_materialproviders.mpl.config'] = Mock() - sys.modules['aws_cryptographic_materialproviders.mpl.models'] = Mock() - sys.modules['aws_cryptographic_materialproviders.mpl.references'] = Mock() - # Reload module given the mock - reload(aws_encryption_sdk.streaming_client) +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_True_THEN_streaming_client_has_mpl_is_TRUE(): + """If the MPL IS installed in the runtime environment, + assert the streaming client has _HAS_MPL set to True""" assert hasattr(aws_encryption_sdk.streaming_client, "_HAS_MPL") assert aws_encryption_sdk.streaming_client._HAS_MPL is True -@patch.object(aws_encryption_sdk.streaming_client.mpl_import_handler, "has_mpl") -def test_GIVEN_has_mpl_returns_False_WHEN_import_streaming_client_THEN_does_not_import_mpl_modules(has_mpl_mock): - has_mpl_mock.return_value = False - - # Reload module given the mock - reload(aws_encryption_sdk.streaming_client) +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_GIVEN_test_has_mpl_is_False_THEN_streaming_client_has_mpl_is_TRUE(): + """If the MPL IS NOT installed in the runtime environment, + assert the streaming client has _HAS_MPL set to False""" assert hasattr(aws_encryption_sdk.streaming_client, "_HAS_MPL") assert aws_encryption_sdk.streaming_client._HAS_MPL is False From bf8f67c1fd4a8523c4a0a76eb5f5e0245e5b25b5 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:18:23 -0800 Subject: [PATCH 079/172] cleanup --- examples/src/keyrings/hierarchical_keyring.py | 11 +++++------ src/aws_encryption_sdk/streaming_client.py | 2 +- test/unit/test_streaming_client_mpl_import.py | 12 ++++-------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index 3e56d1e56..50f620456 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -4,12 +4,6 @@ import sys import boto3 -from typing import Dict - -import aws_encryption_sdk -from aws_encryption_sdk import CommitmentPolicy -from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError - # ignore missing MPL for pylint, but the MPL is required for this example # noqa pylint: disable=import-error from aws_cryptographic_materialproviders.keystore.client import KeyStore @@ -25,6 +19,11 @@ GetBranchKeyIdOutput, ) from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier, IKeyring +from typing import Dict + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError module_root_dir = '/'.join(__file__.split("/")[:-1]) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 8e004e84e..ad998088c 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -78,7 +78,7 @@ from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput from aws_cryptographic_materialproviders.mpl.references import IKeyring _HAS_MPL = True -except ImportError as e: +except ImportError: _HAS_MPL = False _LOGGER = logging.getLogger(__name__) diff --git a/test/unit/test_streaming_client_mpl_import.py b/test/unit/test_streaming_client_mpl_import.py index ebafd199f..3eda0ad63 100644 --- a/test/unit/test_streaming_client_mpl_import.py +++ b/test/unit/test_streaming_client_mpl_import.py @@ -12,11 +12,7 @@ # language governing permissions and limitations under the License. """Unit test suite to validate aws_encryption_sdk.streaming_client MPL import logic.""" -import sys -from importlib import reload - import pytest -from mock import Mock, patch import aws_encryption_sdk.streaming_client @@ -25,14 +21,14 @@ # Check if MPL is installed, and skip tests based on whether it is try: - import aws_cryptographic_materialproviders + import aws_cryptographic_materialproviders # noqa pylint: disable=unused-import HAS_MPL = True -except ImportError as e: +except ImportError: HAS_MPL = False @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_True_THEN_streaming_client_has_mpl_is_TRUE(): +def test_GIVEN_test_has_mpl_is_True_THEN_streaming_client_has_mpl_is_True(): """If the MPL IS installed in the runtime environment, assert the streaming client has _HAS_MPL set to True""" @@ -41,7 +37,7 @@ def test_GIVEN_test_has_mpl_is_True_THEN_streaming_client_has_mpl_is_TRUE(): @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") -def test_GIVEN_test_has_mpl_is_False_THEN_streaming_client_has_mpl_is_TRUE(): +def test_GIVEN_test_has_mpl_is_False_THEN_streaming_client_has_mpl_is_False(): """If the MPL IS NOT installed in the runtime environment, assert the streaming client has _HAS_MPL set to False""" From b24be113a9a179d9cc22602b06a55222fc557ef3 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:23:07 -0800 Subject: [PATCH 080/172] re-enable test --- .github/workflows/ci_tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index d46c19c48..603f54371 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -24,8 +24,8 @@ jobs: fail-fast: true matrix: os: - # - ubuntu-latest - # - windows-latest + - ubuntu-latest + - windows-latest - macos-latest python: - 3.7 From acba1b0143ee6b40312b5a02ba6d95ea736b9d0a Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:25:52 -0800 Subject: [PATCH 081/172] re-enable test --- test/unit/test_streaming_client_mpl_import.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/unit/test_streaming_client_mpl_import.py b/test/unit/test_streaming_client_mpl_import.py index 3eda0ad63..a4ca87e2a 100644 --- a/test/unit/test_streaming_client_mpl_import.py +++ b/test/unit/test_streaming_client_mpl_import.py @@ -19,7 +19,9 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] -# Check if MPL is installed, and skip tests based on whether it is +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. try: import aws_cryptographic_materialproviders # noqa pylint: disable=unused-import HAS_MPL = True From 42b7b745dec470b06bf1fecaae6d0578b17df2e1 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:29:57 -0800 Subject: [PATCH 082/172] longpaths --- .github/workflows/ci_tests.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 603f54371..a8bad7bfb 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -70,6 +70,7 @@ jobs: python-version: ${{ matrix.python }} architecture: ${{ matrix.architecture }} - run: | + git config --system core.longpaths true python -m pip install --upgrade pip pip install --upgrade -r dev_requirements/ci-requirements.txt - name: run test From f226e7e8bd5b3df06fda12f63be601939a0a8711 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:34:14 -0800 Subject: [PATCH 083/172] longpaths --- .github/workflows/ci_tests.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index a8bad7bfb..cab32dcc7 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -64,13 +64,16 @@ jobs: - python: 3.10 optional_mpl_dependency: -mpl steps: + - name: Support longpaths + run: | + git config --global core.longpaths true + - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: ${{ matrix.architecture }} - run: | - git config --system core.longpaths true python -m pip install --upgrade pip pip install --upgrade -r dev_requirements/ci-requirements.txt - name: run test From aa2f80a2aaff191082ed0844ded4ebe812eafb2c Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:39:24 -0800 Subject: [PATCH 084/172] debug windows fail --- .github/workflows/ci_tests.yaml | 4 ++-- src/aws_encryption_sdk/streaming_client.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index cab32dcc7..132c65bd0 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -24,9 +24,9 @@ jobs: fail-fast: true matrix: os: - - ubuntu-latest + # - ubuntu-latest - windows-latest - - macos-latest + # - macos-latest python: - 3.7 - 3.8 diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index ad998088c..bc37d7de5 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -78,7 +78,8 @@ from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput from aws_cryptographic_materialproviders.mpl.references import IKeyring _HAS_MPL = True -except ImportError: +except ImportError as e: + print(e) _HAS_MPL = False _LOGGER = logging.getLogger(__name__) From bc002b682dd7113734cf8494e17e48a1462e18c6 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:42:48 -0800 Subject: [PATCH 085/172] debug windows fail --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 1c133adaa..fa3e8530f 100644 --- a/tox.ini +++ b/tox.ini @@ -56,7 +56,7 @@ envlist = # coverage :: Runs code coverage, failing the build if coverage is below the configured threshold [testenv:base-command] -commands = pytest --basetemp={envtmpdir} -l {posargs} +commands = pytest --basetemp={envtmpdir} -l {posargs} -s -v [testenv] ; passenv = AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,AWS_PROFILE,PIP_CONFIG_FILE From 8dd0303615d72712dc7c190f6e0724656c26bfb2 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:46:34 -0800 Subject: [PATCH 086/172] debug windows fail --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index bc37d7de5..4a00b99f4 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -79,7 +79,7 @@ from aws_cryptographic_materialproviders.mpl.references import IKeyring _HAS_MPL = True except ImportError as e: - print(e) + print("IMPORT ERROR" + str(e)) _HAS_MPL = False _LOGGER = logging.getLogger(__name__) From 1e9db3b3b0d32da9c55e4e918c9320318026a611 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:50:22 -0800 Subject: [PATCH 087/172] debug windows fail --- .github/workflows/ci_tests.yaml | 9 ++++++--- src/aws_encryption_sdk/streaming_client.py | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 132c65bd0..d64d3ce79 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -24,9 +24,12 @@ jobs: fail-fast: true matrix: os: - # - ubuntu-latest - - windows-latest - # - macos-latest + - ubuntu-latest + # Windows fails due to "No module named 'Wrappers'" + # This SHOULD be fixed once Dafny generates fully-qualified import statements + # Disable for now + # - windows-latest + - macos-latest python: - 3.7 - 3.8 diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 4a00b99f4..8e004e84e 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -79,7 +79,6 @@ from aws_cryptographic_materialproviders.mpl.references import IKeyring _HAS_MPL = True except ImportError as e: - print("IMPORT ERROR" + str(e)) _HAS_MPL = False _LOGGER = logging.getLogger(__name__) From 74d4e667d1828dd6437f1aeb42436c0b44c2d7c0 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 10:53:26 -0800 Subject: [PATCH 088/172] disable windows until pythonpath --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 8e004e84e..ad998088c 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -78,7 +78,7 @@ from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput from aws_cryptographic_materialproviders.mpl.references import IKeyring _HAS_MPL = True -except ImportError as e: +except ImportError: _HAS_MPL = False _LOGGER = logging.getLogger(__name__) From 1bb23e862a73d96a376e207aeb82b3b2d3e19ec4 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 11:07:51 -0800 Subject: [PATCH 089/172] expand testing --- .github/workflows/ci_static-analysis.yaml | 4 +--- .github/workflows/ci_tests.yaml | 10 ++++++++++ codebuild/py311/examples_mpl.yml | 7 ++++--- codebuild/py312/awses_local_mpl.yml | 2 ++ codebuild/py312/examples_mpl.yml | 10 +++++++--- codebuild/py312/integ_mpl.yml | 2 ++ tox.ini | 11 ++++++++--- 7 files changed, 34 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci_static-analysis.yaml b/.github/workflows/ci_static-analysis.yaml index 03fa62165..0093ae9a9 100644 --- a/.github/workflows/ci_static-analysis.yaml +++ b/.github/workflows/ci_static-analysis.yaml @@ -13,8 +13,6 @@ jobs: strategy: fail-fast: false matrix: - python: - - 3.8 category: - bandit - doc8 @@ -32,7 +30,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: ${{ matrix.python }} + python-version: 3.8 - run: | python -m pip install --upgrade pip pip install --upgrade -r dev_requirements/ci-requirements.txt diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index d64d3ce79..58f9b1b11 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -44,10 +44,14 @@ jobs: category: - local - accept + - mpllocal + - mplaccept # These require credentials. # Enable them once we sort how to provide them. # - integ # - examples + # Append '-mpl' to some test environments. + # This suffix signals to tox to install the MPL in the test environment. optional_mpl_dependency: - "" - -mpl @@ -66,7 +70,13 @@ jobs: optional_mpl_dependency: -mpl - python: 3.10 optional_mpl_dependency: -mpl + # mpllocal and mplaccept require the MPL to be installed + - python: mpllocal + optional_mpl_dependency: + - python: mplaccept + optional_mpl_dependency: steps: + # Support long Dafny filenames (used in MPL and DBESDK repos) - name: Support longpaths run: | git config --global core.longpaths true diff --git a/codebuild/py311/examples_mpl.yml b/codebuild/py311/examples_mpl.yml index f8f2a6a01..19a5dec05 100644 --- a/codebuild/py311/examples_mpl.yml +++ b/codebuild/py311/examples_mpl.yml @@ -2,7 +2,7 @@ version: 0.2 env: variables: - # No TOXENV; examples using the MPL switch envs + # No TOXENV. This runs multiple environments. REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f @@ -20,14 +20,15 @@ phases: build: commands: - pip install "tox < 4.0" + # Run non-MPL-specific tests with the MPL installed - tox -e py311-examples-mpl - # Assume special role + # Assume special role to access keystore - TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-Py311ExamplesMpl") - export TMP_ROLE - export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId') - export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey') - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') - aws sts get-caller-identity - # Run special role-specific examples + # Run MPL-specific tests with special role - tox -e py311-mplexamples-mpl diff --git a/codebuild/py312/awses_local_mpl.yml b/codebuild/py312/awses_local_mpl.yml index db25f4f57..1d0f80319 100644 --- a/codebuild/py312/awses_local_mpl.yml +++ b/codebuild/py312/awses_local_mpl.yml @@ -1,3 +1,5 @@ +# Runs the same tests as awses_local in an environment with the MPL installed. +# This asserts existing tests continue to pass with the MPL installed. version: 0.2 env: diff --git a/codebuild/py312/examples_mpl.yml b/codebuild/py312/examples_mpl.yml index ba0660024..366222441 100644 --- a/codebuild/py312/examples_mpl.yml +++ b/codebuild/py312/examples_mpl.yml @@ -1,8 +1,11 @@ +# Runs the same tests as examples in an environment with the MPL installed +# to assert existing tests continue to pass with the MPL installed. +# Then, run MPL-specific tests. version: 0.2 env: variables: - # No TOXENV; examples using the MPL switch envs + # No TOXENV. This runs multiple environments. REGION: "us-west-2" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f @@ -25,13 +28,14 @@ phases: - pip install --upgrade pip - pip install setuptools - pip install "tox < 4.0" + # Run non-MPL-specific tests with the MPL installed - tox -e py312-examples-mpl - # Assume special role + # Assume special role to access keystore - TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-Py311ExamplesMpl") - export TMP_ROLE - export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId') - export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey') - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') - aws sts get-caller-identity - # Run special role-specific examples + # Run MPL-specific tests with special role - tox -e py312-mplexamples-mpl diff --git a/codebuild/py312/integ_mpl.yml b/codebuild/py312/integ_mpl.yml index 553f41e8a..e292acc57 100644 --- a/codebuild/py312/integ_mpl.yml +++ b/codebuild/py312/integ_mpl.yml @@ -1,3 +1,5 @@ +# Runs the same tests as integ in an environment with the MPL installed. +# This asserts existing tests continue to pass with the MPL installed. version: 0.2 env: diff --git a/tox.ini b/tox.ini index fa3e8530f..e5a585ca5 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,7 @@ envlist = # These must be separate from the above target. # These require the `-mpl` suffix so tox installs the MPL. # The `mpl` prefix runs only MPL-specific tests - py{311,312}-mpl{local,examples}-mpl + py{311,312}-mpl{local,integ,accept,examples}-mpl nocmk, bandit, doc8, readme, docs, {flake8,pylint}{,-tests,-examples}, @@ -56,7 +56,7 @@ envlist = # coverage :: Runs code coverage, failing the build if coverage is below the configured threshold [testenv:base-command] -commands = pytest --basetemp={envtmpdir} -l {posargs} -s -v +commands = pytest --basetemp={envtmpdir} -l {posargs} [testenv] ; passenv = AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,AWS_PROFILE,PIP_CONFIG_FILE @@ -87,11 +87,16 @@ commands = # MPL unit tests require the MPL to be installed mpllocal: {[testenv:base-command]commands} test/unit/mpl/ -m local integ: {[testenv:base-command]commands} test/ -m integ --ignore test/unit/mpl/ + # MPL integ tests require the MPL to be installed + mplinteg: {[testenv:base-command]commands} test/unit/mpl -m integ accept: {[testenv:base-command]commands} test/ -m accept --ignore test/unit/mpl/ + # MPL accept tests require the MPL to be installed + mplaccept: {[testenv:base-command]commands} test/unit/mpl -m accept examples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/keyrings/ # MPL keyring examples require a special IAM role; run these separately under a separate set of permissions mplexamples: {[testenv:base-command]commands} examples/test/keyrings -m examples - all: {[testenv:base-command]commands} test/ examples/test/ --ignore test/unit/mpl/ + all: {[testenv:base-command]commands} test/ examples/test/ --ignore test/unit/mpl/ --ignore examples/test/keyrings/ + mplall: {[testenv:base-command]commands} test/unit/mpl/ examples/test/keyrings/ manual: {[testenv:base-command]commands} # Run code coverage on the unit tests From 1ee69cefbb3f0d3486c2d0e873a1ac6c0a4bf8ff Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 11:11:05 -0800 Subject: [PATCH 090/172] expand testing --- .github/workflows/ci_tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 58f9b1b11..daa108060 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -72,9 +72,9 @@ jobs: optional_mpl_dependency: -mpl # mpllocal and mplaccept require the MPL to be installed - python: mpllocal - optional_mpl_dependency: + optional_mpl_dependency: "" - python: mplaccept - optional_mpl_dependency: + optional_mpl_dependency: "" steps: # Support long Dafny filenames (used in MPL and DBESDK repos) - name: Support longpaths From b33f2f706fb1dbb9f98d64b06a0c7ad6e97d08b2 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 11:12:01 -0800 Subject: [PATCH 091/172] expand testing --- .github/workflows/ci_tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index daa108060..495192a81 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -71,9 +71,9 @@ jobs: - python: 3.10 optional_mpl_dependency: -mpl # mpllocal and mplaccept require the MPL to be installed - - python: mpllocal + - category: mpllocal optional_mpl_dependency: "" - - python: mplaccept + - category: mplaccept optional_mpl_dependency: "" steps: # Support long Dafny filenames (used in MPL and DBESDK repos) From c582888a3f064ab1ab258f9c8bcb8fac80df6a36 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 11:16:22 -0800 Subject: [PATCH 092/172] expand testing --- tox.ini | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tox.ini b/tox.ini index e5a585ca5..cee1e35a4 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,7 @@ envlist = # These must be separate from the above target. # These require the `-mpl` suffix so tox installs the MPL. # The `mpl` prefix runs only MPL-specific tests - py{311,312}-mpl{local,integ,accept,examples}-mpl + py{311,312}-mpl{local,examples}-mpl nocmk, bandit, doc8, readme, docs, {flake8,pylint}{,-tests,-examples}, @@ -87,11 +87,9 @@ commands = # MPL unit tests require the MPL to be installed mpllocal: {[testenv:base-command]commands} test/unit/mpl/ -m local integ: {[testenv:base-command]commands} test/ -m integ --ignore test/unit/mpl/ - # MPL integ tests require the MPL to be installed - mplinteg: {[testenv:base-command]commands} test/unit/mpl -m integ + # No MPL-specific integ tests accept: {[testenv:base-command]commands} test/ -m accept --ignore test/unit/mpl/ - # MPL accept tests require the MPL to be installed - mplaccept: {[testenv:base-command]commands} test/unit/mpl -m accept + # No MPL-specific accept tests examples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/keyrings/ # MPL keyring examples require a special IAM role; run these separately under a separate set of permissions mplexamples: {[testenv:base-command]commands} examples/test/keyrings -m examples From 5ae44f5a077a240c1a9cd49bd49b08d165a4ad6c Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 11:19:22 -0800 Subject: [PATCH 093/172] expand testing --- .github/workflows/ci_tests.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 495192a81..803d4741e 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -45,7 +45,6 @@ jobs: - local - accept - mpllocal - - mplaccept # These require credentials. # Enable them once we sort how to provide them. # - integ @@ -70,11 +69,9 @@ jobs: optional_mpl_dependency: -mpl - python: 3.10 optional_mpl_dependency: -mpl - # mpllocal and mplaccept require the MPL to be installed + # mpllocal requires the MPL to be installed - category: mpllocal optional_mpl_dependency: "" - - category: mplaccept - optional_mpl_dependency: "" steps: # Support long Dafny filenames (used in MPL and DBESDK repos) - name: Support longpaths From cb7e3d1c8c8a58548ef03cdbc22f663d676c179f Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 11:45:49 -0800 Subject: [PATCH 094/172] cleanup --- codebuild/coverage/coverage_mpl.yml | 2 +- examples/src/keyrings/hierarchical_keyring.py | 60 ++++++------------- examples/src/keyrings/module_.py | 2 +- examples/src/module_.py | 2 +- .../unit/test_crypto_authentication_signer.py | 6 +- test_vector_handlers/tox.ini | 4 +- tox.ini | 3 +- 7 files changed, 27 insertions(+), 52 deletions(-) diff --git a/codebuild/coverage/coverage_mpl.yml b/codebuild/coverage/coverage_mpl.yml index 5dcc65382..922705569 100644 --- a/codebuild/coverage/coverage_mpl.yml +++ b/codebuild/coverage/coverage_mpl.yml @@ -7,7 +7,7 @@ env: phases: install: runtime-versions: - python: latest + python: 3.11 build: commands: - pip install "tox < 4.0" diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index 50f620456..8f8707013 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -15,8 +15,6 @@ CacheTypeDefault, CreateAwsKmsHierarchicalKeyringInput, DefaultCache, - GetBranchKeyIdInput, - GetBranchKeyIdOutput, ) from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier, IKeyring from typing import Dict @@ -25,6 +23,8 @@ from aws_encryption_sdk import CommitmentPolicy from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from .example_branch_key_id_supplier import ExampleBranchKeyIdSupplier + module_root_dir = '/'.join(__file__.split("/")[:-1]) sys.path.append(module_root_dir) @@ -73,39 +73,6 @@ def encrypt_and_decrypt_with_keyring( branch_key_id_A: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier branch_key_id_B: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier - class ExampleBranchKeyIdSupplier(IBranchKeyIdSupplier): - """Example implementation of a branch key ID supplier.""" - - branch_key_id_for_tenant_A: str - branch_key_id_for_tenant_B: str - - def __init__(self, tenant_1_id, tenant_2_id): - self.branch_key_id_for_tenant_A = tenant_1_id - self.branch_key_id_for_tenant_B = tenant_2_id - - def get_branch_key_id( - self, - # Change this to `native_input` - input: GetBranchKeyIdInput # noqa pylint: disable=redefined-builtin - ) -> GetBranchKeyIdOutput: - """Returns branch key ID from the tenant ID in input's encryption context.""" - encryption_context: Dict[str, str] = input.encryption_context - - if b"tenant" not in encryption_context: - raise ValueError("EncryptionContext invalid, does not contain expected tenant key value pair.") - - tenant_key_id: str = encryption_context.get(b"tenant") - branch_key_id: str - - if tenant_key_id == b"TenantA": - branch_key_id = self.branch_key_id_for_tenant_A - elif tenant_key_id == b"TenantB": - branch_key_id = self.branch_key_id_for_tenant_B - else: - raise ValueError(f"Item does not contain valid tenant ID: {tenant_key_id=}") - - return GetBranchKeyIdOutput(branch_key_id=branch_key_id) - # 5. Create a branch key supplier that maps the branch key id to a more readable format branch_key_id_supplier: IBranchKeyIdSupplier = ExampleBranchKeyIdSupplier( tenant_1_id=branch_key_id_A, @@ -132,8 +99,10 @@ def get_branch_key_id( input=keyring_input ) - # The Branch Key Id supplier uses the encryption context to determine which branch key id will - # be used to encrypt data. + # 7. Create encryption context for both tenants. + # The Branch Key Id supplier uses the encryption context to determine which branch key id will + # be used to encrypt data. + # Create encryption context for TenantA encryption_context_A: Dict[str, str] = { "tenant": "TenantA", @@ -154,7 +123,7 @@ def get_branch_key_id( "the data you are handling": "is what you think it is", } - # Encrypt the data for encryptionContextA & encryptionContextB + # 8. Encrypt the data for encryptionContextA & encryptionContextB ciphertext_A, _ = client.encrypt( source=EXAMPLE_DATA, keyring=hierarchical_keyring, @@ -166,8 +135,8 @@ def get_branch_key_id( encryption_context=encryption_context_B ) - # To attest that TenantKeyB cannot decrypt a message written by TenantKeyA - # let's construct more restrictive hierarchical keyrings. + # 9. To attest that TenantKeyB cannot decrypt a message written by TenantKeyA, + # let's construct more restrictive hierarchical keyrings. keyring_input_A: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( key_store=keystore, branch_key_id=branch_key_id_A, @@ -198,6 +167,11 @@ def get_branch_key_id( input=keyring_input_B ) + # 10. Demonstrate that data encrypted by one tenant's key + # cannot be decrypted with by a keyring specific to another tenant. + + # Keyring with tenant B's branch key cannot decrypt data encrypted with tenant A's branch key + # This will fail and raise a AWSEncryptionSDKClientError, which we swallow ONLY for demonstration purposes. try: client.decrypt( source=ciphertext_A, @@ -206,7 +180,8 @@ def get_branch_key_id( except AWSEncryptionSDKClientError: pass - # This should fail + # Keyring with tenant A's branch key cannot decrypt data encrypted with tenant B's branch key. + # This will fail and raise a AWSEncryptionSDKClientError, which we swallow ONLY for demonstration purposes. try: client.decrypt( source=ciphertext_B, @@ -215,7 +190,8 @@ def get_branch_key_id( except AWSEncryptionSDKClientError: pass - # These should succeed + # 10. Demonstrate that data encrypted by one tenant's branch key can be decrypted by that tenant, + # and that the decrypted data matches the input data. plaintext_bytes_A, _ = client.decrypt( source=ciphertext_A, keyring=hierarchical_keyring_A diff --git a/examples/src/keyrings/module_.py b/examples/src/keyrings/module_.py index d9a8c058f..3e8d3062a 100644 --- a/examples/src/keyrings/module_.py +++ b/examples/src/keyrings/module_.py @@ -1 +1 @@ -"""Should remove this.""" +"""Should remove this once PYTHONPATH issues are resolved by adding doo files.""" diff --git a/examples/src/module_.py b/examples/src/module_.py index d9a8c058f..3e8d3062a 100644 --- a/examples/src/module_.py +++ b/examples/src/module_.py @@ -1 +1 @@ -"""Should remove this.""" +"""Should remove this once PYTHONPATH issues are resolved by adding doo files.""" diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index 2e5f5a4fd..bd7227fd3 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -81,8 +81,10 @@ def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch 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) - + # Explicitly pass in patched serialization module. + # Patching the module introduces namespace issues + # which causes the method's `isinstance` checks to fail + # by changing the namespace from `serialization.Encoding.DER` to `Encoding.DER`. signer = Signer.from_key_bytes( algorithm=_algorithm, key_bytes=sentinel.key_bytes, diff --git a/test_vector_handlers/tox.ini b/test_vector_handlers/tox.ini index 7004080e3..df2707f6a 100644 --- a/test_vector_handlers/tox.ini +++ b/test_vector_handlers/tox.ini @@ -2,7 +2,7 @@ envlist = # The test vectors depend on new features now, # so until release we can only effectively test the local version of the ESDK. - py{37,38,39,310}-awses_local{,-mpl}, + py{37,38,39,310}-awses_local # 1.2.0 and 1.2.max are being difficult because of attrs bandit, doc8, readme, {flake8,pylint}{,-tests}, @@ -48,8 +48,6 @@ passenv = sitepackages = False deps = -rtest/requirements.txt - # install the MPL if in environment - mpl: -r../requirements_mpl.txt .. commands = {[testenv:base-command]commands} diff --git a/tox.ini b/tox.ini index cee1e35a4..ae30f3122 100644 --- a/tox.ini +++ b/tox.ini @@ -59,7 +59,6 @@ envlist = commands = pytest --basetemp={envtmpdir} -l {posargs} [testenv] -; passenv = AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1,AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,AWS_PROFILE,PIP_CONFIG_FILE passenv = # Identifies AWS KMS key id to use in integration tests AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID \ @@ -80,7 +79,7 @@ passenv = sitepackages = False deps = -rdev_requirements/test-requirements.txt - # install the MPL if in environment + # install the MPL requirements if the `-mpl` suffix is present mpl: -rrequirements_mpl.txt commands = local: {[testenv:base-command]commands} test/ -m local --ignore test/unit/mpl/ From b026b532b59b177cfb63be0019c508d829b41aea Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 11:53:41 -0800 Subject: [PATCH 095/172] cleanup --- .../internal/mpl/cmm_handler.py | 1 - .../internal/mpl/materials_handlers.py | 6 --- .../internal/utils/__init__.py | 18 +++++++++ src/aws_encryption_sdk/streaming_client.py | 37 +++++++------------ 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/aws_encryption_sdk/internal/mpl/cmm_handler.py b/src/aws_encryption_sdk/internal/mpl/cmm_handler.py index 9789651e5..1575e0187 100644 --- a/src/aws_encryption_sdk/internal/mpl/cmm_handler.py +++ b/src/aws_encryption_sdk/internal/mpl/cmm_handler.py @@ -29,7 +29,6 @@ from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey -# TODO-MPL Should this implement interface...? seems like yes since it implements all of interface methods class CMMHandler(CryptoMaterialsManager): """ In instances where encryption materials may be provided by either diff --git a/src/aws_encryption_sdk/internal/mpl/materials_handlers.py b/src/aws_encryption_sdk/internal/mpl/materials_handlers.py index bf32c2718..79312f863 100644 --- a/src/aws_encryption_sdk/internal/mpl/materials_handlers.py +++ b/src/aws_encryption_sdk/internal/mpl/materials_handlers.py @@ -93,9 +93,6 @@ def data_encryption_key(self) -> DataKey: if hasattr(self, "native_materials"): return self.native_materials.data_encryption_key else: - # TODO-MPL This impl is probably wrong, but works for for now - # If this works for all features, great! Remove this comment before launch. - # Otherwise, fix the implementation. mpl_dek = self.mpl_materials.plaintext_data_key return DataKey( # key_provider is unused, but the return type is DataKey @@ -149,9 +146,6 @@ def data_key(self) -> DataKey: if hasattr(self, "native_materials"): return self.native_materials.data_key else: - # TODO-MPL This impl is probably wrong, but works for for now - # If this works for all features, great! Remove this comment before launch. - # Otherwise, fix the implementation. return DataKey( key_provider=MasterKeyInfo( provider_id="", diff --git a/src/aws_encryption_sdk/internal/utils/__init__.py b/src/aws_encryption_sdk/internal/utils/__init__.py index dac38ac73..b65f6df0f 100644 --- a/src/aws_encryption_sdk/internal/utils/__init__.py +++ b/src/aws_encryption_sdk/internal/utils/__init__.py @@ -163,3 +163,21 @@ def source_data_key_length_check(source_data_key, algorithm): actual=len(source_data_key.data_key), required=algorithm.kdf_input_len ) ) + +def exactly_one_arg_is_not_none(*args): + """ + Helper function for internal ESDK logic. + Returns `True` if exactly one item in the list is not `None`. + Returns `False` otherwise. + """ + # Have not found any `not None` + found_one = False + for arg in args: + if arg is not None: + if found_one is False: + # Have not already found a `not None`, found a `not None` => only one `not None` (so far) + found_one = True + else: + # Already found a `not None`, found another `not None` => not exactly one `not None` + return False + return found_one \ No newline at end of file diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index ad998088c..72f18c117 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -59,6 +59,7 @@ serialize_non_framed_open, ) from aws_encryption_sdk.internal.mpl.cmm_handler import CMMHandler +from aws_encryption_sdk.internal.utils import exactly_one_arg_is_not_none from aws_encryption_sdk.internal.utils.commitment import ( validate_commitment_policy_on_decrypt, validate_commitment_policy_on_encrypt, @@ -84,25 +85,6 @@ _LOGGER = logging.getLogger(__name__) -def _exactly_one_arg_is_not_none(*args): - """ - Private helper function. - Returns `True` if exactly one item in the list is not `None`. - Returns `False` otherwise. - """ - # Have not found any `not None` - found_one = False - for arg in args: - if arg is not None: - if found_one is False: - # Have not already found a `not None`, found a `not None` => only one `not None` (so far) - found_one = True - else: - # Already found a `not None`, found another `not None` => not exactly one `not None` - return False - return found_one - - @attr.s(hash=True) # pylint: disable=too-many-instance-attributes @six.add_metaclass(abc.ABCMeta) class _ClientConfig(object): # pylint: disable=too-many-instance-attributes @@ -147,6 +129,7 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(MasterKeyProvider)) ) if _HAS_MPL: + # Keyrings are only available if the MPL is installed in the runtime keyring = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(IKeyring)) ) @@ -158,10 +141,13 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes ) # DEPRECATED: Value is no longer configurable here. Parameter left here to avoid breaking consumers. def _has_mpl_attrs_post_init(self): + """If the MPL is present in the runtime, perform MPL-specific post-init logic + to validate the new object has a valid state. + """ if not hasattr(self, "keyring"): self._no_mpl_attrs_post_init() return - if not _exactly_one_arg_is_not_none(self.materials_manager, self.key_provider, self.keyring): + if not exactly_one_arg_is_not_none(self.materials_manager, self.key_provider, self.keyring): raise TypeError("Exactly one of keyring, materials_manager, or key_provider must be provided") if self.materials_manager is None: if self.key_provider is not None: @@ -187,6 +173,9 @@ def _has_mpl_attrs_post_init(self): self.materials_manager = cmm_handler def _no_mpl_attrs_post_init(self): + """If the MPL is NOT present in the runtime, perform post-init logic + to validate the new object has a valid state. + """ both_cmm_and_mkp_defined = self.materials_manager is not None and self.key_provider is not None neither_cmm_nor_mkp_defined = self.materials_manager is None and self.key_provider is None @@ -560,8 +549,8 @@ def _prep_message(self): if self._encryption_materials.signing_key is None: self.signer = None else: - # MPL verification key is NOT key bytes, it is bytes of the compressed point - # TODO-MPL: clean this up, least-privilege violation. + # MPL verification key is PEM bytes, not DER bytes. + # If the underlying CMM is from the MPL, load PEM bytes. if (isinstance(self.config.materials_manager, CMMHandler) and hasattr(self.config.materials_manager, "mpl_cmm")): self.signer = Signer.from_key_bytes( @@ -928,8 +917,8 @@ def _read_header(self): if decryption_materials.verification_key is None: self.verifier = None else: - # MPL verification key is NOT key bytes, it is bytes of the compressed point - # TODO-MPL: clean this up, least-privilege violation. + # MPL verification key is NOT key bytes; it is bytes of the compressed point. + # If the underlying CMM is from the MPL, load PEM bytes. if (isinstance(self.config.materials_manager, CMMHandler) and hasattr(self.config.materials_manager, "mpl_cmm")): self.verifier = Verifier.from_encoded_point( From 50afa3ade9d8c5f2712392b925fe3b621e97ba4d Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 11:54:25 -0800 Subject: [PATCH 096/172] cleanup --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 72f18c117..032ed7d15 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -918,7 +918,7 @@ def _read_header(self): self.verifier = None else: # MPL verification key is NOT key bytes; it is bytes of the compressed point. - # If the underlying CMM is from the MPL, load PEM bytes. + # If the underlying CMM is from the MPL, load bytes from encoded point. if (isinstance(self.config.materials_manager, CMMHandler) and hasattr(self.config.materials_manager, "mpl_cmm")): self.verifier = Verifier.from_encoded_point( From 1c612a0b9e9cc85bae1f71a7d1027d2901c5de82 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 12:01:11 -0800 Subject: [PATCH 097/172] cleanup --- examples/src/keyrings/hierarchical_keyring.py | 2 +- src/aws_encryption_sdk/internal/utils/__init__.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index 8f8707013..c71719346 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -169,7 +169,7 @@ def encrypt_and_decrypt_with_keyring( # 10. Demonstrate that data encrypted by one tenant's key # cannot be decrypted with by a keyring specific to another tenant. - + # Keyring with tenant B's branch key cannot decrypt data encrypted with tenant A's branch key # This will fail and raise a AWSEncryptionSDKClientError, which we swallow ONLY for demonstration purposes. try: diff --git a/src/aws_encryption_sdk/internal/utils/__init__.py b/src/aws_encryption_sdk/internal/utils/__init__.py index b65f6df0f..b08121281 100644 --- a/src/aws_encryption_sdk/internal/utils/__init__.py +++ b/src/aws_encryption_sdk/internal/utils/__init__.py @@ -164,11 +164,15 @@ def source_data_key_length_check(source_data_key, algorithm): ) ) + def exactly_one_arg_is_not_none(*args): """ Helper function for internal ESDK logic. - Returns `True` if exactly one item in the list is not `None`. + Returns `True` if exactly one item in the provided arguments is not `None`. Returns `False` otherwise. + + :param args: Input arguments to check + :returns: `True` if exactly one item in the provided arguments is not `None`; `False` otherwise """ # Have not found any `not None` found_one = False @@ -180,4 +184,4 @@ def exactly_one_arg_is_not_none(*args): else: # Already found a `not None`, found another `not None` => not exactly one `not None` return False - return found_one \ No newline at end of file + return found_one From bcdb4ba37f189d8fe6407c66f3ee3ccb1dbc7ebe Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 12:19:22 -0800 Subject: [PATCH 098/172] add missing file --- .../example_branch_key_id_supplier.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 examples/src/keyrings/example_branch_key_id_supplier.py diff --git a/examples/src/keyrings/example_branch_key_id_supplier.py b/examples/src/keyrings/example_branch_key_id_supplier.py new file mode 100644 index 000000000..a3ef0df6f --- /dev/null +++ b/examples/src/keyrings/example_branch_key_id_supplier.py @@ -0,0 +1,37 @@ +from aws_cryptographic_materialproviders.mpl.models import GetBranchKeyIdInput, GetBranchKeyIdOutput +from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier +from typing import Dict + + +class ExampleBranchKeyIdSupplier(IBranchKeyIdSupplier): + """Example implementation of a branch key ID supplier.""" + + branch_key_id_for_tenant_A: str + branch_key_id_for_tenant_B: str + + def __init__(self, tenant_1_id, tenant_2_id): + self.branch_key_id_for_tenant_A = tenant_1_id + self.branch_key_id_for_tenant_B = tenant_2_id + + def get_branch_key_id( + self, + # Change this to `native_input` + input: GetBranchKeyIdInput # noqa pylint: disable=redefined-builtin + ) -> GetBranchKeyIdOutput: + """Returns branch key ID from the tenant ID in input's encryption context.""" + encryption_context: Dict[str, str] = input.encryption_context + + if b"tenant" not in encryption_context: + raise ValueError("EncryptionContext invalid, does not contain expected tenant key value pair.") + + tenant_key_id: str = encryption_context.get(b"tenant") + branch_key_id: str + + if tenant_key_id == b"TenantA": + branch_key_id = self.branch_key_id_for_tenant_A + elif tenant_key_id == b"TenantB": + branch_key_id = self.branch_key_id_for_tenant_B + else: + raise ValueError(f"Item does not contain valid tenant ID: {tenant_key_id=}") + + return GetBranchKeyIdOutput(branch_key_id=branch_key_id) \ No newline at end of file From 41fe2f9facf04bbbdf0ccf0168c20aa9e27e059c Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 12:26:47 -0800 Subject: [PATCH 099/172] add missing file --- test_vector_handlers/tox.ini | 3 +++ tox.ini | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/test_vector_handlers/tox.ini b/test_vector_handlers/tox.ini index df2707f6a..e5e467d8a 100644 --- a/test_vector_handlers/tox.ini +++ b/test_vector_handlers/tox.ini @@ -3,6 +3,7 @@ envlist = # The test vectors depend on new features now, # so until release we can only effectively test the local version of the ESDK. py{37,38,39,310}-awses_local + py{311,312}-awses_local{,-mpl} # 1.2.0 and 1.2.max are being difficult because of attrs bandit, doc8, readme, {flake8,pylint}{,-tests}, @@ -48,6 +49,8 @@ passenv = sitepackages = False deps = -rtest/requirements.txt + # Install the MPL requirements if the `-mpl` suffix is present + mpl: -rrequirements_mpl.txt .. commands = {[testenv:base-command]commands} diff --git a/tox.ini b/tox.ini index ae30f3122..72e8ec9fa 100644 --- a/tox.ini +++ b/tox.ini @@ -79,7 +79,7 @@ passenv = sitepackages = False deps = -rdev_requirements/test-requirements.txt - # install the MPL requirements if the `-mpl` suffix is present + # Install the MPL requirements if the `-mpl` suffix is present mpl: -rrequirements_mpl.txt commands = local: {[testenv:base-command]commands} test/ -m local --ignore test/unit/mpl/ From 1ba857e74a1e7e117a8208ac21e235d1c5d2e18a Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 12:31:48 -0800 Subject: [PATCH 100/172] add missing file --- test_vector_handlers/tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_vector_handlers/tox.ini b/test_vector_handlers/tox.ini index e5e467d8a..580b641e0 100644 --- a/test_vector_handlers/tox.ini +++ b/test_vector_handlers/tox.ini @@ -50,7 +50,7 @@ sitepackages = False deps = -rtest/requirements.txt # Install the MPL requirements if the `-mpl` suffix is present - mpl: -rrequirements_mpl.txt + mpl: -r../requirements_mpl.txt .. commands = {[testenv:base-command]commands} From 74bfe127d6ea86de4637b1dfea3621f703bff0cd Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 21 Feb 2024 12:38:32 -0800 Subject: [PATCH 101/172] cleanup --- examples/src/keyrings/example_branch_key_id_supplier.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/src/keyrings/example_branch_key_id_supplier.py b/examples/src/keyrings/example_branch_key_id_supplier.py index a3ef0df6f..a06280fa1 100644 --- a/examples/src/keyrings/example_branch_key_id_supplier.py +++ b/examples/src/keyrings/example_branch_key_id_supplier.py @@ -1,3 +1,5 @@ +"""Example implementation of a branch key ID supplier.""" + from aws_cryptographic_materialproviders.mpl.models import GetBranchKeyIdInput, GetBranchKeyIdOutput from aws_cryptographic_materialproviders.mpl.references import IBranchKeyIdSupplier from typing import Dict @@ -10,12 +12,13 @@ class ExampleBranchKeyIdSupplier(IBranchKeyIdSupplier): branch_key_id_for_tenant_B: str def __init__(self, tenant_1_id, tenant_2_id): + """Example constructor for a branch key ID supplier.""" self.branch_key_id_for_tenant_A = tenant_1_id self.branch_key_id_for_tenant_B = tenant_2_id def get_branch_key_id( self, - # Change this to `native_input` + # TODO-MPL: Change this to `native_input` in Smithy-Dafny input: GetBranchKeyIdInput # noqa pylint: disable=redefined-builtin ) -> GetBranchKeyIdOutput: """Returns branch key ID from the tenant ID in input's encryption context.""" @@ -34,4 +37,4 @@ def get_branch_key_id( else: raise ValueError(f"Item does not contain valid tenant ID: {tenant_key_id=}") - return GetBranchKeyIdOutput(branch_key_id=branch_key_id) \ No newline at end of file + return GetBranchKeyIdOutput(branch_key_id=branch_key_id) From b3b9a0ffd82d962ef4ccea15813b1ba09d6aac3d Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 22 Feb 2024 12:07:24 -0800 Subject: [PATCH 102/172] refactor --- .github/workflows/ci_tests.yaml | 1 + .../internal/mpl/cmm_handler.py | 157 ----------------- .../internal/mpl/materials_handlers.py | 164 ------------------ src/aws_encryption_sdk/streaming_client.py | 10 +- test/unit/mpl/README.md | 1 - test/unit/mpl/test_cmm_handler.py | 97 ----------- 6 files changed, 5 insertions(+), 425 deletions(-) delete mode 100644 src/aws_encryption_sdk/internal/mpl/cmm_handler.py delete mode 100644 src/aws_encryption_sdk/internal/mpl/materials_handlers.py delete mode 100644 test/unit/mpl/README.md delete mode 100644 test/unit/mpl/test_cmm_handler.py diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 803d4741e..3a6b16d45 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -27,6 +27,7 @@ jobs: - ubuntu-latest # Windows fails due to "No module named 'Wrappers'" # This SHOULD be fixed once Dafny generates fully-qualified import statements + # (i.e. doo files, per-package module names) # Disable for now # - windows-latest - macos-latest diff --git a/src/aws_encryption_sdk/internal/mpl/cmm_handler.py b/src/aws_encryption_sdk/internal/mpl/cmm_handler.py deleted file mode 100644 index 1575e0187..000000000 --- a/src/aws_encryption_sdk/internal/mpl/cmm_handler.py +++ /dev/null @@ -1,157 +0,0 @@ -"""Retrieves encryption/decryption materials from an underlying materials provider.""" - -# These dependencies are only loaded if you install the MPL. -try: - # pylint seems to struggle with this conditional import - # pylint: disable=unused-import - from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException - from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, - DecryptMaterialsOutput, - EncryptedDataKey as MPL_EncryptedDataKey, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, - ) - from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager - -except ImportError: - pass - -from typing import List - -from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError -from aws_encryption_sdk.identifiers import CommitmentPolicy -from aws_encryption_sdk.internal.mpl.materials_handlers import DecryptionMaterialsHandler, EncryptionMaterialsHandler -from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest -from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager -from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey - - -class CMMHandler(CryptoMaterialsManager): - """ - In instances where encryption materials may be provided by either - an implementation of the native - `aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager` - or an implementation of the MPL's - `aws_cryptographic_materialproviders.mpl.references.ICryptographicMaterialsManager`, - this provides the correct materials based on the underlying materials manager. - """ - - native_cmm: CryptoMaterialsManager - mpl_cmm: 'ICryptographicMaterialsManager' - - def _is_using_native_cmm(self): - return hasattr(self, "native_cmm") and not hasattr(self, "mpl_cmm") - - def __init__( - self, - cmm: 'CryptoMaterialsManager | ICryptographicMaterialsManager' - ): - """ - Create DecryptionMaterialsHandler. - :param cmm: Underlying cryptographic materials manager - """ - if isinstance(cmm, CryptoMaterialsManager): - self.native_cmm = cmm - elif isinstance(cmm, ICryptographicMaterialsManager): - self.mpl_cmm = cmm - else: - raise ValueError(f"Invalid CMM passed to CMMHandler. cmm: {cmm}") - - def get_encryption_materials( - self, - request: EncryptionMaterialsRequest - ) -> EncryptionMaterialsHandler: - """ - Returns an EncryptionMaterialsHandler for the configured CMM. - :param request: Request for encryption materials - """ - if self._is_using_native_cmm(): - return EncryptionMaterialsHandler(self.native_cmm.get_encryption_materials(request)) - else: - try: - mpl_input: GetEncryptionMaterialsInput = CMMHandler._native_to_mpl_get_encryption_materials( - request - ) - mpl_output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) - return EncryptionMaterialsHandler(mpl_output.encryption_materials) - except AwsCryptographicMaterialProvidersException as mpl_exception: - # Wrap MPL error into the ESDK error type - # so customers only have to catch ESDK error types. - raise AWSEncryptionSDKClientError(mpl_exception) - - @staticmethod - def _native_to_mpl_get_encryption_materials( - request: EncryptionMaterialsRequest - ) -> 'GetEncryptionMaterialsInput': - output: GetEncryptionMaterialsInput = GetEncryptionMaterialsInput( - encryption_context=request.encryption_context, - commitment_policy=CMMHandler._native_to_mpl_commmitment_policy( - request.commitment_policy - ), - max_plaintext_length=request.plaintext_length, - ) - return output - - @staticmethod - def _native_to_mpl_commmitment_policy( - native_commitment_policy: CommitmentPolicy - ) -> 'CommitmentPolicyESDK': - if native_commitment_policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT: - return CommitmentPolicyESDK(value="FORBID_ENCRYPT_ALLOW_DECRYPT") - elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT: - return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_ALLOW_DECRYPT") - elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT: - return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_REQUIRE_DECRYPT") - else: - raise ValueError(f"Invalid native_commitment_policy: {native_commitment_policy}") - - def decrypt_materials( - self, - request: DecryptionMaterialsRequest - ) -> DecryptionMaterialsHandler: - """ - Returns a DecryptionMaterialsHandler for the configured CMM. - :param request: Request for decryption materials - """ - if self._is_using_native_cmm(): - return DecryptionMaterialsHandler(self.native_cmm.decrypt_materials(request)) - else: - try: - mpl_input: 'DecryptMaterialsInput' = \ - CMMHandler._create_mpl_decrypt_materials_input_from_request(request) - mpl_output: 'DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(mpl_input) - return DecryptionMaterialsHandler(mpl_output.decryption_materials) - except AwsCryptographicMaterialProvidersException as mpl_exception: - # Wrap MPL error into the ESDK error type - # so customers only have to catch ESDK error types. - raise AWSEncryptionSDKClientError(mpl_exception) - - @staticmethod - def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> 'AlgorithmSuiteIdESDK': - # MPL algorithm suite ID = hexstr(native_algorithm_id) padded to 4 digits post-`x`. - return AlgorithmSuiteIdESDK(f"{native_algorithm_id:#0{6}x}") - - @staticmethod - def _create_mpl_decrypt_materials_input_from_request( - request: DecryptionMaterialsRequest - ) -> 'DecryptMaterialsInput': - key_blob_list: List[Native_EncryptedDataKey] = request.encrypted_data_keys - list_edks = [MPL_EncryptedDataKey( - key_provider_id=key_blob.key_provider.provider_id, - key_provider_info=key_blob.key_provider.key_info, - ciphertext=key_blob.encrypted_data_key, - ) for key_blob in key_blob_list] - output: DecryptMaterialsInput = DecryptMaterialsInput( - algorithm_suite_id=CMMHandler._native_algorithm_id_to_mpl_algorithm_id( - request.algorithm.algorithm_id - ), - commitment_policy=CMMHandler._native_to_mpl_commmitment_policy( - request.commitment_policy - ), - encrypted_data_keys=list_edks, - encryption_context=request.encryption_context, - ) - return output diff --git a/src/aws_encryption_sdk/internal/mpl/materials_handlers.py b/src/aws_encryption_sdk/internal/mpl/materials_handlers.py deleted file mode 100644 index 79312f863..000000000 --- a/src/aws_encryption_sdk/internal/mpl/materials_handlers.py +++ /dev/null @@ -1,164 +0,0 @@ -"""Provides encryption/decryption materials from an underlying materials provider.""" -# These dependencies are only loaded if you install the MPL. -try: - from aws_cryptographic_materialproviders.mpl.models import ( - DecryptionMaterials as MPL_DecryptionMaterials, - EncryptedDataKey as MPL_EncryptedDataKey, - EncryptionMaterials as MPL_EncryptionMaterials, - ) -except ImportError: - pass - -from typing import Dict, List, Set - -from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite -from aws_encryption_sdk.materials_managers import ( - DecryptionMaterials as Native_DecryptionMaterials, - EncryptionMaterials as Native_EncryptionMaterials, -) -from aws_encryption_sdk.structures import DataKey, EncryptedDataKey as Native_EncryptedDataKey, MasterKeyInfo - - -def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str): - # MPL algorithm suite ID == hex(native algorithm suite ID) - return int(mpl_algorithm_id, 16) - - -class EncryptionMaterialsHandler: - """ - In instances where encryption materials may be provided by either - the native `aws_encryption_sdk.materials_managers.EncryptionMaterials` - or the MPL's `aws_cryptographic_materialproviders.mpl.models.EncryptionMaterials`, - this provides the correct materials based on the configured materials provider. - """ - - native_materials: Native_EncryptionMaterials - mpl_materials: 'MPL_EncryptionMaterials' - - def __init__( - self, - materials: 'Native_EncryptionMaterials | MPL_EncryptionMaterials' - ): - """ - Create EncryptionMaterialsHandler. - :param materials: Underlying encryption materials - """ - if isinstance(materials, Native_EncryptionMaterials): - self.native_materials = materials - elif isinstance(materials, MPL_EncryptionMaterials): - self.mpl_materials = materials - else: - raise ValueError("Invalid EncryptionMaterials passed to EncryptionMaterialsHandler. " - f"materials: {materials}") - - @property - def algorithm(self) -> Algorithm: - """Materials' native Algorithm.""" - if hasattr(self, "native_materials"): - return self.native_materials.algorithm - else: - return AlgorithmSuite.get_by_id( - _mpl_algorithm_id_to_native_algorithm_id( - self.mpl_materials.algorithm_suite.id.value - ) - ) - - @property - def encryption_context(self) -> Dict[str, str]: - """Materials' encryption context.""" - if hasattr(self, "native_materials"): - return self.native_materials.encryption_context - else: - return self.mpl_materials.encryption_context - - @property - def encrypted_data_keys(self) -> List[Native_EncryptedDataKey]: - """Materials' encrypted data keys.""" - if hasattr(self, "native_materials"): - return self.native_materials.encrypted_data_keys - else: - mpl_edk_list: List[MPL_EncryptedDataKey] = self.mpl_materials.encrypted_data_keys - key_blob_list: Set[Native_EncryptedDataKey] = {Native_EncryptedDataKey( - key_provider=MasterKeyInfo( - provider_id=mpl_edk.key_provider_id, - key_info=mpl_edk.key_provider_info, - ), - encrypted_data_key=mpl_edk.ciphertext, - ) for mpl_edk in mpl_edk_list} - return key_blob_list - - @property - def data_encryption_key(self) -> DataKey: - """Materials' data encryption key.""" - if hasattr(self, "native_materials"): - return self.native_materials.data_encryption_key - else: - mpl_dek = self.mpl_materials.plaintext_data_key - return DataKey( - # key_provider is unused, but the return type is DataKey - key_provider=MasterKeyInfo( - provider_id="", - key_info=b'' - ), - data_key=mpl_dek, - encrypted_data_key=b'', # No encrypted DEK - ) - - @property - def signing_key(self) -> bytes: - """Materials' signing key.""" - if hasattr(self, "native_materials"): - return self.native_materials.signing_key - else: - return self.mpl_materials.signing_key - - -class DecryptionMaterialsHandler: - """ - In instances where decryption materials may be provided by either - the native `aws_encryption_sdk.materials_managers.DecryptionMaterials` - or the MPL's `aws_cryptographic_materialproviders.mpl.models.DecryptionMaterials`, - this provides the correct materials based on the configured materials provider. - """ - - native_materials: Native_DecryptionMaterials - mpl_materials: 'MPL_DecryptionMaterials' - - def __init__( - self, - materials: 'Native_DecryptionMaterials | MPL_DecryptionMaterials' - ): - """ - Create DecryptionMaterialsHandler. - :param materials: Underlying decryption materials - """ - if isinstance(materials, Native_DecryptionMaterials): - self.native_materials = materials - elif isinstance(materials, MPL_DecryptionMaterials): - self.mpl_materials = materials - else: - raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler.\ - materials: {materials}") - - @property - def data_key(self) -> DataKey: - """Materials' data key.""" - if hasattr(self, "native_materials"): - return self.native_materials.data_key - else: - return DataKey( - key_provider=MasterKeyInfo( - provider_id="", - key_info=b'' - ), - data_key=self.mpl_materials.plaintext_data_key, - encrypted_data_key=b'', - ) - - @property - def verification_key(self) -> bytes: - """Materials' verification key.""" - if hasattr(self, "native_materials"): - return self.native_materials.verification_key - else: - return self.mpl_materials.verification_key diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 032ed7d15..61f2f88c6 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -58,7 +58,7 @@ serialize_non_framed_close, serialize_non_framed_open, ) -from aws_encryption_sdk.internal.mpl.cmm_handler import CMMHandler +from aws_encryption_sdk.materials_managers.mpl.cmm import MPLCMMHandler from aws_encryption_sdk.internal.utils import exactly_one_arg_is_not_none from aws_encryption_sdk.internal.utils.commitment import ( validate_commitment_policy_on_decrypt, @@ -169,7 +169,7 @@ def _has_mpl_attrs_post_init(self): keyring=self.keyring ) ) - cmm_handler: CryptoMaterialsManager = CMMHandler(cmm) + cmm_handler: CryptoMaterialsManager = MPLCMMHandler(cmm) self.materials_manager = cmm_handler def _no_mpl_attrs_post_init(self): @@ -551,8 +551,7 @@ def _prep_message(self): else: # MPL verification key is PEM bytes, not DER bytes. # If the underlying CMM is from the MPL, load PEM bytes. - if (isinstance(self.config.materials_manager, CMMHandler) - and hasattr(self.config.materials_manager, "mpl_cmm")): + if (isinstance(self.config.materials_manager, MPLCMMHandler)): self.signer = Signer.from_key_bytes( algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key, encoding=serialization.Encoding.PEM, @@ -919,8 +918,7 @@ def _read_header(self): else: # MPL verification key is NOT key bytes; it is bytes of the compressed point. # If the underlying CMM is from the MPL, load bytes from encoded point. - if (isinstance(self.config.materials_manager, CMMHandler) - and hasattr(self.config.materials_manager, "mpl_cmm")): + if (isinstance(self.config.materials_manager, MPLCMMHandler)): self.verifier = Verifier.from_encoded_point( algorithm=header.algorithm, encoded_point=base64.b64encode(decryption_materials.verification_key) diff --git a/test/unit/mpl/README.md b/test/unit/mpl/README.md deleted file mode 100644 index 839feb7a2..000000000 --- a/test/unit/mpl/README.md +++ /dev/null @@ -1 +0,0 @@ -Tests in this file REQUIRE the aws-cryptographic-material-providers module to be installed in order to run. \ No newline at end of file diff --git a/test/unit/mpl/test_cmm_handler.py b/test/unit/mpl/test_cmm_handler.py deleted file mode 100644 index d16374899..000000000 --- a/test/unit/mpl/test_cmm_handler.py +++ /dev/null @@ -1,97 +0,0 @@ -# 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. -"""Test suite to verify the cmm_handler module delegates correctly.""" -import pytest -from aws_cryptographic_materialproviders.mpl.models import ( - EncryptionMaterials as MPL_EncryptionMaterials, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, -) -from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager -from mock import MagicMock, patch - -from aws_encryption_sdk.internal.mpl.cmm_handler import CMMHandler -from aws_encryption_sdk.internal.mpl.materials_handlers import EncryptionMaterialsHandler -from aws_encryption_sdk.materials_managers import ( - EncryptionMaterials as Native_EncryptionMaterials, - EncryptionMaterialsRequest, -) -from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager - -mock_native_cmm = MagicMock(__class__=CryptoMaterialsManager) -mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) -mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) -mock_encryption_materials_handler = MagicMock(__class__=EncryptionMaterialsHandler) -mock_native_encryption_materials = MagicMock(__class__=Native_EncryptionMaterials) -mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) - -pytestmark = [pytest.mark.unit, pytest.mark.local] - - -def test_GIVEN_native_CMM_WHEN_create_CMMHandler_THEN_is_using_native_cmm_returns_True(): - cmm_handler = CMMHandler(cmm=mock_native_cmm) - assert cmm_handler._is_using_native_cmm() - - -def test_GIVEN_mpl_CMM_WHEN_create_CMMHandler_THEN_is_using_native_cmm_returns_False(): - cmm_handler = CMMHandler(cmm=mock_mpl_cmm) - assert not cmm_handler._is_using_native_cmm() - - -def test_GIVEN_unknown_CMM_WHEN_create_CMMHandler_THEN_raise_ValueError(): - with pytest.raises(ValueError): - CMMHandler(cmm="not a CMM") - - -@patch.object(mock_native_cmm, "get_encryption_materials") -def test_GIVEN_native_CMM_WHEN_get_encryption_materials_THEN_return_native_encryption_materials( - mock_get_encryption_materials -): - # Mock: native_cmm.get_encryption_materials returns mock native encryption materials - mock_get_encryption_materials.return_value = mock_native_encryption_materials - - cmm_handler = CMMHandler(cmm=mock_native_cmm) - test = cmm_handler.get_encryption_materials(mock_encryption_materials_request) - - # Verify cmm_handler returns EncryptionMaterialsHandler - assert isinstance(test, EncryptionMaterialsHandler) - # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` - assert test.native_materials == mock_native_encryption_materials - # Verify we actually called `get_encryption_materials` - mock_native_cmm.get_encryption_materials.assert_called_once_with(mock_encryption_materials_request) - - -@patch.object(mock_mpl_cmm, "get_encryption_materials") -@patch("aws_encryption_sdk.internal.mpl.cmm_handler.CMMHandler._native_to_mpl_get_encryption_materials") -def test_GIVEN_mpl_CMM_WHEN_get_encryption_materials_THEN_return_mpl_encryption_materials( - mock_native_to_mpl_get_encryption_materials, - mock_get_encryption_materials, -): - # Mock: mpl_cmm.get_encryption_materials returns mock MPL encryption materials - mock_get_encryption_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) - mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials - mock_get_encryption_materials.return_value = mock_get_encryption_materials_output - - # Mock: CMMHandler._native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput - mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) - mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input - - cmm_handler = CMMHandler(cmm=mock_mpl_cmm) - test = cmm_handler.get_encryption_materials(mock_encryption_materials_request) - - # Verify cmm_handler returns EncryptionMaterialsHandler - assert isinstance(test, EncryptionMaterialsHandler) - # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` - assert test.mpl_materials == mock_mpl_encryption_materials - # Verify we actually called `get_encryption_materials` - mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) From a594125a635c0741e121d87bf367940b22f7610e Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 22 Feb 2024 12:07:33 -0800 Subject: [PATCH 103/172] refactor --- .../materials_managers/mpl/__init__.py | 13 ++ .../materials_managers/mpl/cmm.py | 142 ++++++++++++++++++ .../materials_managers/mpl/materials.py | 135 +++++++++++++++++ 3 files changed, 290 insertions(+) create mode 100644 src/aws_encryption_sdk/materials_managers/mpl/__init__.py create mode 100644 src/aws_encryption_sdk/materials_managers/mpl/cmm.py create mode 100644 src/aws_encryption_sdk/materials_managers/mpl/materials.py diff --git a/src/aws_encryption_sdk/materials_managers/mpl/__init__.py b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py new file mode 100644 index 000000000..295400d76 --- /dev/null +++ b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py @@ -0,0 +1,13 @@ +# 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. +"""Modules related to the MPL's materials managers interfaces.""" diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py new file mode 100644 index 000000000..e16b49d51 --- /dev/null +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -0,0 +1,142 @@ +"""Retrieves encryption/decryption materials from the MPL.""" + +# These dependencies are only loaded if you install the MPL. +try: + # pylint seems to struggle with this conditional import + # pylint: disable=unused-import + from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException + from aws_cryptographic_materialproviders.mpl.models import ( + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, + DecryptMaterialsInput, + DecryptMaterialsOutput, + EncryptedDataKey as MPL_EncryptedDataKey, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, + ) + from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager + +except ImportError: + pass + +from typing import List + +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.identifiers import CommitmentPolicy +from aws_encryption_sdk.materials_managers.mpl.materials import MPLEncryptionMaterials, MPLDecryptionMaterials +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey + + +class MPLCMMHandler(CryptoMaterialsManager): + """ + In instances where encryption materials are provided by an implementation of the MPL's + `aws_cryptographic_materialproviders.mpl.references.ICryptographicMaterialsManager`, + this maps the ESDK CMM interfaces to the MPL CMM. + """ + + mpl_cmm: 'ICryptographicMaterialsManager' + + def __init__( + self, + mpl_cmm: 'ICryptographicMaterialsManager' + ): + """ + Create DecryptionMaterialsHandler. + :param cmm: Underlying cryptographic materials manager + """ + if isinstance(mpl_cmm, ICryptographicMaterialsManager): + self.mpl_cmm = mpl_cmm + else: + raise ValueError(f"Invalid CMM passed to MPLCMMHandler. cmm: {mpl_cmm}") + + def get_encryption_materials( + self, + request: EncryptionMaterialsRequest + ) -> MPLEncryptionMaterials: + """ + Returns an EncryptionMaterialsHandler for the configured CMM. + :param request: Request for encryption materials + """ + try: + mpl_input: GetEncryptionMaterialsInput = MPLCMMHandler._native_to_mpl_get_encryption_materials( + request + ) + mpl_output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) + return MPLEncryptionMaterials(mpl_output.encryption_materials) + except AwsCryptographicMaterialProvidersException as mpl_exception: + # Wrap MPL error into the ESDK error type + # so customers only have to catch ESDK error types. + raise AWSEncryptionSDKClientError(mpl_exception) + + @staticmethod + def _native_to_mpl_get_encryption_materials( + request: EncryptionMaterialsRequest + ) -> 'GetEncryptionMaterialsInput': + output: GetEncryptionMaterialsInput = GetEncryptionMaterialsInput( + encryption_context=request.encryption_context, + commitment_policy=MPLCMMHandler._native_to_mpl_commmitment_policy( + request.commitment_policy + ), + max_plaintext_length=request.plaintext_length, + ) + return output + + @staticmethod + def _native_to_mpl_commmitment_policy( + native_commitment_policy: CommitmentPolicy + ) -> 'CommitmentPolicyESDK': + if native_commitment_policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT: + return CommitmentPolicyESDK(value="FORBID_ENCRYPT_ALLOW_DECRYPT") + elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT: + return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_ALLOW_DECRYPT") + elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT: + return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_REQUIRE_DECRYPT") + else: + raise ValueError(f"Invalid native_commitment_policy: {native_commitment_policy}") + + def decrypt_materials( + self, + request: DecryptionMaterialsRequest + ) -> MPLDecryptionMaterials: + """ + Returns a DecryptionMaterialsHandler for the configured CMM. + :param request: Request for decryption materials + """ + try: + mpl_input: 'DecryptMaterialsInput' = \ + MPLCMMHandler._create_mpl_decrypt_materials_input_from_request(request) + mpl_output: 'DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(mpl_input) + return MPLDecryptionMaterials(mpl_output.decryption_materials) + except AwsCryptographicMaterialProvidersException as mpl_exception: + # Wrap MPL error into the ESDK error type + # so customers only have to catch ESDK error types. + raise AWSEncryptionSDKClientError(mpl_exception) + + @staticmethod + def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> 'AlgorithmSuiteIdESDK': + # MPL algorithm suite ID = hexstr(native_algorithm_id) padded to 4 digits post-`x`. + return AlgorithmSuiteIdESDK(f"{native_algorithm_id:#0{6}x}") + + @staticmethod + def _create_mpl_decrypt_materials_input_from_request( + request: DecryptionMaterialsRequest + ) -> 'DecryptMaterialsInput': + key_blob_list: List[Native_EncryptedDataKey] = request.encrypted_data_keys + list_edks = [MPL_EncryptedDataKey( + key_provider_id=key_blob.key_provider.provider_id, + key_provider_info=key_blob.key_provider.key_info, + ciphertext=key_blob.encrypted_data_key, + ) for key_blob in key_blob_list] + output: DecryptMaterialsInput = DecryptMaterialsInput( + algorithm_suite_id=MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id( + request.algorithm.algorithm_id + ), + commitment_policy=MPLCMMHandler._native_to_mpl_commmitment_policy( + request.commitment_policy + ), + encrypted_data_keys=list_edks, + encryption_context=request.encryption_context, + ) + return output diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py new file mode 100644 index 000000000..fdcf2ec06 --- /dev/null +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -0,0 +1,135 @@ +"""Provides encryption/decryption materials from an underlying materials provider.""" +# These dependencies are only loaded if you install the MPL. +try: + from aws_cryptographic_materialproviders.mpl.models import ( + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, + ) +except ImportError: + pass + +from typing import Dict, List, Set + +from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite +from aws_encryption_sdk.materials_managers import ( + DecryptionMaterials as Native_DecryptionMaterials, + EncryptionMaterials as Native_EncryptionMaterials, +) +from aws_encryption_sdk.structures import DataKey, EncryptedDataKey as Native_EncryptedDataKey, MasterKeyInfo + + +def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str) -> int: + # MPL algorithm suite ID == hex(native algorithm suite ID) + return int(mpl_algorithm_id, 16) + + +class MPLEncryptionMaterials(Native_EncryptionMaterials): + """ + In instances where encryption materials are be provided by + the MPL's `aws_cryptographic_materialproviders.mpl.models.EncryptionMaterials`, + this maps the ESDK interfaces to the underlying MPL materials. + """ + + mpl_materials: 'MPL_EncryptionMaterials' + + def __init__( + self, + materials: 'MPL_EncryptionMaterials' + ): + """ + Create MPLEncryptionMaterialsHandler. + :param materials: Underlying encryption materials + """ + if isinstance(materials, MPL_EncryptionMaterials): + self.mpl_materials = materials + else: + raise ValueError("Invalid EncryptionMaterials passed to EncryptionMaterialsHandler. " + f"materials: {materials}") + + @property + def algorithm(self) -> Algorithm: + """Materials' native Algorithm.""" + return AlgorithmSuite.get_by_id( + _mpl_algorithm_id_to_native_algorithm_id( + self.mpl_materials.algorithm_suite.id.value + ) + ) + + @property + def encryption_context(self) -> Dict[str, str]: + """Materials' encryption context.""" + return self.mpl_materials.encryption_context + + @property + def encrypted_data_keys(self) -> List[Native_EncryptedDataKey]: + """Materials' encrypted data keys.""" + mpl_edk_list: List[MPL_EncryptedDataKey] = self.mpl_materials.encrypted_data_keys + key_blob_list: Set[Native_EncryptedDataKey] = {Native_EncryptedDataKey( + key_provider=MasterKeyInfo( + provider_id=mpl_edk.key_provider_id, + key_info=mpl_edk.key_provider_info, + ), + encrypted_data_key=mpl_edk.ciphertext, + ) for mpl_edk in mpl_edk_list} + return key_blob_list + + @property + def data_encryption_key(self) -> DataKey: + """Materials' data encryption key.""" + mpl_dek = self.mpl_materials.plaintext_data_key + return DataKey( + # key_provider is unused, but the return type is DataKey + key_provider=MasterKeyInfo( + provider_id="", + key_info=b'' + ), + data_key=mpl_dek, + encrypted_data_key=b'', # No encrypted DEK + ) + + @property + def signing_key(self) -> bytes: + """Materials' signing key.""" + return self.mpl_materials.signing_key + + +class MPLDecryptionMaterials(Native_DecryptionMaterials): + """ + In instances where decryption materials are be provided by + the MPL's `aws_cryptographic_materialproviders.mpl.models.DecryptionMaterials`, + this maps the ESDK interfaces to the underlying MPL materials. + """ + + mpl_materials: 'MPL_DecryptionMaterials' + + def __init__( + self, + materials: 'MPL_DecryptionMaterials' + ): + """ + Create DecryptionMaterialsHandler. + :param materials: Underlying decryption materials + """ + if isinstance(materials, MPL_DecryptionMaterials): + self.mpl_materials = materials + else: + raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler.\ + materials: {materials}") + + @property + def data_key(self) -> DataKey: + """Materials' data key.""" + return DataKey( + key_provider=MasterKeyInfo( + provider_id="", + key_info=b'' + ), + data_key=self.mpl_materials.plaintext_data_key, + encrypted_data_key=b'', + ) + + @property + def verification_key(self) -> bytes: + """Materials' verification key.""" + return self.mpl_materials.verification_key From fdd2eda60f42aeea832b7851db1487dfb2784882 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 10:03:34 -0800 Subject: [PATCH 104/172] unit tests --- .../internal/mpl/__init__.py | 13 -------- .../materials_managers/mpl/cmm.py | 20 ++++++----- .../materials_managers/mpl/materials.py | 33 +++++++++++-------- 3 files changed, 32 insertions(+), 34 deletions(-) delete mode 100644 src/aws_encryption_sdk/internal/mpl/__init__.py diff --git a/src/aws_encryption_sdk/internal/mpl/__init__.py b/src/aws_encryption_sdk/internal/mpl/__init__.py deleted file mode 100644 index 41497cc20..000000000 --- a/src/aws_encryption_sdk/internal/mpl/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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. -"""Modules related to the MPL.""" diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index e16b49d51..cd789b994 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -15,9 +15,9 @@ GetEncryptionMaterialsOutput, ) from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager - + _HAS_MPL = True except ImportError: - pass + _HAS_MPL = False from typing import List @@ -43,9 +43,12 @@ def __init__( mpl_cmm: 'ICryptographicMaterialsManager' ): """ - Create DecryptionMaterialsHandler. - :param cmm: Underlying cryptographic materials manager + Create MPLCMMHandler. + :param mpl_cmm: Underlying MPL cryptographic materials manager """ + if not _HAS_MPL: + raise ImportError("You MUST install the aws-cryptographic-material-providers " + f"library to create an instance of {MPLCMMHandler}") if isinstance(mpl_cmm, ICryptographicMaterialsManager): self.mpl_cmm = mpl_cmm else: @@ -74,11 +77,12 @@ def get_encryption_materials( def _native_to_mpl_get_encryption_materials( request: EncryptionMaterialsRequest ) -> 'GetEncryptionMaterialsInput': + commitment_policy = MPLCMMHandler._native_to_mpl_commmitment_policy( + request.commitment_policy + ) output: GetEncryptionMaterialsInput = GetEncryptionMaterialsInput( encryption_context=request.encryption_context, - commitment_policy=MPLCMMHandler._native_to_mpl_commmitment_policy( - request.commitment_policy - ), + commitment_policy=commitment_policy, max_plaintext_length=request.plaintext_length, ) return output @@ -101,7 +105,7 @@ def decrypt_materials( request: DecryptionMaterialsRequest ) -> MPLDecryptionMaterials: """ - Returns a DecryptionMaterialsHandler for the configured CMM. + Returns a MPLDecryptionMaterials for the configured CMM. :param request: Request for decryption materials """ try: diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index fdcf2ec06..bd4b5f729 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -6,8 +6,9 @@ EncryptedDataKey as MPL_EncryptedDataKey, EncryptionMaterials as MPL_EncryptionMaterials, ) + _HAS_MPL = True except ImportError: - pass + _HAS_MPL = False from typing import Dict, List, Set @@ -35,17 +36,20 @@ class MPLEncryptionMaterials(Native_EncryptionMaterials): def __init__( self, - materials: 'MPL_EncryptionMaterials' + mpl_materials: 'MPL_EncryptionMaterials' ): """ - Create MPLEncryptionMaterialsHandler. + Create MPLEncryptionMaterials. :param materials: Underlying encryption materials """ - if isinstance(materials, MPL_EncryptionMaterials): - self.mpl_materials = materials + if not _HAS_MPL: + raise ImportError("You MUST install the aws-cryptographic-material-providers " + f"library to create an instance of {MPLEncryptionMaterials}") + if isinstance(mpl_materials, MPL_EncryptionMaterials): + self.mpl_materials = mpl_materials else: - raise ValueError("Invalid EncryptionMaterials passed to EncryptionMaterialsHandler. " - f"materials: {materials}") + raise ValueError("Invalid EncryptionMaterials passed to MPLEncryptionMaterials. " + f"materials: {mpl_materials}") @property def algorithm(self) -> Algorithm: @@ -105,17 +109,20 @@ class MPLDecryptionMaterials(Native_DecryptionMaterials): def __init__( self, - materials: 'MPL_DecryptionMaterials' + mpl_materials: 'MPL_DecryptionMaterials' ): """ - Create DecryptionMaterialsHandler. + Create MPLDecryptionMaterials. :param materials: Underlying decryption materials """ - if isinstance(materials, MPL_DecryptionMaterials): - self.mpl_materials = materials + if not _HAS_MPL: + raise ImportError("You MUST install the aws-cryptographic-material-providers " + f"library to create an instance of {MPLDecryptionMaterials}") + if isinstance(mpl_materials, MPL_DecryptionMaterials): + self.mpl_materials = mpl_materials else: - raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsHandler.\ - materials: {materials}") + raise ValueError(f"Invalid DecryptionMaterials passed to MPLDecryptionMaterials.\ + materials: {mpl_materials}") @property def data_key(self) -> DataKey: From 0138f226a73bff13fe1e24d865b09ec7e2ff42b2 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 10:03:47 -0800 Subject: [PATCH 105/172] unit tests --- test/unit/test_material_managers_mpl_cmm.py | 278 ++++++++++++++++++ .../test_material_managers_mpl_materials.py | 221 ++++++++++++++ 2 files changed, 499 insertions(+) create mode 100644 test/unit/test_material_managers_mpl_cmm.py create mode 100644 test/unit/test_material_managers_mpl_materials.py diff --git a/test/unit/test_material_managers_mpl_cmm.py b/test/unit/test_material_managers_mpl_cmm.py new file mode 100644 index 000000000..77bf5502d --- /dev/null +++ b/test/unit/test_material_managers_mpl_cmm.py @@ -0,0 +1,278 @@ +# 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" + +import pytest +from mock import MagicMock, patch + + +from aws_encryption_sdk.identifiers import CommitmentPolicy +import aws_encryption_sdk.materials_managers.mpl.cmm +from aws_encryption_sdk.materials_managers.mpl.cmm import MPLCMMHandler +from aws_encryption_sdk.materials_managers.mpl.materials import ( + MPLEncryptionMaterials, + MPLDecryptionMaterials, +) + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException + from aws_cryptographic_materialproviders.mpl.models import ( + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, + DecryptMaterialsInput, + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptionMaterials as MPL_EncryptionMaterials, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, + ) + from aws_cryptographic_materialproviders.mpl.references import ( + ICryptographicMaterialsManager + ) + HAS_MPL = True + + mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) + mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) + mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) + +except ImportError: + HAS_MPL = False + + # Ensure references to these mocks exist, even if they aren't used in a non-MPL context + mock_mpl_cmm = None + mock_mpl_encryption_materials = None + mock_mpl_decrypt_materials = None + +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.materials_managers import ( + EncryptionMaterialsRequest, + DecryptionMaterialsRequest, +) + + +mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) +mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) +mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): + """If the MPL IS NOT installed in the runtime environment, + assert the cmm has _HAS_MPL set to False""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is False + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): + """If the MPL IS installed in the runtime environment, + assert the cmm has _HAS_MPL set to True""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is True + + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): + with pytest.raises(ImportError): + MPLCMMHandler(mpl_cmm="doesn't matter") + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): + mpl_cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + + assert mpl_cmm_handler.mpl_cmm == mock_mpl_cmm + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): + with pytest.raises(ValueError): + MPLCMMHandler(mpl_cmm="not a valid mpl_cmm") + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch.object(mock_mpl_cmm, "get_encryption_materials") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_get_encryption_materials") +def test_GIVEN_valid_request_WHEN_call_get_encryption_materials_THEN_return_MPLEncryptionMaterials( + mock_native_to_mpl_get_encryption_materials, + mock_get_encryption_materials, +): + + # Mock: mpl_cmm.get_encryption_materials returns mock MPL encryption materials + mock_get_encryption_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) + mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials + mock_get_encryption_materials.return_value = mock_get_encryption_materials_output + + # Mock: CMMHandler._native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput + mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) + mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + test = cmm_handler.get_encryption_materials(mock_encryption_materials_request) + + # Verify cmm_handler returns MPLEncryptionMaterials + assert isinstance(test, MPLEncryptionMaterials) + # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` + assert test.mpl_materials == mock_mpl_encryption_materials + # Verify we actually called `get_encryption_materials` + mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") +def test_GIVEN_get_encryption_materials_raises_MPL_Exception_WHEN_call_get_encryption_materials_THEN_raise_ESDK_Exception( + _ +): + with pytest.raises(AWSEncryptionSDKClientError): + with patch.object(mock_mpl_cmm, "get_encryption_materials", + side_effect=AwsCryptographicMaterialProvidersException("any")): + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + cmm_handler.get_encryption_materials(mock_encryption_materials_request) + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") +def test_GIVEN_native_to_mpl_commmitment_policy_returns_valid_policy_WHEN_call_native_to_mpl_get_encryption_materials_THEN_returns_GetEncryptionMaterialsInput( + mock_mpl_commitment_policy +): + mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) + mock_mpl_commitment_policy.return_value = mock_commitment_policy + + output = MPLCMMHandler._native_to_mpl_get_encryption_materials(mock_encryption_materials_request) + + # verify correctness of returned value + assert isinstance(output, GetEncryptionMaterialsInput) + assert output.encryption_context == mock_encryption_materials_request.encryption_context + assert output.commitment_policy == mock_commitment_policy + assert output.max_plaintext_length == mock_encryption_materials_request.plaintext_length + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): + native_commitment_policy = CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT + + output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + + assert isinstance(output, CommitmentPolicyESDK) + assert output.value == "FORBID_ENCRYPT_ALLOW_DECRYPT" + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): + native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT + + output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + + assert isinstance(output, CommitmentPolicyESDK) + assert output.value == "REQUIRE_ENCRYPT_ALLOW_DECRYPT" + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): + native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + + output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + + assert isinstance(output, CommitmentPolicyESDK) + assert output.value == "REQUIRE_ENCRYPT_REQUIRE_DECRYPT" + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_call_native_to_mpl_commmitment_policyTHEN_raise_ValueError(): + native_commitment_policy = "not a commitment policy" + + with pytest.raises(ValueError): + MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch.object(mock_mpl_cmm, "decrypt_materials") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") +def test_GIVEN_valid_request_WHEN_call_decrypt_materials_THEN_return_MPLDecryptionMaterials( + mock_native_to_mpl_decrypt_materials, + mock_get_encryption_materials, +): + + # Mock: mpl_cmm.get_decryption_materials returns mock MPL decryption materials + mock_decrypt_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) + mock_decrypt_materials_output.decryption_materials = mock_mpl_decrypt_materials + mock_get_encryption_materials.return_value = mock_decrypt_materials_output + + # Mock: CMMHandler._create_mpl_decrypt_materials_input_from_request creates a DecryptMaterialsInput + mock_decrypt_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) + mock_native_to_mpl_decrypt_materials.return_value = mock_decrypt_materials_input + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + output = cmm_handler.decrypt_materials(mock_decryption_materials_request) + + # Verify cmm_handler returns MPLDecryptionMaterials + assert isinstance(output, MPLDecryptionMaterials) + # Verify returned MPLDecryptionMaterials uses the output of `decrypt_materials` + assert output.mpl_materials == mock_mpl_decrypt_materials + # Verify we actually called `decrypt_materials` + mock_mpl_cmm.decrypt_materials.assert_called_once_with(mock_decrypt_materials_input) + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") +def test_GIVEN_decrypt_materials_raises_MPL_Exception_WHEN_call_decrypt_materials_THEN_raise_ESDK_Exception( + _ +): + with pytest.raises(AWSEncryptionSDKClientError): + with patch.object(mock_mpl_cmm, "decrypt_materials", + side_effect=AwsCryptographicMaterialProvidersException("any")): + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + cmm_handler.decrypt_materials(mock_decryption_materials_request) + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_WHEN_call_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_AlgorithmSuiteIdESDK(): + some_native_algorithm_id = 0x0000 # Not a real algorithm ID, but fits the format + + mpl_output = MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id( + some_native_algorithm_id + ) + + assert isinstance(mpl_output, AlgorithmSuiteIdESDK) + assert mpl_output.value == "0x0000" + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") +def test__create_mpl_decrypt_materials_input_from_request( + mock_mpl_commitment_policy, + mock_mpl_algorithm_id, +): + mock_algorithm_id = "0x1234" # Some fake algorithm ID that fits the format + mock_mpl_algorithm_id.return_value = mock_algorithm_id + mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) + mock_mpl_commitment_policy.return_value = mock_commitment_policy + + # mock_decryption_materials_request.algorithm = + + output = MPLCMMHandler._create_mpl_decrypt_materials_input_from_request(mock_decryption_materials_request) + + assert isinstance(output, DecryptMaterialsInput) + assert output.algorithm_suite_id == mock_algorithm_id + assert output.commitment_policy == mock_commitment_policy + assert output.encryption_context == mock_decryption_materials_request.encryption_context + + assert len(output.encrypted_data_keys) == len(mock_decryption_materials_request.encrypted_data_keys) + for i in range(len(output.encrypted_data_keys)): + # Assume input[i] == output[i], seems to work + output_edk = output.encrypted_data_keys[i] + input_edk = mock_decryption_materials_request[i] + assert output_edk.key_provider_id == input_edk.key_provider.provider_id + assert output_edk.key_provider_info == input_edk.key_provider.key_info + assert output_edk.ciphertext == input_edk.encrypted_data_key diff --git a/test/unit/test_material_managers_mpl_materials.py b/test/unit/test_material_managers_mpl_materials.py new file mode 100644 index 000000000..250efeb7e --- /dev/null +++ b/test/unit/test_material_managers_mpl_materials.py @@ -0,0 +1,221 @@ +# 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" + +import pytest +from mock import MagicMock, patch, PropertyMock +from typing import Dict, List + +from aws_encryption_sdk.identifiers import CommitmentPolicy +import aws_encryption_sdk.materials_managers.mpl.materials +from aws_encryption_sdk.materials_managers.mpl.materials import ( + MPLEncryptionMaterials, + MPLDecryptionMaterials, +) +from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException + from aws_cryptographic_materialproviders.mpl.models import ( + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, + DecryptMaterialsInput, + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, + ) + from aws_cryptographic_materialproviders.mpl.references import ( + ICryptographicMaterialsManager + ) + HAS_MPL = True + + mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) + mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) + +except ImportError: + HAS_MPL = False + + # Ensure references to these mocks exist, even if they aren't used in a non-MPL context + mock_mpl_cmm = None + mock_mpl_encryption_materials = None + mock_mpl_decrypt_materials = None + +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.materials_managers import ( + EncryptionMaterialsRequest, + DecryptionMaterialsRequest, +) + + +mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) +mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) +mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): + """If the MPL IS NOT installed in the runtime environment, + assert the cmm has _HAS_MPL set to False""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is False + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): + """If the MPL IS installed in the runtime environment, + assert the cmm has _HAS_MPL set to True""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is True + + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): + with pytest.raises(ImportError): + MPLEncryptionMaterials(mpl_materials="doesn't matter") + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + + assert mpl_encryption_materials.mpl_materials == mock_mpl_encryption_materials + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): + with pytest.raises(ValueError): + MPLEncryptionMaterials(mpl_materials="not a valid mpl_materials") + +def test_mpl_to_native(): + some_mpl_algorithm_id = "0x1234" # Not a real algorithm ID, but fits the format + + native_output = aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id( + some_mpl_algorithm_id + ) + + assert native_output == 0x1234 + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch("aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.materials.AlgorithmSuite.get_by_id") +def test_GIVEN_valid_mpl_algorithm_id_WHEN_get_algorithm_THEN_valid_native_algorithm_id( + mock_algorithm, + mock_native_algorithm_id, +): + # Mock valid conversion from MPL to native algorithm ID + mock_native_algorithm_id.return_value = 0x1234 + + # Mock valid lookup in native AlgorithmSuite lookup + mock_algorithm.return_value = MagicMock(__class__=AlgorithmSuite) + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.algorithm + assert output == mock_algorithm() # property calls automatically, we need to call the mock + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_valid_native_algorithm_id(): + mock_encryption_context = MagicMock(__class__=Dict[str, str]) + mock_mpl_encryption_materials.encryption_context = mock_encryption_context + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.encryption_context + + assert output == mock_encryption_context + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_valid_nativefadsf_algorithm_id(): + mock_edk = MagicMock(__class__=MPL_EncryptedDataKey) + mock_mpl_key_provider_id = MagicMock(__class__=str) + mock_edk.key_provider_id = mock_mpl_key_provider_id + mock_mpl_key_provider_info = MagicMock(__class__=bytes) + mock_edk.key_provider_info = mock_mpl_key_provider_info + mock_mpl_ciphertext = MagicMock(__class__=bytes) + mock_edk.ciphertext = mock_mpl_ciphertext + + mock_edks = [ mock_edk ] + mock_mpl_encryption_materials.encrypted_data_keys = mock_edks + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.encrypted_data_keys + output_as_list = list(output) + + assert len(output_as_list) == len(mock_edks) + for i in range(len(output_as_list)): + # assume output[i] corresponds to input[i] + native_edk = output_as_list[i] + mpl_edk = mock_edks[i] + + assert native_edk.encrypted_data_key == mpl_edk.ciphertext + assert native_edk.key_provider.provider_id == mpl_edk.key_provider_id + assert native_edk.key_provider.key_info == mpl_edk.key_provider_info + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_valid_nativefadsffadsfa_algorithm_id(): + mock_data_key = MagicMock(__class__=bytes) + mock_mpl_encryption_materials.plaintext_data_key = mock_data_key + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.data_encryption_key + + assert output.key_provider.provider_id == "" + assert output.key_provider.key_info == b"" + assert output.data_key == mock_data_key + assert output.encrypted_data_key == b"" + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_valid_nativefasdfasdffadsf_algorithm_id(): + mock_signing_key = MagicMock(__class__=bytes) + mock_mpl_encryption_materials.signing_key = mock_signing_key + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.signing_key + + assert output == mock_signing_key + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_valid_nativeffasdfasdadsffadsfa_algorithm_id(): + mock_data_key = MagicMock(__class__=bytes) + mock_mpl_decrypt_materials.plaintext_data_key = mock_data_key + + mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.data_key + + assert output.key_provider.provider_id == "" + assert output.key_provider.key_info == b"" + assert output.data_key == mock_data_key + assert output.encrypted_data_key == b"" + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_validadsfasdf_nativefasdfasdffadsf_algorithm_id(): + mock_verification_key = MagicMock(__class__=bytes) + mock_mpl_decrypt_materials.verification_key = mock_verification_key + + mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.verification_key + + assert output == mock_verification_key From f213e1912c4c87ead95eb92734c959d5ea91a388 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 10:14:13 -0800 Subject: [PATCH 106/172] upgrade image --- buildspec.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/buildspec.yml b/buildspec.yml index 3d70c144d..5dbd3f2b8 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -108,6 +108,8 @@ batch: buildspec: codebuild/coverage/coverage.yml - identifier: code_coverage_mpl buildspec: codebuild/coverage/coverage_mpl.yml + env: + image: aws/codebuild/standard:7.0 - identifier: compliance buildspec: codebuild/compliance/compliance.yml From d55f2963b82270f1a3377ff524a55ae663b5675a Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 10:23:56 -0800 Subject: [PATCH 107/172] refactor tests --- test/unit/mpl/__init__.py | 12 + .../mpl/test_material_managers_mpl_cmm.py | 278 ++++++++++++++++++ .../test_material_managers_mpl_materials.py | 221 ++++++++++++++ tox.ini | 12 +- 4 files changed, 516 insertions(+), 7 deletions(-) create mode 100644 test/unit/mpl/__init__.py create mode 100644 test/unit/mpl/test_material_managers_mpl_cmm.py create mode 100644 test/unit/mpl/test_material_managers_mpl_materials.py diff --git a/test/unit/mpl/__init__.py b/test/unit/mpl/__init__.py new file mode 100644 index 000000000..53a960891 --- /dev/null +++ b/test/unit/mpl/__init__.py @@ -0,0 +1,12 @@ +# 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. diff --git a/test/unit/mpl/test_material_managers_mpl_cmm.py b/test/unit/mpl/test_material_managers_mpl_cmm.py new file mode 100644 index 000000000..77bf5502d --- /dev/null +++ b/test/unit/mpl/test_material_managers_mpl_cmm.py @@ -0,0 +1,278 @@ +# 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" + +import pytest +from mock import MagicMock, patch + + +from aws_encryption_sdk.identifiers import CommitmentPolicy +import aws_encryption_sdk.materials_managers.mpl.cmm +from aws_encryption_sdk.materials_managers.mpl.cmm import MPLCMMHandler +from aws_encryption_sdk.materials_managers.mpl.materials import ( + MPLEncryptionMaterials, + MPLDecryptionMaterials, +) + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException + from aws_cryptographic_materialproviders.mpl.models import ( + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, + DecryptMaterialsInput, + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptionMaterials as MPL_EncryptionMaterials, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, + ) + from aws_cryptographic_materialproviders.mpl.references import ( + ICryptographicMaterialsManager + ) + HAS_MPL = True + + mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) + mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) + mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) + +except ImportError: + HAS_MPL = False + + # Ensure references to these mocks exist, even if they aren't used in a non-MPL context + mock_mpl_cmm = None + mock_mpl_encryption_materials = None + mock_mpl_decrypt_materials = None + +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.materials_managers import ( + EncryptionMaterialsRequest, + DecryptionMaterialsRequest, +) + + +mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) +mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) +mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): + """If the MPL IS NOT installed in the runtime environment, + assert the cmm has _HAS_MPL set to False""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is False + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): + """If the MPL IS installed in the runtime environment, + assert the cmm has _HAS_MPL set to True""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is True + + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): + with pytest.raises(ImportError): + MPLCMMHandler(mpl_cmm="doesn't matter") + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): + mpl_cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + + assert mpl_cmm_handler.mpl_cmm == mock_mpl_cmm + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): + with pytest.raises(ValueError): + MPLCMMHandler(mpl_cmm="not a valid mpl_cmm") + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch.object(mock_mpl_cmm, "get_encryption_materials") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_get_encryption_materials") +def test_GIVEN_valid_request_WHEN_call_get_encryption_materials_THEN_return_MPLEncryptionMaterials( + mock_native_to_mpl_get_encryption_materials, + mock_get_encryption_materials, +): + + # Mock: mpl_cmm.get_encryption_materials returns mock MPL encryption materials + mock_get_encryption_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) + mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials + mock_get_encryption_materials.return_value = mock_get_encryption_materials_output + + # Mock: CMMHandler._native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput + mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) + mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + test = cmm_handler.get_encryption_materials(mock_encryption_materials_request) + + # Verify cmm_handler returns MPLEncryptionMaterials + assert isinstance(test, MPLEncryptionMaterials) + # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` + assert test.mpl_materials == mock_mpl_encryption_materials + # Verify we actually called `get_encryption_materials` + mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") +def test_GIVEN_get_encryption_materials_raises_MPL_Exception_WHEN_call_get_encryption_materials_THEN_raise_ESDK_Exception( + _ +): + with pytest.raises(AWSEncryptionSDKClientError): + with patch.object(mock_mpl_cmm, "get_encryption_materials", + side_effect=AwsCryptographicMaterialProvidersException("any")): + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + cmm_handler.get_encryption_materials(mock_encryption_materials_request) + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") +def test_GIVEN_native_to_mpl_commmitment_policy_returns_valid_policy_WHEN_call_native_to_mpl_get_encryption_materials_THEN_returns_GetEncryptionMaterialsInput( + mock_mpl_commitment_policy +): + mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) + mock_mpl_commitment_policy.return_value = mock_commitment_policy + + output = MPLCMMHandler._native_to_mpl_get_encryption_materials(mock_encryption_materials_request) + + # verify correctness of returned value + assert isinstance(output, GetEncryptionMaterialsInput) + assert output.encryption_context == mock_encryption_materials_request.encryption_context + assert output.commitment_policy == mock_commitment_policy + assert output.max_plaintext_length == mock_encryption_materials_request.plaintext_length + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): + native_commitment_policy = CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT + + output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + + assert isinstance(output, CommitmentPolicyESDK) + assert output.value == "FORBID_ENCRYPT_ALLOW_DECRYPT" + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): + native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT + + output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + + assert isinstance(output, CommitmentPolicyESDK) + assert output.value == "REQUIRE_ENCRYPT_ALLOW_DECRYPT" + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): + native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + + output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + + assert isinstance(output, CommitmentPolicyESDK) + assert output.value == "REQUIRE_ENCRYPT_REQUIRE_DECRYPT" + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_call_native_to_mpl_commmitment_policyTHEN_raise_ValueError(): + native_commitment_policy = "not a commitment policy" + + with pytest.raises(ValueError): + MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch.object(mock_mpl_cmm, "decrypt_materials") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") +def test_GIVEN_valid_request_WHEN_call_decrypt_materials_THEN_return_MPLDecryptionMaterials( + mock_native_to_mpl_decrypt_materials, + mock_get_encryption_materials, +): + + # Mock: mpl_cmm.get_decryption_materials returns mock MPL decryption materials + mock_decrypt_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) + mock_decrypt_materials_output.decryption_materials = mock_mpl_decrypt_materials + mock_get_encryption_materials.return_value = mock_decrypt_materials_output + + # Mock: CMMHandler._create_mpl_decrypt_materials_input_from_request creates a DecryptMaterialsInput + mock_decrypt_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) + mock_native_to_mpl_decrypt_materials.return_value = mock_decrypt_materials_input + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + output = cmm_handler.decrypt_materials(mock_decryption_materials_request) + + # Verify cmm_handler returns MPLDecryptionMaterials + assert isinstance(output, MPLDecryptionMaterials) + # Verify returned MPLDecryptionMaterials uses the output of `decrypt_materials` + assert output.mpl_materials == mock_mpl_decrypt_materials + # Verify we actually called `decrypt_materials` + mock_mpl_cmm.decrypt_materials.assert_called_once_with(mock_decrypt_materials_input) + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") +def test_GIVEN_decrypt_materials_raises_MPL_Exception_WHEN_call_decrypt_materials_THEN_raise_ESDK_Exception( + _ +): + with pytest.raises(AWSEncryptionSDKClientError): + with patch.object(mock_mpl_cmm, "decrypt_materials", + side_effect=AwsCryptographicMaterialProvidersException("any")): + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + cmm_handler.decrypt_materials(mock_decryption_materials_request) + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_WHEN_call_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_AlgorithmSuiteIdESDK(): + some_native_algorithm_id = 0x0000 # Not a real algorithm ID, but fits the format + + mpl_output = MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id( + some_native_algorithm_id + ) + + assert isinstance(mpl_output, AlgorithmSuiteIdESDK) + assert mpl_output.value == "0x0000" + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") +def test__create_mpl_decrypt_materials_input_from_request( + mock_mpl_commitment_policy, + mock_mpl_algorithm_id, +): + mock_algorithm_id = "0x1234" # Some fake algorithm ID that fits the format + mock_mpl_algorithm_id.return_value = mock_algorithm_id + mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) + mock_mpl_commitment_policy.return_value = mock_commitment_policy + + # mock_decryption_materials_request.algorithm = + + output = MPLCMMHandler._create_mpl_decrypt_materials_input_from_request(mock_decryption_materials_request) + + assert isinstance(output, DecryptMaterialsInput) + assert output.algorithm_suite_id == mock_algorithm_id + assert output.commitment_policy == mock_commitment_policy + assert output.encryption_context == mock_decryption_materials_request.encryption_context + + assert len(output.encrypted_data_keys) == len(mock_decryption_materials_request.encrypted_data_keys) + for i in range(len(output.encrypted_data_keys)): + # Assume input[i] == output[i], seems to work + output_edk = output.encrypted_data_keys[i] + input_edk = mock_decryption_materials_request[i] + assert output_edk.key_provider_id == input_edk.key_provider.provider_id + assert output_edk.key_provider_info == input_edk.key_provider.key_info + assert output_edk.ciphertext == input_edk.encrypted_data_key diff --git a/test/unit/mpl/test_material_managers_mpl_materials.py b/test/unit/mpl/test_material_managers_mpl_materials.py new file mode 100644 index 000000000..250efeb7e --- /dev/null +++ b/test/unit/mpl/test_material_managers_mpl_materials.py @@ -0,0 +1,221 @@ +# 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" + +import pytest +from mock import MagicMock, patch, PropertyMock +from typing import Dict, List + +from aws_encryption_sdk.identifiers import CommitmentPolicy +import aws_encryption_sdk.materials_managers.mpl.materials +from aws_encryption_sdk.materials_managers.mpl.materials import ( + MPLEncryptionMaterials, + MPLDecryptionMaterials, +) +from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException + from aws_cryptographic_materialproviders.mpl.models import ( + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, + DecryptMaterialsInput, + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, + ) + from aws_cryptographic_materialproviders.mpl.references import ( + ICryptographicMaterialsManager + ) + HAS_MPL = True + + mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) + mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) + +except ImportError: + HAS_MPL = False + + # Ensure references to these mocks exist, even if they aren't used in a non-MPL context + mock_mpl_cmm = None + mock_mpl_encryption_materials = None + mock_mpl_decrypt_materials = None + +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.materials_managers import ( + EncryptionMaterialsRequest, + DecryptionMaterialsRequest, +) + + +mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) +mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) +mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): + """If the MPL IS NOT installed in the runtime environment, + assert the cmm has _HAS_MPL set to False""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is False + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): + """If the MPL IS installed in the runtime environment, + assert the cmm has _HAS_MPL set to True""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is True + + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): + with pytest.raises(ImportError): + MPLEncryptionMaterials(mpl_materials="doesn't matter") + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + + assert mpl_encryption_materials.mpl_materials == mock_mpl_encryption_materials + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): + with pytest.raises(ValueError): + MPLEncryptionMaterials(mpl_materials="not a valid mpl_materials") + +def test_mpl_to_native(): + some_mpl_algorithm_id = "0x1234" # Not a real algorithm ID, but fits the format + + native_output = aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id( + some_mpl_algorithm_id + ) + + assert native_output == 0x1234 + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch("aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.materials.AlgorithmSuite.get_by_id") +def test_GIVEN_valid_mpl_algorithm_id_WHEN_get_algorithm_THEN_valid_native_algorithm_id( + mock_algorithm, + mock_native_algorithm_id, +): + # Mock valid conversion from MPL to native algorithm ID + mock_native_algorithm_id.return_value = 0x1234 + + # Mock valid lookup in native AlgorithmSuite lookup + mock_algorithm.return_value = MagicMock(__class__=AlgorithmSuite) + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.algorithm + assert output == mock_algorithm() # property calls automatically, we need to call the mock + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_valid_native_algorithm_id(): + mock_encryption_context = MagicMock(__class__=Dict[str, str]) + mock_mpl_encryption_materials.encryption_context = mock_encryption_context + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.encryption_context + + assert output == mock_encryption_context + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_valid_nativefadsf_algorithm_id(): + mock_edk = MagicMock(__class__=MPL_EncryptedDataKey) + mock_mpl_key_provider_id = MagicMock(__class__=str) + mock_edk.key_provider_id = mock_mpl_key_provider_id + mock_mpl_key_provider_info = MagicMock(__class__=bytes) + mock_edk.key_provider_info = mock_mpl_key_provider_info + mock_mpl_ciphertext = MagicMock(__class__=bytes) + mock_edk.ciphertext = mock_mpl_ciphertext + + mock_edks = [ mock_edk ] + mock_mpl_encryption_materials.encrypted_data_keys = mock_edks + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.encrypted_data_keys + output_as_list = list(output) + + assert len(output_as_list) == len(mock_edks) + for i in range(len(output_as_list)): + # assume output[i] corresponds to input[i] + native_edk = output_as_list[i] + mpl_edk = mock_edks[i] + + assert native_edk.encrypted_data_key == mpl_edk.ciphertext + assert native_edk.key_provider.provider_id == mpl_edk.key_provider_id + assert native_edk.key_provider.key_info == mpl_edk.key_provider_info + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_valid_nativefadsffadsfa_algorithm_id(): + mock_data_key = MagicMock(__class__=bytes) + mock_mpl_encryption_materials.plaintext_data_key = mock_data_key + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.data_encryption_key + + assert output.key_provider.provider_id == "" + assert output.key_provider.key_info == b"" + assert output.data_key == mock_data_key + assert output.encrypted_data_key == b"" + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_valid_nativefasdfasdffadsf_algorithm_id(): + mock_signing_key = MagicMock(__class__=bytes) + mock_mpl_encryption_materials.signing_key = mock_signing_key + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.signing_key + + assert output == mock_signing_key + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_valid_nativeffasdfasdadsffadsfa_algorithm_id(): + mock_data_key = MagicMock(__class__=bytes) + mock_mpl_decrypt_materials.plaintext_data_key = mock_data_key + + mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.data_key + + assert output.key_provider.provider_id == "" + assert output.key_provider.key_info == b"" + assert output.data_key == mock_data_key + assert output.encrypted_data_key == b"" + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GecTHEN_validadsfasdf_nativefasdfasdffadsf_algorithm_id(): + mock_verification_key = MagicMock(__class__=bytes) + mock_mpl_decrypt_materials.verification_key = mock_verification_key + + mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.verification_key + + assert output == mock_verification_key diff --git a/tox.ini b/tox.ini index 72e8ec9fa..346e4fae0 100644 --- a/tox.ini +++ b/tox.ini @@ -84,23 +84,21 @@ deps = commands = local: {[testenv:base-command]commands} test/ -m local --ignore test/unit/mpl/ # MPL unit tests require the MPL to be installed - mpllocal: {[testenv:base-command]commands} test/unit/mpl/ -m local - integ: {[testenv:base-command]commands} test/ -m integ --ignore test/unit/mpl/ - # No MPL-specific integ tests - accept: {[testenv:base-command]commands} test/ -m accept --ignore test/unit/mpl/ - # No MPL-specific accept tests + mpllocal: {[testenv:base-command]commands} test/ -m local + integ: {[testenv:base-command]commands} test/ -m integ + accept: {[testenv:base-command]commands} test/ -m accept examples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/keyrings/ # MPL keyring examples require a special IAM role; run these separately under a separate set of permissions mplexamples: {[testenv:base-command]commands} examples/test/keyrings -m examples all: {[testenv:base-command]commands} test/ examples/test/ --ignore test/unit/mpl/ --ignore examples/test/keyrings/ - mplall: {[testenv:base-command]commands} test/unit/mpl/ examples/test/keyrings/ + mplall: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} # Run code coverage on the unit tests [testenv:coverage] commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local --ignore test/unit/mpl/ [testenv:mplcoverage-mpl] -commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/unit/mpl/ -m local +commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local # Verify that local tests work without environment variables present [testenv:nocmk] From 5ec46687b47ab907cc4a53ca9cc18a4f677c65e6 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 10:33:22 -0800 Subject: [PATCH 108/172] refactor tests --- .../materials_managers/mpl/cmm.py | 32 +- .../materials_managers/mpl/materials.py | 22 +- test/unit/mpl/__init__.py | 12 - .../mpl/test_material_managers_mpl_cmm.py | 278 ------------------ .../test_material_managers_mpl_materials.py | 221 -------------- test/unit/test_material_managers_mpl_cmm.py | 278 ------------------ .../test_material_managers_mpl_materials.py | 221 -------------- tox.ini | 6 +- 8 files changed, 20 insertions(+), 1050 deletions(-) delete mode 100644 test/unit/mpl/__init__.py delete mode 100644 test/unit/mpl/test_material_managers_mpl_cmm.py delete mode 100644 test/unit/mpl/test_material_managers_mpl_materials.py delete mode 100644 test/unit/test_material_managers_mpl_cmm.py delete mode 100644 test/unit/test_material_managers_mpl_materials.py diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index cd789b994..1e3e3fb34 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -1,23 +1,16 @@ """Retrieves encryption/decryption materials from the MPL.""" -# These dependencies are only loaded if you install the MPL. -try: - # pylint seems to struggle with this conditional import - # pylint: disable=unused-import - from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException - from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, - DecryptMaterialsOutput, - EncryptedDataKey as MPL_EncryptedDataKey, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, - ) - from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager - _HAS_MPL = True -except ImportError: - _HAS_MPL = False +from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException +from aws_cryptographic_materialproviders.mpl.models import ( + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, + DecryptMaterialsInput, + DecryptMaterialsOutput, + EncryptedDataKey as MPL_EncryptedDataKey, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, +) +from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager from typing import List @@ -46,9 +39,6 @@ def __init__( Create MPLCMMHandler. :param mpl_cmm: Underlying MPL cryptographic materials manager """ - if not _HAS_MPL: - raise ImportError("You MUST install the aws-cryptographic-material-providers " - f"library to create an instance of {MPLCMMHandler}") if isinstance(mpl_cmm, ICryptographicMaterialsManager): self.mpl_cmm = mpl_cmm else: diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index bd4b5f729..1ea2a199d 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -1,14 +1,10 @@ """Provides encryption/decryption materials from an underlying materials provider.""" -# These dependencies are only loaded if you install the MPL. -try: - from aws_cryptographic_materialproviders.mpl.models import ( - DecryptionMaterials as MPL_DecryptionMaterials, - EncryptedDataKey as MPL_EncryptedDataKey, - EncryptionMaterials as MPL_EncryptionMaterials, - ) - _HAS_MPL = True -except ImportError: - _HAS_MPL = False + +from aws_cryptographic_materialproviders.mpl.models import ( + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, +) from typing import Dict, List, Set @@ -42,9 +38,6 @@ def __init__( Create MPLEncryptionMaterials. :param materials: Underlying encryption materials """ - if not _HAS_MPL: - raise ImportError("You MUST install the aws-cryptographic-material-providers " - f"library to create an instance of {MPLEncryptionMaterials}") if isinstance(mpl_materials, MPL_EncryptionMaterials): self.mpl_materials = mpl_materials else: @@ -115,9 +108,6 @@ def __init__( Create MPLDecryptionMaterials. :param materials: Underlying decryption materials """ - if not _HAS_MPL: - raise ImportError("You MUST install the aws-cryptographic-material-providers " - f"library to create an instance of {MPLDecryptionMaterials}") if isinstance(mpl_materials, MPL_DecryptionMaterials): self.mpl_materials = mpl_materials else: diff --git a/test/unit/mpl/__init__.py b/test/unit/mpl/__init__.py deleted file mode 100644 index 53a960891..000000000 --- a/test/unit/mpl/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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. diff --git a/test/unit/mpl/test_material_managers_mpl_cmm.py b/test/unit/mpl/test_material_managers_mpl_cmm.py deleted file mode 100644 index 77bf5502d..000000000 --- a/test/unit/mpl/test_material_managers_mpl_cmm.py +++ /dev/null @@ -1,278 +0,0 @@ -# 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" - -import pytest -from mock import MagicMock, patch - - -from aws_encryption_sdk.identifiers import CommitmentPolicy -import aws_encryption_sdk.materials_managers.mpl.cmm -from aws_encryption_sdk.materials_managers.mpl.cmm import MPLCMMHandler -from aws_encryption_sdk.materials_managers.mpl.materials import ( - MPLEncryptionMaterials, - MPLDecryptionMaterials, -) - -pytestmark = [pytest.mark.unit, pytest.mark.local] - - -# Check if MPL is installed, and skip tests based on its installation status -# Ideally, this logic would be based on mocking imports and testing logic, -# but doing that introduces errors that cause other tests to fail. -try: - from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException - from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, - DecryptionMaterials as MPL_DecryptionMaterials, - EncryptionMaterials as MPL_EncryptionMaterials, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, - ) - from aws_cryptographic_materialproviders.mpl.references import ( - ICryptographicMaterialsManager - ) - HAS_MPL = True - - mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) - mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) - mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) - -except ImportError: - HAS_MPL = False - - # Ensure references to these mocks exist, even if they aren't used in a non-MPL context - mock_mpl_cmm = None - mock_mpl_encryption_materials = None - mock_mpl_decrypt_materials = None - -from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError -from aws_encryption_sdk.materials_managers import ( - EncryptionMaterialsRequest, - DecryptionMaterialsRequest, -) - - -mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) -mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) -mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) - -@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") -def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): - """If the MPL IS NOT installed in the runtime environment, - assert the cmm has _HAS_MPL set to False""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is False - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): - """If the MPL IS installed in the runtime environment, - assert the cmm has _HAS_MPL set to True""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is True - - -@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): - with pytest.raises(ImportError): - MPLCMMHandler(mpl_cmm="doesn't matter") - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): - mpl_cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - - assert mpl_cmm_handler.mpl_cmm == mock_mpl_cmm - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): - with pytest.raises(ValueError): - MPLCMMHandler(mpl_cmm="not a valid mpl_cmm") - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch.object(mock_mpl_cmm, "get_encryption_materials") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_get_encryption_materials") -def test_GIVEN_valid_request_WHEN_call_get_encryption_materials_THEN_return_MPLEncryptionMaterials( - mock_native_to_mpl_get_encryption_materials, - mock_get_encryption_materials, -): - - # Mock: mpl_cmm.get_encryption_materials returns mock MPL encryption materials - mock_get_encryption_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) - mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials - mock_get_encryption_materials.return_value = mock_get_encryption_materials_output - - # Mock: CMMHandler._native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput - mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) - mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - test = cmm_handler.get_encryption_materials(mock_encryption_materials_request) - - # Verify cmm_handler returns MPLEncryptionMaterials - assert isinstance(test, MPLEncryptionMaterials) - # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` - assert test.mpl_materials == mock_mpl_encryption_materials - # Verify we actually called `get_encryption_materials` - mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") -def test_GIVEN_get_encryption_materials_raises_MPL_Exception_WHEN_call_get_encryption_materials_THEN_raise_ESDK_Exception( - _ -): - with pytest.raises(AWSEncryptionSDKClientError): - with patch.object(mock_mpl_cmm, "get_encryption_materials", - side_effect=AwsCryptographicMaterialProvidersException("any")): - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - cmm_handler.get_encryption_materials(mock_encryption_materials_request) - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") -def test_GIVEN_native_to_mpl_commmitment_policy_returns_valid_policy_WHEN_call_native_to_mpl_get_encryption_materials_THEN_returns_GetEncryptionMaterialsInput( - mock_mpl_commitment_policy -): - mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) - mock_mpl_commitment_policy.return_value = mock_commitment_policy - - output = MPLCMMHandler._native_to_mpl_get_encryption_materials(mock_encryption_materials_request) - - # verify correctness of returned value - assert isinstance(output, GetEncryptionMaterialsInput) - assert output.encryption_context == mock_encryption_materials_request.encryption_context - assert output.commitment_policy == mock_commitment_policy - assert output.max_plaintext_length == mock_encryption_materials_request.plaintext_length - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): - native_commitment_policy = CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT - - output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) - - assert isinstance(output, CommitmentPolicyESDK) - assert output.value == "FORBID_ENCRYPT_ALLOW_DECRYPT" - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): - native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT - - output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) - - assert isinstance(output, CommitmentPolicyESDK) - assert output.value == "REQUIRE_ENCRYPT_ALLOW_DECRYPT" - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): - native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT - - output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) - - assert isinstance(output, CommitmentPolicyESDK) - assert output.value == "REQUIRE_ENCRYPT_REQUIRE_DECRYPT" - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_call_native_to_mpl_commmitment_policyTHEN_raise_ValueError(): - native_commitment_policy = "not a commitment policy" - - with pytest.raises(ValueError): - MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch.object(mock_mpl_cmm, "decrypt_materials") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") -def test_GIVEN_valid_request_WHEN_call_decrypt_materials_THEN_return_MPLDecryptionMaterials( - mock_native_to_mpl_decrypt_materials, - mock_get_encryption_materials, -): - - # Mock: mpl_cmm.get_decryption_materials returns mock MPL decryption materials - mock_decrypt_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) - mock_decrypt_materials_output.decryption_materials = mock_mpl_decrypt_materials - mock_get_encryption_materials.return_value = mock_decrypt_materials_output - - # Mock: CMMHandler._create_mpl_decrypt_materials_input_from_request creates a DecryptMaterialsInput - mock_decrypt_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) - mock_native_to_mpl_decrypt_materials.return_value = mock_decrypt_materials_input - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - output = cmm_handler.decrypt_materials(mock_decryption_materials_request) - - # Verify cmm_handler returns MPLDecryptionMaterials - assert isinstance(output, MPLDecryptionMaterials) - # Verify returned MPLDecryptionMaterials uses the output of `decrypt_materials` - assert output.mpl_materials == mock_mpl_decrypt_materials - # Verify we actually called `decrypt_materials` - mock_mpl_cmm.decrypt_materials.assert_called_once_with(mock_decrypt_materials_input) - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") -def test_GIVEN_decrypt_materials_raises_MPL_Exception_WHEN_call_decrypt_materials_THEN_raise_ESDK_Exception( - _ -): - with pytest.raises(AWSEncryptionSDKClientError): - with patch.object(mock_mpl_cmm, "decrypt_materials", - side_effect=AwsCryptographicMaterialProvidersException("any")): - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - cmm_handler.decrypt_materials(mock_decryption_materials_request) - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_WHEN_call_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_AlgorithmSuiteIdESDK(): - some_native_algorithm_id = 0x0000 # Not a real algorithm ID, but fits the format - - mpl_output = MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id( - some_native_algorithm_id - ) - - assert isinstance(mpl_output, AlgorithmSuiteIdESDK) - assert mpl_output.value == "0x0000" - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") -def test__create_mpl_decrypt_materials_input_from_request( - mock_mpl_commitment_policy, - mock_mpl_algorithm_id, -): - mock_algorithm_id = "0x1234" # Some fake algorithm ID that fits the format - mock_mpl_algorithm_id.return_value = mock_algorithm_id - mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) - mock_mpl_commitment_policy.return_value = mock_commitment_policy - - # mock_decryption_materials_request.algorithm = - - output = MPLCMMHandler._create_mpl_decrypt_materials_input_from_request(mock_decryption_materials_request) - - assert isinstance(output, DecryptMaterialsInput) - assert output.algorithm_suite_id == mock_algorithm_id - assert output.commitment_policy == mock_commitment_policy - assert output.encryption_context == mock_decryption_materials_request.encryption_context - - assert len(output.encrypted_data_keys) == len(mock_decryption_materials_request.encrypted_data_keys) - for i in range(len(output.encrypted_data_keys)): - # Assume input[i] == output[i], seems to work - output_edk = output.encrypted_data_keys[i] - input_edk = mock_decryption_materials_request[i] - assert output_edk.key_provider_id == input_edk.key_provider.provider_id - assert output_edk.key_provider_info == input_edk.key_provider.key_info - assert output_edk.ciphertext == input_edk.encrypted_data_key diff --git a/test/unit/mpl/test_material_managers_mpl_materials.py b/test/unit/mpl/test_material_managers_mpl_materials.py deleted file mode 100644 index 250efeb7e..000000000 --- a/test/unit/mpl/test_material_managers_mpl_materials.py +++ /dev/null @@ -1,221 +0,0 @@ -# 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" - -import pytest -from mock import MagicMock, patch, PropertyMock -from typing import Dict, List - -from aws_encryption_sdk.identifiers import CommitmentPolicy -import aws_encryption_sdk.materials_managers.mpl.materials -from aws_encryption_sdk.materials_managers.mpl.materials import ( - MPLEncryptionMaterials, - MPLDecryptionMaterials, -) -from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite - -pytestmark = [pytest.mark.unit, pytest.mark.local] - - -# Check if MPL is installed, and skip tests based on its installation status -# Ideally, this logic would be based on mocking imports and testing logic, -# but doing that introduces errors that cause other tests to fail. -try: - from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException - from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, - DecryptionMaterials as MPL_DecryptionMaterials, - EncryptedDataKey as MPL_EncryptedDataKey, - EncryptionMaterials as MPL_EncryptionMaterials, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, - ) - from aws_cryptographic_materialproviders.mpl.references import ( - ICryptographicMaterialsManager - ) - HAS_MPL = True - - mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) - mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) - -except ImportError: - HAS_MPL = False - - # Ensure references to these mocks exist, even if they aren't used in a non-MPL context - mock_mpl_cmm = None - mock_mpl_encryption_materials = None - mock_mpl_decrypt_materials = None - -from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError -from aws_encryption_sdk.materials_managers import ( - EncryptionMaterialsRequest, - DecryptionMaterialsRequest, -) - - -mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) -mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) -mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) - -@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") -def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): - """If the MPL IS NOT installed in the runtime environment, - assert the cmm has _HAS_MPL set to False""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is False - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): - """If the MPL IS installed in the runtime environment, - assert the cmm has _HAS_MPL set to True""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is True - - -@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): - with pytest.raises(ImportError): - MPLEncryptionMaterials(mpl_materials="doesn't matter") - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - - assert mpl_encryption_materials.mpl_materials == mock_mpl_encryption_materials - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): - with pytest.raises(ValueError): - MPLEncryptionMaterials(mpl_materials="not a valid mpl_materials") - -def test_mpl_to_native(): - some_mpl_algorithm_id = "0x1234" # Not a real algorithm ID, but fits the format - - native_output = aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id( - some_mpl_algorithm_id - ) - - assert native_output == 0x1234 - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch("aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id") -@patch("aws_encryption_sdk.materials_managers.mpl.materials.AlgorithmSuite.get_by_id") -def test_GIVEN_valid_mpl_algorithm_id_WHEN_get_algorithm_THEN_valid_native_algorithm_id( - mock_algorithm, - mock_native_algorithm_id, -): - # Mock valid conversion from MPL to native algorithm ID - mock_native_algorithm_id.return_value = 0x1234 - - # Mock valid lookup in native AlgorithmSuite lookup - mock_algorithm.return_value = MagicMock(__class__=AlgorithmSuite) - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.algorithm - assert output == mock_algorithm() # property calls automatically, we need to call the mock - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_valid_native_algorithm_id(): - mock_encryption_context = MagicMock(__class__=Dict[str, str]) - mock_mpl_encryption_materials.encryption_context = mock_encryption_context - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.encryption_context - - assert output == mock_encryption_context - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_valid_nativefadsf_algorithm_id(): - mock_edk = MagicMock(__class__=MPL_EncryptedDataKey) - mock_mpl_key_provider_id = MagicMock(__class__=str) - mock_edk.key_provider_id = mock_mpl_key_provider_id - mock_mpl_key_provider_info = MagicMock(__class__=bytes) - mock_edk.key_provider_info = mock_mpl_key_provider_info - mock_mpl_ciphertext = MagicMock(__class__=bytes) - mock_edk.ciphertext = mock_mpl_ciphertext - - mock_edks = [ mock_edk ] - mock_mpl_encryption_materials.encrypted_data_keys = mock_edks - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.encrypted_data_keys - output_as_list = list(output) - - assert len(output_as_list) == len(mock_edks) - for i in range(len(output_as_list)): - # assume output[i] corresponds to input[i] - native_edk = output_as_list[i] - mpl_edk = mock_edks[i] - - assert native_edk.encrypted_data_key == mpl_edk.ciphertext - assert native_edk.key_provider.provider_id == mpl_edk.key_provider_id - assert native_edk.key_provider.key_info == mpl_edk.key_provider_info - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_valid_nativefadsffadsfa_algorithm_id(): - mock_data_key = MagicMock(__class__=bytes) - mock_mpl_encryption_materials.plaintext_data_key = mock_data_key - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.data_encryption_key - - assert output.key_provider.provider_id == "" - assert output.key_provider.key_info == b"" - assert output.data_key == mock_data_key - assert output.encrypted_data_key == b"" - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_valid_nativefasdfasdffadsf_algorithm_id(): - mock_signing_key = MagicMock(__class__=bytes) - mock_mpl_encryption_materials.signing_key = mock_signing_key - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.signing_key - - assert output == mock_signing_key - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_valid_nativeffasdfasdadsffadsfa_algorithm_id(): - mock_data_key = MagicMock(__class__=bytes) - mock_mpl_decrypt_materials.plaintext_data_key = mock_data_key - - mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) - output = mpl_decryption_materials.data_key - - assert output.key_provider.provider_id == "" - assert output.key_provider.key_info == b"" - assert output.data_key == mock_data_key - assert output.encrypted_data_key == b"" - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_validadsfasdf_nativefasdfasdffadsf_algorithm_id(): - mock_verification_key = MagicMock(__class__=bytes) - mock_mpl_decrypt_materials.verification_key = mock_verification_key - - mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) - output = mpl_decryption_materials.verification_key - - assert output == mock_verification_key diff --git a/test/unit/test_material_managers_mpl_cmm.py b/test/unit/test_material_managers_mpl_cmm.py deleted file mode 100644 index 77bf5502d..000000000 --- a/test/unit/test_material_managers_mpl_cmm.py +++ /dev/null @@ -1,278 +0,0 @@ -# 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" - -import pytest -from mock import MagicMock, patch - - -from aws_encryption_sdk.identifiers import CommitmentPolicy -import aws_encryption_sdk.materials_managers.mpl.cmm -from aws_encryption_sdk.materials_managers.mpl.cmm import MPLCMMHandler -from aws_encryption_sdk.materials_managers.mpl.materials import ( - MPLEncryptionMaterials, - MPLDecryptionMaterials, -) - -pytestmark = [pytest.mark.unit, pytest.mark.local] - - -# Check if MPL is installed, and skip tests based on its installation status -# Ideally, this logic would be based on mocking imports and testing logic, -# but doing that introduces errors that cause other tests to fail. -try: - from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException - from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, - DecryptionMaterials as MPL_DecryptionMaterials, - EncryptionMaterials as MPL_EncryptionMaterials, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, - ) - from aws_cryptographic_materialproviders.mpl.references import ( - ICryptographicMaterialsManager - ) - HAS_MPL = True - - mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) - mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) - mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) - -except ImportError: - HAS_MPL = False - - # Ensure references to these mocks exist, even if they aren't used in a non-MPL context - mock_mpl_cmm = None - mock_mpl_encryption_materials = None - mock_mpl_decrypt_materials = None - -from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError -from aws_encryption_sdk.materials_managers import ( - EncryptionMaterialsRequest, - DecryptionMaterialsRequest, -) - - -mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) -mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) -mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) - -@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") -def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): - """If the MPL IS NOT installed in the runtime environment, - assert the cmm has _HAS_MPL set to False""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is False - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): - """If the MPL IS installed in the runtime environment, - assert the cmm has _HAS_MPL set to True""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is True - - -@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): - with pytest.raises(ImportError): - MPLCMMHandler(mpl_cmm="doesn't matter") - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): - mpl_cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - - assert mpl_cmm_handler.mpl_cmm == mock_mpl_cmm - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): - with pytest.raises(ValueError): - MPLCMMHandler(mpl_cmm="not a valid mpl_cmm") - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch.object(mock_mpl_cmm, "get_encryption_materials") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_get_encryption_materials") -def test_GIVEN_valid_request_WHEN_call_get_encryption_materials_THEN_return_MPLEncryptionMaterials( - mock_native_to_mpl_get_encryption_materials, - mock_get_encryption_materials, -): - - # Mock: mpl_cmm.get_encryption_materials returns mock MPL encryption materials - mock_get_encryption_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) - mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials - mock_get_encryption_materials.return_value = mock_get_encryption_materials_output - - # Mock: CMMHandler._native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput - mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) - mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - test = cmm_handler.get_encryption_materials(mock_encryption_materials_request) - - # Verify cmm_handler returns MPLEncryptionMaterials - assert isinstance(test, MPLEncryptionMaterials) - # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` - assert test.mpl_materials == mock_mpl_encryption_materials - # Verify we actually called `get_encryption_materials` - mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") -def test_GIVEN_get_encryption_materials_raises_MPL_Exception_WHEN_call_get_encryption_materials_THEN_raise_ESDK_Exception( - _ -): - with pytest.raises(AWSEncryptionSDKClientError): - with patch.object(mock_mpl_cmm, "get_encryption_materials", - side_effect=AwsCryptographicMaterialProvidersException("any")): - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - cmm_handler.get_encryption_materials(mock_encryption_materials_request) - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") -def test_GIVEN_native_to_mpl_commmitment_policy_returns_valid_policy_WHEN_call_native_to_mpl_get_encryption_materials_THEN_returns_GetEncryptionMaterialsInput( - mock_mpl_commitment_policy -): - mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) - mock_mpl_commitment_policy.return_value = mock_commitment_policy - - output = MPLCMMHandler._native_to_mpl_get_encryption_materials(mock_encryption_materials_request) - - # verify correctness of returned value - assert isinstance(output, GetEncryptionMaterialsInput) - assert output.encryption_context == mock_encryption_materials_request.encryption_context - assert output.commitment_policy == mock_commitment_policy - assert output.max_plaintext_length == mock_encryption_materials_request.plaintext_length - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): - native_commitment_policy = CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT - - output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) - - assert isinstance(output, CommitmentPolicyESDK) - assert output.value == "FORBID_ENCRYPT_ALLOW_DECRYPT" - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): - native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT - - output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) - - assert isinstance(output, CommitmentPolicyESDK) - assert output.value == "REQUIRE_ENCRYPT_ALLOW_DECRYPT" - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): - native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT - - output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) - - assert isinstance(output, CommitmentPolicyESDK) - assert output.value == "REQUIRE_ENCRYPT_REQUIRE_DECRYPT" - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_call_native_to_mpl_commmitment_policyTHEN_raise_ValueError(): - native_commitment_policy = "not a commitment policy" - - with pytest.raises(ValueError): - MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch.object(mock_mpl_cmm, "decrypt_materials") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") -def test_GIVEN_valid_request_WHEN_call_decrypt_materials_THEN_return_MPLDecryptionMaterials( - mock_native_to_mpl_decrypt_materials, - mock_get_encryption_materials, -): - - # Mock: mpl_cmm.get_decryption_materials returns mock MPL decryption materials - mock_decrypt_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) - mock_decrypt_materials_output.decryption_materials = mock_mpl_decrypt_materials - mock_get_encryption_materials.return_value = mock_decrypt_materials_output - - # Mock: CMMHandler._create_mpl_decrypt_materials_input_from_request creates a DecryptMaterialsInput - mock_decrypt_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) - mock_native_to_mpl_decrypt_materials.return_value = mock_decrypt_materials_input - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - output = cmm_handler.decrypt_materials(mock_decryption_materials_request) - - # Verify cmm_handler returns MPLDecryptionMaterials - assert isinstance(output, MPLDecryptionMaterials) - # Verify returned MPLDecryptionMaterials uses the output of `decrypt_materials` - assert output.mpl_materials == mock_mpl_decrypt_materials - # Verify we actually called `decrypt_materials` - mock_mpl_cmm.decrypt_materials.assert_called_once_with(mock_decrypt_materials_input) - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") -def test_GIVEN_decrypt_materials_raises_MPL_Exception_WHEN_call_decrypt_materials_THEN_raise_ESDK_Exception( - _ -): - with pytest.raises(AWSEncryptionSDKClientError): - with patch.object(mock_mpl_cmm, "decrypt_materials", - side_effect=AwsCryptographicMaterialProvidersException("any")): - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - cmm_handler.decrypt_materials(mock_decryption_materials_request) - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_WHEN_call_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_AlgorithmSuiteIdESDK(): - some_native_algorithm_id = 0x0000 # Not a real algorithm ID, but fits the format - - mpl_output = MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id( - some_native_algorithm_id - ) - - assert isinstance(mpl_output, AlgorithmSuiteIdESDK) - assert mpl_output.value == "0x0000" - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") -def test__create_mpl_decrypt_materials_input_from_request( - mock_mpl_commitment_policy, - mock_mpl_algorithm_id, -): - mock_algorithm_id = "0x1234" # Some fake algorithm ID that fits the format - mock_mpl_algorithm_id.return_value = mock_algorithm_id - mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) - mock_mpl_commitment_policy.return_value = mock_commitment_policy - - # mock_decryption_materials_request.algorithm = - - output = MPLCMMHandler._create_mpl_decrypt_materials_input_from_request(mock_decryption_materials_request) - - assert isinstance(output, DecryptMaterialsInput) - assert output.algorithm_suite_id == mock_algorithm_id - assert output.commitment_policy == mock_commitment_policy - assert output.encryption_context == mock_decryption_materials_request.encryption_context - - assert len(output.encrypted_data_keys) == len(mock_decryption_materials_request.encrypted_data_keys) - for i in range(len(output.encrypted_data_keys)): - # Assume input[i] == output[i], seems to work - output_edk = output.encrypted_data_keys[i] - input_edk = mock_decryption_materials_request[i] - assert output_edk.key_provider_id == input_edk.key_provider.provider_id - assert output_edk.key_provider_info == input_edk.key_provider.key_info - assert output_edk.ciphertext == input_edk.encrypted_data_key diff --git a/test/unit/test_material_managers_mpl_materials.py b/test/unit/test_material_managers_mpl_materials.py deleted file mode 100644 index 250efeb7e..000000000 --- a/test/unit/test_material_managers_mpl_materials.py +++ /dev/null @@ -1,221 +0,0 @@ -# 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" - -import pytest -from mock import MagicMock, patch, PropertyMock -from typing import Dict, List - -from aws_encryption_sdk.identifiers import CommitmentPolicy -import aws_encryption_sdk.materials_managers.mpl.materials -from aws_encryption_sdk.materials_managers.mpl.materials import ( - MPLEncryptionMaterials, - MPLDecryptionMaterials, -) -from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite - -pytestmark = [pytest.mark.unit, pytest.mark.local] - - -# Check if MPL is installed, and skip tests based on its installation status -# Ideally, this logic would be based on mocking imports and testing logic, -# but doing that introduces errors that cause other tests to fail. -try: - from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException - from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, - DecryptionMaterials as MPL_DecryptionMaterials, - EncryptedDataKey as MPL_EncryptedDataKey, - EncryptionMaterials as MPL_EncryptionMaterials, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, - ) - from aws_cryptographic_materialproviders.mpl.references import ( - ICryptographicMaterialsManager - ) - HAS_MPL = True - - mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) - mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) - -except ImportError: - HAS_MPL = False - - # Ensure references to these mocks exist, even if they aren't used in a non-MPL context - mock_mpl_cmm = None - mock_mpl_encryption_materials = None - mock_mpl_decrypt_materials = None - -from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError -from aws_encryption_sdk.materials_managers import ( - EncryptionMaterialsRequest, - DecryptionMaterialsRequest, -) - - -mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) -mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) -mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) - -@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") -def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): - """If the MPL IS NOT installed in the runtime environment, - assert the cmm has _HAS_MPL set to False""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is False - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): - """If the MPL IS installed in the runtime environment, - assert the cmm has _HAS_MPL set to True""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is True - - -@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): - with pytest.raises(ImportError): - MPLEncryptionMaterials(mpl_materials="doesn't matter") - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - - assert mpl_encryption_materials.mpl_materials == mock_mpl_encryption_materials - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): - with pytest.raises(ValueError): - MPLEncryptionMaterials(mpl_materials="not a valid mpl_materials") - -def test_mpl_to_native(): - some_mpl_algorithm_id = "0x1234" # Not a real algorithm ID, but fits the format - - native_output = aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id( - some_mpl_algorithm_id - ) - - assert native_output == 0x1234 - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -@patch("aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id") -@patch("aws_encryption_sdk.materials_managers.mpl.materials.AlgorithmSuite.get_by_id") -def test_GIVEN_valid_mpl_algorithm_id_WHEN_get_algorithm_THEN_valid_native_algorithm_id( - mock_algorithm, - mock_native_algorithm_id, -): - # Mock valid conversion from MPL to native algorithm ID - mock_native_algorithm_id.return_value = 0x1234 - - # Mock valid lookup in native AlgorithmSuite lookup - mock_algorithm.return_value = MagicMock(__class__=AlgorithmSuite) - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.algorithm - assert output == mock_algorithm() # property calls automatically, we need to call the mock - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_valid_native_algorithm_id(): - mock_encryption_context = MagicMock(__class__=Dict[str, str]) - mock_mpl_encryption_materials.encryption_context = mock_encryption_context - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.encryption_context - - assert output == mock_encryption_context - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_valid_nativefadsf_algorithm_id(): - mock_edk = MagicMock(__class__=MPL_EncryptedDataKey) - mock_mpl_key_provider_id = MagicMock(__class__=str) - mock_edk.key_provider_id = mock_mpl_key_provider_id - mock_mpl_key_provider_info = MagicMock(__class__=bytes) - mock_edk.key_provider_info = mock_mpl_key_provider_info - mock_mpl_ciphertext = MagicMock(__class__=bytes) - mock_edk.ciphertext = mock_mpl_ciphertext - - mock_edks = [ mock_edk ] - mock_mpl_encryption_materials.encrypted_data_keys = mock_edks - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.encrypted_data_keys - output_as_list = list(output) - - assert len(output_as_list) == len(mock_edks) - for i in range(len(output_as_list)): - # assume output[i] corresponds to input[i] - native_edk = output_as_list[i] - mpl_edk = mock_edks[i] - - assert native_edk.encrypted_data_key == mpl_edk.ciphertext - assert native_edk.key_provider.provider_id == mpl_edk.key_provider_id - assert native_edk.key_provider.key_info == mpl_edk.key_provider_info - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_valid_nativefadsffadsfa_algorithm_id(): - mock_data_key = MagicMock(__class__=bytes) - mock_mpl_encryption_materials.plaintext_data_key = mock_data_key - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.data_encryption_key - - assert output.key_provider.provider_id == "" - assert output.key_provider.key_info == b"" - assert output.data_key == mock_data_key - assert output.encrypted_data_key == b"" - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_valid_nativefasdfasdffadsf_algorithm_id(): - mock_signing_key = MagicMock(__class__=bytes) - mock_mpl_encryption_materials.signing_key = mock_signing_key - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.signing_key - - assert output == mock_signing_key - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_valid_nativeffasdfasdadsffadsfa_algorithm_id(): - mock_data_key = MagicMock(__class__=bytes) - mock_mpl_decrypt_materials.plaintext_data_key = mock_data_key - - mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) - output = mpl_decryption_materials.data_key - - assert output.key_provider.provider_id == "" - assert output.key_provider.key_info == b"" - assert output.data_key == mock_data_key - assert output.encrypted_data_key == b"" - - -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") -def test_GecTHEN_validadsfasdf_nativefasdfasdffadsf_algorithm_id(): - mock_verification_key = MagicMock(__class__=bytes) - mock_mpl_decrypt_materials.verification_key = mock_verification_key - - mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) - output = mpl_decryption_materials.verification_key - - assert output == mock_verification_key diff --git a/tox.ini b/tox.ini index 346e4fae0..e48f9f3b5 100644 --- a/tox.ini +++ b/tox.ini @@ -82,7 +82,7 @@ deps = # Install the MPL requirements if the `-mpl` suffix is present mpl: -rrequirements_mpl.txt commands = - local: {[testenv:base-command]commands} test/ -m local --ignore test/unit/mpl/ + local: {[testenv:base-command]commands} test/ -m local --ignore test/mpl/ # MPL unit tests require the MPL to be installed mpllocal: {[testenv:base-command]commands} test/ -m local integ: {[testenv:base-command]commands} test/ -m integ @@ -90,13 +90,13 @@ commands = examples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/keyrings/ # MPL keyring examples require a special IAM role; run these separately under a separate set of permissions mplexamples: {[testenv:base-command]commands} examples/test/keyrings -m examples - all: {[testenv:base-command]commands} test/ examples/test/ --ignore test/unit/mpl/ --ignore examples/test/keyrings/ + all: {[testenv:base-command]commands} test/ examples/test/ --ignore test/mpl/ --ignore examples/test/keyrings/ mplall: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} # Run code coverage on the unit tests [testenv:coverage] -commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local --ignore test/unit/mpl/ +commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local --ignore test/mpl/ [testenv:mplcoverage-mpl] commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local From 61ba4dec6bdc404a13bb245fa5bbb2078d014edc Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 10:36:39 -0800 Subject: [PATCH 109/172] refactor tests --- src/aws_encryption_sdk/streaming_client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 61f2f88c6..01d4ca5ac 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -58,7 +58,6 @@ serialize_non_framed_close, serialize_non_framed_open, ) -from aws_encryption_sdk.materials_managers.mpl.cmm import MPLCMMHandler from aws_encryption_sdk.internal.utils import exactly_one_arg_is_not_none from aws_encryption_sdk.internal.utils.commitment import ( validate_commitment_policy_on_decrypt, @@ -79,6 +78,10 @@ from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput from aws_cryptographic_materialproviders.mpl.references import IKeyring _HAS_MPL = True + + # Import internal ESDK modules that depend on the MPL + from aws_encryption_sdk.materials_managers.mpl.cmm import MPLCMMHandler + except ImportError: _HAS_MPL = False From 95c5be6bfb7fea0ef31ebcc8e5ddec0ac07df9fd Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 10:37:17 -0800 Subject: [PATCH 110/172] refactor tests --- src/aws_encryption_sdk/streaming_client.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 01d4ca5ac..a3c05bbb7 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -554,7 +554,8 @@ def _prep_message(self): else: # MPL verification key is PEM bytes, not DER bytes. # If the underlying CMM is from the MPL, load PEM bytes. - if (isinstance(self.config.materials_manager, MPLCMMHandler)): + if (_HAS_MPL + and isinstance(self.config.materials_manager, MPLCMMHandler)): self.signer = Signer.from_key_bytes( algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key, encoding=serialization.Encoding.PEM, @@ -921,7 +922,8 @@ def _read_header(self): else: # MPL verification key is NOT key bytes; it is bytes of the compressed point. # If the underlying CMM is from the MPL, load bytes from encoded point. - if (isinstance(self.config.materials_manager, MPLCMMHandler)): + if (_HAS_MPL + and isinstance(self.config.materials_manager, MPLCMMHandler)): self.verifier = Verifier.from_encoded_point( algorithm=header.algorithm, encoded_point=base64.b64encode(decryption_materials.verification_key) From 9566873a946acb70622962bdbcc8b2086e88e16d Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 10:51:45 -0800 Subject: [PATCH 111/172] refactor tests --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index e48f9f3b5..9e3fe2b98 100644 --- a/tox.ini +++ b/tox.ini @@ -96,7 +96,7 @@ commands = # Run code coverage on the unit tests [testenv:coverage] -commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local --ignore test/mpl/ +commands = {[testenv:base-command]commands} --cov aws_encryption_sdk --cov-config=.coveragerc test/ -m local --ignore test/mpl/ [testenv:mplcoverage-mpl] commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local From 66420832da4a4a8806254f0b8487ee03452a17d1 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 11:16:25 -0800 Subject: [PATCH 112/172] fix cov --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 9e3fe2b98..70ed4281f 100644 --- a/tox.ini +++ b/tox.ini @@ -96,9 +96,9 @@ commands = # Run code coverage on the unit tests [testenv:coverage] -commands = {[testenv:base-command]commands} --cov aws_encryption_sdk --cov-config=.coveragerc test/ -m local --ignore test/mpl/ +commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local --ignore test/mpl/ [testenv:mplcoverage-mpl] -commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local +commands = {[testenv:base-command]commands} --cov --cov-config=.coveragercmpl aws_encryption_sdk test/ -m local # Verify that local tests work without environment variables present [testenv:nocmk] From 51d2804343797d98848f7eea2b8f333f9f7f8a46 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 11:22:26 -0800 Subject: [PATCH 113/172] fix cov --- .coveragerc | 7 + .coveragercmpl | 1 + .gitignore | 5 +- test/mpl/README.md | 1 + test/mpl/__init__.py | 13 + .../unit/test_material_managers_mpl_cmm.py | 250 ++++++++++++++++++ .../test_material_managers_mpl_materials.py | 197 ++++++++++++++ 7 files changed, 472 insertions(+), 2 deletions(-) create mode 100644 .coveragerc create mode 100644 .coveragercmpl create mode 100644 test/mpl/README.md create mode 100644 test/mpl/__init__.py create mode 100644 test/mpl/unit/test_material_managers_mpl_cmm.py create mode 100644 test/mpl/unit/test_material_managers_mpl_materials.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..8957349aa --- /dev/null +++ b/.coveragerc @@ -0,0 +1,7 @@ +# .coveragerc file when running coverage WITHOUT coverage for the MPL +# This prevents the ESDK without the MPL from considering the MPL-specific modules as "missed" coverage +[run] +omit = */aws_encryption_sdk/materials_managers/mpl/* + +[report] +omit = */aws_encryption_sdk/materials_managers/mpl/* \ No newline at end of file diff --git a/.coveragercmpl b/.coveragercmpl new file mode 100644 index 000000000..31a7b4407 --- /dev/null +++ b/.coveragercmpl @@ -0,0 +1 @@ +# .coveragerc file when running coverage WITH coverage for the MPL diff --git a/.gitignore b/.gitignore index 63097dcba..fc224adc4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,8 +19,9 @@ docs/build __pycache__ *.egg-info -# Coverage.py -.coverage* +# Coverage.py, NOT .coveragerc nor .coveragercmpl +.coverage +.coverage.py # MyPy .mypy_cache diff --git a/test/mpl/README.md b/test/mpl/README.md new file mode 100644 index 000000000..7ae7134d0 --- /dev/null +++ b/test/mpl/README.md @@ -0,0 +1 @@ +Tests in this directory REQUIRE the [aws-cryptographic-material-providers](https://github.com/aws/aws-cryptographic-material-providers-library) library to execute. \ No newline at end of file diff --git a/test/mpl/__init__.py b/test/mpl/__init__.py new file mode 100644 index 000000000..b976c1308 --- /dev/null +++ b/test/mpl/__init__.py @@ -0,0 +1,13 @@ +# 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. +"""Module containing tests that REQUIRE the aws-cryptographic-material-providers library to run.""" \ No newline at end of file diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py new file mode 100644 index 000000000..b1589b1cf --- /dev/null +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -0,0 +1,250 @@ +# 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" + +import pytest +from mock import MagicMock, patch + + +from aws_encryption_sdk.identifiers import CommitmentPolicy +import aws_encryption_sdk.materials_managers.mpl.cmm +from aws_encryption_sdk.materials_managers.mpl.cmm import MPLCMMHandler +from aws_encryption_sdk.materials_managers.mpl.materials import ( + MPLEncryptionMaterials, + MPLDecryptionMaterials, +) + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException +from aws_cryptographic_materialproviders.mpl.models import ( + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, + DecryptMaterialsInput, + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptionMaterials as MPL_EncryptionMaterials, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, +) +from aws_cryptographic_materialproviders.mpl.references import ( + ICryptographicMaterialsManager +) + +mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) +mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) +mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) + + +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.materials_managers import ( + EncryptionMaterialsRequest, + DecryptionMaterialsRequest, +) + + +mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) +mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) +mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) + +def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): + """If the MPL IS NOT installed in the runtime environment, + assert the cmm has _HAS_MPL set to False""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is False + + +def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): + """If the MPL IS installed in the runtime environment, + assert the cmm has _HAS_MPL set to True""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is True + + +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): + with pytest.raises(ImportError): + MPLCMMHandler(mpl_cmm="doesn't matter") + + +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): + mpl_cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + + assert mpl_cmm_handler.mpl_cmm == mock_mpl_cmm + + +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): + with pytest.raises(ValueError): + MPLCMMHandler(mpl_cmm="not a valid mpl_cmm") + + +@patch.object(mock_mpl_cmm, "get_encryption_materials") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_get_encryption_materials") +def test_GIVEN_valid_request_WHEN_call_get_encryption_materials_THEN_return_MPLEncryptionMaterials( + mock_native_to_mpl_get_encryption_materials, + mock_get_encryption_materials, +): + + # Mock: mpl_cmm.get_encryption_materials returns mock MPL encryption materials + mock_get_encryption_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) + mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials + mock_get_encryption_materials.return_value = mock_get_encryption_materials_output + + # Mock: CMMHandler._native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput + mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) + mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + test = cmm_handler.get_encryption_materials(mock_encryption_materials_request) + + # Verify cmm_handler returns MPLEncryptionMaterials + assert isinstance(test, MPLEncryptionMaterials) + # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` + assert test.mpl_materials == mock_mpl_encryption_materials + # Verify we actually called `get_encryption_materials` + mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) + + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") +def test_GIVEN_get_encryption_materials_raises_MPL_Exception_WHEN_call_get_encryption_materials_THEN_raise_ESDK_Exception( + _ +): + with pytest.raises(AWSEncryptionSDKClientError): + with patch.object(mock_mpl_cmm, "get_encryption_materials", + side_effect=AwsCryptographicMaterialProvidersException("any")): + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + cmm_handler.get_encryption_materials(mock_encryption_materials_request) + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") +def test_GIVEN_native_to_mpl_commmitment_policy_returns_valid_policy_WHEN_call_native_to_mpl_get_encryption_materials_THEN_returns_GetEncryptionMaterialsInput( + mock_mpl_commitment_policy +): + mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) + mock_mpl_commitment_policy.return_value = mock_commitment_policy + + output = MPLCMMHandler._native_to_mpl_get_encryption_materials(mock_encryption_materials_request) + + # verify correctness of returned value + assert isinstance(output, GetEncryptionMaterialsInput) + assert output.encryption_context == mock_encryption_materials_request.encryption_context + assert output.commitment_policy == mock_commitment_policy + assert output.max_plaintext_length == mock_encryption_materials_request.plaintext_length + + +def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): + native_commitment_policy = CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT + + output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + + assert isinstance(output, CommitmentPolicyESDK) + assert output.value == "FORBID_ENCRYPT_ALLOW_DECRYPT" + +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): + native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT + + output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + + assert isinstance(output, CommitmentPolicyESDK) + assert output.value == "REQUIRE_ENCRYPT_ALLOW_DECRYPT" + +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): + native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + + output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + + assert isinstance(output, CommitmentPolicyESDK) + assert output.value == "REQUIRE_ENCRYPT_REQUIRE_DECRYPT" + +def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_call_native_to_mpl_commmitment_policyTHEN_raise_ValueError(): + native_commitment_policy = "not a commitment policy" + + with pytest.raises(ValueError): + MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + +@patch.object(mock_mpl_cmm, "decrypt_materials") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") +def test_GIVEN_valid_request_WHEN_call_decrypt_materials_THEN_return_MPLDecryptionMaterials( + mock_native_to_mpl_decrypt_materials, + mock_get_encryption_materials, +): + + # Mock: mpl_cmm.get_decryption_materials returns mock MPL decryption materials + mock_decrypt_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) + mock_decrypt_materials_output.decryption_materials = mock_mpl_decrypt_materials + mock_get_encryption_materials.return_value = mock_decrypt_materials_output + + # Mock: CMMHandler._create_mpl_decrypt_materials_input_from_request creates a DecryptMaterialsInput + mock_decrypt_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) + mock_native_to_mpl_decrypt_materials.return_value = mock_decrypt_materials_input + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + output = cmm_handler.decrypt_materials(mock_decryption_materials_request) + + # Verify cmm_handler returns MPLDecryptionMaterials + assert isinstance(output, MPLDecryptionMaterials) + # Verify returned MPLDecryptionMaterials uses the output of `decrypt_materials` + assert output.mpl_materials == mock_mpl_decrypt_materials + # Verify we actually called `decrypt_materials` + mock_mpl_cmm.decrypt_materials.assert_called_once_with(mock_decrypt_materials_input) + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") +def test_GIVEN_decrypt_materials_raises_MPL_Exception_WHEN_call_decrypt_materials_THEN_raise_ESDK_Exception( + _ +): + with pytest.raises(AWSEncryptionSDKClientError): + with patch.object(mock_mpl_cmm, "decrypt_materials", + side_effect=AwsCryptographicMaterialProvidersException("any")): + + cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) + cmm_handler.decrypt_materials(mock_decryption_materials_request) + +def test_WHEN_call_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_AlgorithmSuiteIdESDK(): + some_native_algorithm_id = 0x0000 # Not a real algorithm ID, but fits the format + + mpl_output = MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id( + some_native_algorithm_id + ) + + assert isinstance(mpl_output, AlgorithmSuiteIdESDK) + assert mpl_output.value == "0x0000" + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") +def test__create_mpl_decrypt_materials_input_from_request( + mock_mpl_commitment_policy, + mock_mpl_algorithm_id, +): + mock_algorithm_id = "0x1234" # Some fake algorithm ID that fits the format + mock_mpl_algorithm_id.return_value = mock_algorithm_id + mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) + mock_mpl_commitment_policy.return_value = mock_commitment_policy + + # mock_decryption_materials_request.algorithm = + + output = MPLCMMHandler._create_mpl_decrypt_materials_input_from_request(mock_decryption_materials_request) + + assert isinstance(output, DecryptMaterialsInput) + assert output.algorithm_suite_id == mock_algorithm_id + assert output.commitment_policy == mock_commitment_policy + assert output.encryption_context == mock_decryption_materials_request.encryption_context + + assert len(output.encrypted_data_keys) == len(mock_decryption_materials_request.encrypted_data_keys) + for i in range(len(output.encrypted_data_keys)): + # Assume input[i] == output[i], seems to work + output_edk = output.encrypted_data_keys[i] + input_edk = mock_decryption_materials_request[i] + assert output_edk.key_provider_id == input_edk.key_provider.provider_id + assert output_edk.key_provider_info == input_edk.key_provider.key_info + assert output_edk.ciphertext == input_edk.encrypted_data_key diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py new file mode 100644 index 000000000..dfd6b2769 --- /dev/null +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -0,0 +1,197 @@ +# 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" + +import pytest +from mock import MagicMock, patch, PropertyMock +from typing import Dict, List + +from aws_encryption_sdk.identifiers import CommitmentPolicy +import aws_encryption_sdk.materials_managers.mpl.materials +from aws_encryption_sdk.materials_managers.mpl.materials import ( + MPLEncryptionMaterials, + MPLDecryptionMaterials, +) +from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException +from aws_cryptographic_materialproviders.mpl.models import ( + AlgorithmSuiteIdESDK, + CommitmentPolicyESDK, + DecryptMaterialsInput, + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, + GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput, +) +from aws_cryptographic_materialproviders.mpl.references import ( + ICryptographicMaterialsManager +) + +mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) +mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) + + +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.materials_managers import ( + EncryptionMaterialsRequest, + DecryptionMaterialsRequest, +) + + +mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) +mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) +mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) + +def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): + """If the MPL IS NOT installed in the runtime environment, + assert the cmm has _HAS_MPL set to False""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is False + + +def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): + """If the MPL IS installed in the runtime environment, + assert the cmm has _HAS_MPL set to True""" + + assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") + assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is True + + +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): + with pytest.raises(ImportError): + MPLEncryptionMaterials(mpl_materials="doesn't matter") + + +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + + assert mpl_encryption_materials.mpl_materials == mock_mpl_encryption_materials + + +def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): + with pytest.raises(ValueError): + MPLEncryptionMaterials(mpl_materials="not a valid mpl_materials") + +def test_mpl_to_native(): + some_mpl_algorithm_id = "0x1234" # Not a real algorithm ID, but fits the format + + native_output = aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id( + some_mpl_algorithm_id + ) + + assert native_output == 0x1234 + + +@patch("aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.materials.AlgorithmSuite.get_by_id") +def test_GIVEN_valid_mpl_algorithm_id_WHEN_get_algorithm_THEN_valid_native_algorithm_id( + mock_algorithm, + mock_native_algorithm_id, +): + # Mock valid conversion from MPL to native algorithm ID + mock_native_algorithm_id.return_value = 0x1234 + + # Mock valid lookup in native AlgorithmSuite lookup + mock_algorithm.return_value = MagicMock(__class__=AlgorithmSuite) + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.algorithm + assert output == mock_algorithm() # property calls automatically, we need to call the mock + + +def test_GecTHEN_valid_native_algorithm_id(): + mock_encryption_context = MagicMock(__class__=Dict[str, str]) + mock_mpl_encryption_materials.encryption_context = mock_encryption_context + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.encryption_context + + assert output == mock_encryption_context + + +def test_GecTHEN_valid_nativefadsf_algorithm_id(): + mock_edk = MagicMock(__class__=MPL_EncryptedDataKey) + mock_mpl_key_provider_id = MagicMock(__class__=str) + mock_edk.key_provider_id = mock_mpl_key_provider_id + mock_mpl_key_provider_info = MagicMock(__class__=bytes) + mock_edk.key_provider_info = mock_mpl_key_provider_info + mock_mpl_ciphertext = MagicMock(__class__=bytes) + mock_edk.ciphertext = mock_mpl_ciphertext + + mock_edks = [ mock_edk ] + mock_mpl_encryption_materials.encrypted_data_keys = mock_edks + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.encrypted_data_keys + output_as_list = list(output) + + assert len(output_as_list) == len(mock_edks) + for i in range(len(output_as_list)): + # assume output[i] corresponds to input[i] + native_edk = output_as_list[i] + mpl_edk = mock_edks[i] + + assert native_edk.encrypted_data_key == mpl_edk.ciphertext + assert native_edk.key_provider.provider_id == mpl_edk.key_provider_id + assert native_edk.key_provider.key_info == mpl_edk.key_provider_info + +def test_GecTHEN_valid_nativefadsffadsfa_algorithm_id(): + mock_data_key = MagicMock(__class__=bytes) + mock_mpl_encryption_materials.plaintext_data_key = mock_data_key + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.data_encryption_key + + assert output.key_provider.provider_id == "" + assert output.key_provider.key_info == b"" + assert output.data_key == mock_data_key + assert output.encrypted_data_key == b"" + + +def test_GecTHEN_valid_nativefasdfasdffadsf_algorithm_id(): + mock_signing_key = MagicMock(__class__=bytes) + mock_mpl_encryption_materials.signing_key = mock_signing_key + + mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.signing_key + + assert output == mock_signing_key + + +def test_GecTHEN_valid_nativeffasdfasdadsffadsfa_algorithm_id(): + mock_data_key = MagicMock(__class__=bytes) + mock_mpl_decrypt_materials.plaintext_data_key = mock_data_key + + mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.data_key + + assert output.key_provider.provider_id == "" + assert output.key_provider.key_info == b"" + assert output.data_key == mock_data_key + assert output.encrypted_data_key == b"" + + +def test_GecTHEN_validadsfasdf_nativefasdfasdffadsf_algorithm_id(): + mock_verification_key = MagicMock(__class__=bytes) + mock_mpl_decrypt_materials.verification_key = mock_verification_key + + mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.verification_key + + assert output == mock_verification_key From 51e5db501c41d5c8e2351daa7b4331a21132f2b7 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 11:26:15 -0800 Subject: [PATCH 114/172] fix cov --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 70ed4281f..1cc78c45e 100644 --- a/tox.ini +++ b/tox.ini @@ -98,7 +98,7 @@ commands = [testenv:coverage] commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local --ignore test/mpl/ [testenv:mplcoverage-mpl] -commands = {[testenv:base-command]commands} --cov --cov-config=.coveragercmpl aws_encryption_sdk test/ -m local +commands = {[testenv:base-command]commands} --cov-config=.coveragercmpl --cov aws_encryption_sdk test/ -m local # Verify that local tests work without environment variables present [testenv:nocmk] From e2354613a6d9c14c8cd4116a399e27010010d6f4 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 11:29:58 -0800 Subject: [PATCH 115/172] fix cov --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 1cc78c45e..419188b54 100644 --- a/tox.ini +++ b/tox.ini @@ -85,8 +85,8 @@ commands = local: {[testenv:base-command]commands} test/ -m local --ignore test/mpl/ # MPL unit tests require the MPL to be installed mpllocal: {[testenv:base-command]commands} test/ -m local - integ: {[testenv:base-command]commands} test/ -m integ - accept: {[testenv:base-command]commands} test/ -m accept + integ: {[testenv:base-command]commands} test/ -m integ --ignore test/mpl/ + accept: {[testenv:base-command]commands} test/ -m accept --ignore test/mpl/ examples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/keyrings/ # MPL keyring examples require a special IAM role; run these separately under a separate set of permissions mplexamples: {[testenv:base-command]commands} examples/test/keyrings -m examples From e7c745fbdb8e33d342a9027196519c53833fcba8 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 11:34:58 -0800 Subject: [PATCH 116/172] fix tests --- .../unit/test_material_managers_mpl_cmm.py | 20 ------------------- .../test_material_managers_mpl_materials.py | 20 ------------------- tox.ini | 8 +++++--- 3 files changed, 5 insertions(+), 43 deletions(-) diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index b1589b1cf..cae334722 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -57,26 +57,6 @@ mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) -def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): - """If the MPL IS NOT installed in the runtime environment, - assert the cmm has _HAS_MPL set to False""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is False - - -def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): - """If the MPL IS installed in the runtime environment, - assert the cmm has _HAS_MPL set to True""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.cmm, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.cmm._HAS_MPL is True - - -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): - with pytest.raises(ImportError): - MPLCMMHandler(mpl_cmm="doesn't matter") - def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): mpl_cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index dfd6b2769..bb83b89fd 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -57,26 +57,6 @@ mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) -def test_GIVEN_test_has_mpl_is_False_THEN_cmm_has_mpl_is_False(): - """If the MPL IS NOT installed in the runtime environment, - assert the cmm has _HAS_MPL set to False""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is False - - -def test_GIVEN_test_has_mpl_is_True_THEN_cmm_has_mpl_is_True(): - """If the MPL IS installed in the runtime environment, - assert the cmm has _HAS_MPL set to True""" - - assert hasattr(aws_encryption_sdk.materials_managers.mpl.materials, "_HAS_MPL") - assert aws_encryption_sdk.materials_managers.mpl.materials._HAS_MPL is True - - -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_THEN_raise_ImportError(): - with pytest.raises(ImportError): - MPLEncryptionMaterials(mpl_materials="doesn't matter") - def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) diff --git a/tox.ini b/tox.ini index 419188b54..3daa40e47 100644 --- a/tox.ini +++ b/tox.ini @@ -12,9 +12,11 @@ envlist = # tests in a test environment that also has the MPL. py{311,312}-{local,integ,accept,examples}{,-mpl}, # >=3.11: Run ONLY the MPL-specific tests. - # These must be separate from the above target. - # These require the `-mpl` suffix so tox installs the MPL. - # The `mpl` prefix runs only MPL-specific tests + # These must be separate from the above target, since + # these require the `-mpl` suffix. + # The `mpl` prefix specifies a separate target, + # i.e. `mpllocal` instead of `local`. + # `mplXXX` contains tests using MPL components. py{311,312}-mpl{local,examples}-mpl nocmk, bandit, doc8, readme, docs, From fee4f36f44c94bb357d10272190359fdf5d82e62 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 16:57:03 -0800 Subject: [PATCH 117/172] test cleanup --- .../example_branch_key_id_supplier.py | 5 +- .../materials_managers/mpl/cmm.py | 78 +++---- .../materials_managers/mpl/materials.py | 12 +- src/aws_encryption_sdk/streaming_client.py | 8 +- .../unit/test_material_managers_mpl_cmm.py | 207 +++++++++++------- .../test_material_managers_mpl_materials.py | 139 +++++++----- 6 files changed, 271 insertions(+), 178 deletions(-) diff --git a/examples/src/keyrings/example_branch_key_id_supplier.py b/examples/src/keyrings/example_branch_key_id_supplier.py index a06280fa1..ba9ae060c 100644 --- a/examples/src/keyrings/example_branch_key_id_supplier.py +++ b/examples/src/keyrings/example_branch_key_id_supplier.py @@ -18,11 +18,10 @@ def __init__(self, tenant_1_id, tenant_2_id): def get_branch_key_id( self, - # TODO-MPL: Change this to `native_input` in Smithy-Dafny - input: GetBranchKeyIdInput # noqa pylint: disable=redefined-builtin + param: GetBranchKeyIdInput ) -> GetBranchKeyIdOutput: """Returns branch key ID from the tenant ID in input's encryption context.""" - encryption_context: Dict[str, str] = input.encryption_context + encryption_context: Dict[str, str] = param.encryption_context if b"tenant" not in encryption_context: raise ValueError("EncryptionContext invalid, does not contain expected tenant key value pair.") diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index 1e3e3fb34..c97c070f0 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -2,62 +2,64 @@ from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, - DecryptMaterialsOutput, + AlgorithmSuiteIdESDK as MPL_AlgorithmSuiteIdESDK, + CommitmentPolicyESDK as MPL_CommitmentPolicyESDK, + DecryptMaterialsInput as MPL_DecryptMaterialsInput, + DecryptMaterialsOutput as MPL_DecryptMaterialsOutput, EncryptedDataKey as MPL_EncryptedDataKey, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, + GetEncryptionMaterialsInput as MPL_GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput as MPL_GetEncryptionMaterialsOutput, +) +from aws_cryptographic_materialproviders.mpl.references import ( + ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager ) -from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager from typing import List from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError from aws_encryption_sdk.identifiers import CommitmentPolicy -from aws_encryption_sdk.materials_managers.mpl.materials import MPLEncryptionMaterials, MPLDecryptionMaterials +from aws_encryption_sdk.materials_managers.mpl.materials import EncryptionMaterialsFromMPL, DecryptionMaterialsFromMPL from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey -class MPLCMMHandler(CryptoMaterialsManager): +class CryptoMaterialsManagerFromMPL(CryptoMaterialsManager): """ In instances where encryption materials are provided by an implementation of the MPL's - `aws_cryptographic_materialproviders.mpl.references.ICryptographicMaterialsManager`, + `aws_cryptographic_materialproviders.mpl.references.MPL_ICryptographicMaterialsManager`, this maps the ESDK CMM interfaces to the MPL CMM. """ - mpl_cmm: 'ICryptographicMaterialsManager' + mpl_cmm: 'MPL_ICryptographicMaterialsManager' def __init__( self, - mpl_cmm: 'ICryptographicMaterialsManager' + mpl_cmm: 'MPL_ICryptographicMaterialsManager' ): """ - Create MPLCMMHandler. + Create CryptoMaterialsManagerFromMPL. :param mpl_cmm: Underlying MPL cryptographic materials manager """ - if isinstance(mpl_cmm, ICryptographicMaterialsManager): + if isinstance(mpl_cmm, MPL_ICryptographicMaterialsManager): self.mpl_cmm = mpl_cmm else: - raise ValueError(f"Invalid CMM passed to MPLCMMHandler. cmm: {mpl_cmm}") + raise ValueError(f"Invalid CMM passed to CryptoMaterialsManagerFromMPL. cmm: {mpl_cmm}") def get_encryption_materials( self, request: EncryptionMaterialsRequest - ) -> MPLEncryptionMaterials: + ) -> EncryptionMaterialsFromMPL: """ Returns an EncryptionMaterialsHandler for the configured CMM. :param request: Request for encryption materials """ try: - mpl_input: GetEncryptionMaterialsInput = MPLCMMHandler._native_to_mpl_get_encryption_materials( + mpl_input: MPL_GetEncryptionMaterialsInput = CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials( request ) - mpl_output: GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) - return MPLEncryptionMaterials(mpl_output.encryption_materials) + mpl_output: MPL_GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) + return EncryptionMaterialsFromMPL(mpl_output.encryption_materials) except AwsCryptographicMaterialProvidersException as mpl_exception: # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. @@ -66,11 +68,11 @@ def get_encryption_materials( @staticmethod def _native_to_mpl_get_encryption_materials( request: EncryptionMaterialsRequest - ) -> 'GetEncryptionMaterialsInput': - commitment_policy = MPLCMMHandler._native_to_mpl_commmitment_policy( + ) -> 'MPL_GetEncryptionMaterialsInput': + commitment_policy = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy( request.commitment_policy ) - output: GetEncryptionMaterialsInput = GetEncryptionMaterialsInput( + output: MPL_GetEncryptionMaterialsInput = MPL_GetEncryptionMaterialsInput( encryption_context=request.encryption_context, commitment_policy=commitment_policy, max_plaintext_length=request.plaintext_length, @@ -80,54 +82,54 @@ def _native_to_mpl_get_encryption_materials( @staticmethod def _native_to_mpl_commmitment_policy( native_commitment_policy: CommitmentPolicy - ) -> 'CommitmentPolicyESDK': + ) -> 'MPL_CommitmentPolicyESDK': if native_commitment_policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT: - return CommitmentPolicyESDK(value="FORBID_ENCRYPT_ALLOW_DECRYPT") + return MPL_CommitmentPolicyESDK(value="FORBID_ENCRYPT_ALLOW_DECRYPT") elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT: - return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_ALLOW_DECRYPT") + return MPL_CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_ALLOW_DECRYPT") elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT: - return CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_REQUIRE_DECRYPT") + return MPL_CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_REQUIRE_DECRYPT") else: raise ValueError(f"Invalid native_commitment_policy: {native_commitment_policy}") def decrypt_materials( self, request: DecryptionMaterialsRequest - ) -> MPLDecryptionMaterials: + ) -> DecryptionMaterialsFromMPL: """ - Returns a MPLDecryptionMaterials for the configured CMM. + Returns a DecryptionMaterialsFromMPL for the configured CMM. :param request: Request for decryption materials """ try: - mpl_input: 'DecryptMaterialsInput' = \ - MPLCMMHandler._create_mpl_decrypt_materials_input_from_request(request) - mpl_output: 'DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(mpl_input) - return MPLDecryptionMaterials(mpl_output.decryption_materials) + mpl_input: 'MPL_DecryptMaterialsInput' = \ + CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request(request) + mpl_output: 'MPL_DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(mpl_input) + return DecryptionMaterialsFromMPL(mpl_output.decryption_materials) except AwsCryptographicMaterialProvidersException as mpl_exception: # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. raise AWSEncryptionSDKClientError(mpl_exception) @staticmethod - def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> 'AlgorithmSuiteIdESDK': + def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> 'MPL_AlgorithmSuiteIdESDK': # MPL algorithm suite ID = hexstr(native_algorithm_id) padded to 4 digits post-`x`. - return AlgorithmSuiteIdESDK(f"{native_algorithm_id:#0{6}x}") + return MPL_AlgorithmSuiteIdESDK(f"{native_algorithm_id:#0{6}x}") @staticmethod def _create_mpl_decrypt_materials_input_from_request( request: DecryptionMaterialsRequest - ) -> 'DecryptMaterialsInput': + ) -> 'MPL_DecryptMaterialsInput': key_blob_list: List[Native_EncryptedDataKey] = request.encrypted_data_keys list_edks = [MPL_EncryptedDataKey( key_provider_id=key_blob.key_provider.provider_id, key_provider_info=key_blob.key_provider.key_info, ciphertext=key_blob.encrypted_data_key, ) for key_blob in key_blob_list] - output: DecryptMaterialsInput = DecryptMaterialsInput( - algorithm_suite_id=MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id( + output: MPL_DecryptMaterialsInput = MPL_DecryptMaterialsInput( + algorithm_suite_id=CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( request.algorithm.algorithm_id ), - commitment_policy=MPLCMMHandler._native_to_mpl_commmitment_policy( + commitment_policy=CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy( request.commitment_policy ), encrypted_data_keys=list_edks, diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index 1ea2a199d..2bdf3f810 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -21,7 +21,7 @@ def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str) -> int: return int(mpl_algorithm_id, 16) -class MPLEncryptionMaterials(Native_EncryptionMaterials): +class EncryptionMaterialsFromMPL(Native_EncryptionMaterials): """ In instances where encryption materials are be provided by the MPL's `aws_cryptographic_materialproviders.mpl.models.EncryptionMaterials`, @@ -35,13 +35,13 @@ def __init__( mpl_materials: 'MPL_EncryptionMaterials' ): """ - Create MPLEncryptionMaterials. + Create EncryptionMaterialsFromMPL. :param materials: Underlying encryption materials """ if isinstance(mpl_materials, MPL_EncryptionMaterials): self.mpl_materials = mpl_materials else: - raise ValueError("Invalid EncryptionMaterials passed to MPLEncryptionMaterials. " + raise ValueError("Invalid EncryptionMaterials passed to EncryptionMaterialsFromMPL. " f"materials: {mpl_materials}") @property @@ -91,7 +91,7 @@ def signing_key(self) -> bytes: return self.mpl_materials.signing_key -class MPLDecryptionMaterials(Native_DecryptionMaterials): +class DecryptionMaterialsFromMPL(Native_DecryptionMaterials): """ In instances where decryption materials are be provided by the MPL's `aws_cryptographic_materialproviders.mpl.models.DecryptionMaterials`, @@ -105,13 +105,13 @@ def __init__( mpl_materials: 'MPL_DecryptionMaterials' ): """ - Create MPLDecryptionMaterials. + Create DecryptionMaterialsFromMPL. :param materials: Underlying decryption materials """ if isinstance(mpl_materials, MPL_DecryptionMaterials): self.mpl_materials = mpl_materials else: - raise ValueError(f"Invalid DecryptionMaterials passed to MPLDecryptionMaterials.\ + raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsFromMPL.\ materials: {mpl_materials}") @property diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index a3c05bbb7..72ed4efb7 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -80,7 +80,7 @@ _HAS_MPL = True # Import internal ESDK modules that depend on the MPL - from aws_encryption_sdk.materials_managers.mpl.cmm import MPLCMMHandler + from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL except ImportError: _HAS_MPL = False @@ -172,7 +172,7 @@ def _has_mpl_attrs_post_init(self): keyring=self.keyring ) ) - cmm_handler: CryptoMaterialsManager = MPLCMMHandler(cmm) + cmm_handler: CryptoMaterialsManager = CryptoMaterialsManagerFromMPL(cmm) self.materials_manager = cmm_handler def _no_mpl_attrs_post_init(self): @@ -555,7 +555,7 @@ def _prep_message(self): # MPL verification key is PEM bytes, not DER bytes. # If the underlying CMM is from the MPL, load PEM bytes. if (_HAS_MPL - and isinstance(self.config.materials_manager, MPLCMMHandler)): + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): self.signer = Signer.from_key_bytes( algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key, encoding=serialization.Encoding.PEM, @@ -923,7 +923,7 @@ def _read_header(self): # MPL verification key is NOT key bytes; it is bytes of the compressed point. # If the underlying CMM is from the MPL, load bytes from encoded point. if (_HAS_MPL - and isinstance(self.config.materials_manager, MPLCMMHandler)): + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): self.verifier = Verifier.from_encoded_point( algorithm=header.algorithm, encoded_point=base64.b64encode(decryption_materials.verification_key) diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index cae334722..22f8bf63e 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -17,11 +17,10 @@ from aws_encryption_sdk.identifiers import CommitmentPolicy -import aws_encryption_sdk.materials_managers.mpl.cmm -from aws_encryption_sdk.materials_managers.mpl.cmm import MPLCMMHandler +from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL from aws_encryption_sdk.materials_managers.mpl.materials import ( - MPLEncryptionMaterials, - MPLDecryptionMaterials, + EncryptionMaterialsFromMPL, + DecryptionMaterialsFromMPL, ) pytestmark = [pytest.mark.unit, pytest.mark.local] @@ -51,180 +50,234 @@ EncryptionMaterialsRequest, DecryptionMaterialsRequest, ) +from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) -mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) +mock_encryption_materials_handler = MagicMock(__class__=EncryptionMaterialsFromMPL) mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) +mock_edk = MagicMock(__class__=Native_EncryptedDataKey) +mock_mpl_key_provider_id = MagicMock(__class__=str) +mock_edk.key_provider.provider_id = mock_mpl_key_provider_id +mock_mpl_key_provider_info = MagicMock(__class__=bytes) +mock_edk.key_provider.key_info = mock_mpl_key_provider_info +mock_mpl_encrypted_data_key = MagicMock(__class__=bytes) +mock_edk.encrypted_data_key = mock_mpl_encrypted_data_key -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): - mpl_cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - - assert mpl_cmm_handler.mpl_cmm == mock_mpl_cmm + +def test_GIVEN_valid_mpl_cmm_WHEN_create_CryptoMaterialsManagerFromMPL_THEN_return_new_CryptoMaterialsManagerFromMPL(): + # Given: valid mpl_cmm + # When: create new CryptoMaterialsManagerFromMPL + mpl_cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) + # Then: CryptoMaterialsManagerFromMPL is valid + assert mpl_cmm.mpl_cmm == mock_mpl_cmm -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): +def test_GIVEN_invalid_mpl_cmm_WHEN_create_CryptoMaterialsManagerFromMPL_THEN_raise_ValueError(): + # Then: raises ValueError with pytest.raises(ValueError): - MPLCMMHandler(mpl_cmm="not a valid mpl_cmm") + # Given: invalid mpl_cmm + # When: create new CryptoMaterialsManagerFromMPL + CryptoMaterialsManagerFromMPL(mpl_cmm="not a valid mpl_cmm") @patch.object(mock_mpl_cmm, "get_encryption_materials") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_get_encryption_materials") -def test_GIVEN_valid_request_WHEN_call_get_encryption_materials_THEN_return_MPLEncryptionMaterials( +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials") +def test_GIVEN_valid_request_WHEN_get_encryption_materials_THEN_return_EncryptionMaterialsFromMPL( mock_native_to_mpl_get_encryption_materials, mock_get_encryption_materials, ): - # Mock: mpl_cmm.get_encryption_materials returns mock MPL encryption materials + # Given: _native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput + mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) + mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input + + # Given: mpl_cmm.get_encryption_materials returns mock MPL encryption materials mock_get_encryption_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials mock_get_encryption_materials.return_value = mock_get_encryption_materials_output - # Mock: CMMHandler._native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput - mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) - mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - test = cmm_handler.get_encryption_materials(mock_encryption_materials_request) + # When: get_encryption_materials + cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) + output = cmm.get_encryption_materials(mock_encryption_materials_request) - # Verify cmm_handler returns MPLEncryptionMaterials - assert isinstance(test, MPLEncryptionMaterials) + # Then: + # Verify cmm returns EncryptionMaterialsFromMPL + assert isinstance(output, EncryptionMaterialsFromMPL) # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` - assert test.mpl_materials == mock_mpl_encryption_materials + assert output.mpl_materials == mock_mpl_encryption_materials # Verify we actually called `get_encryption_materials` mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") -def test_GIVEN_get_encryption_materials_raises_MPL_Exception_WHEN_call_get_encryption_materials_THEN_raise_ESDK_Exception( +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy") +def test_GIVEN_mpl_cmm_raises_MPLException_WHEN_get_encryption_materials_THEN_raise_ESDKException( _ ): + # Then: Raises AWSEncryptionSDKClientError with pytest.raises(AWSEncryptionSDKClientError): + # Given: mpl_cmm.get_encryption_materials raises MPL exception with patch.object(mock_mpl_cmm, "get_encryption_materials", side_effect=AwsCryptographicMaterialProvidersException("any")): - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - cmm_handler.get_encryption_materials(mock_encryption_materials_request) + # When: get_encryption_materials + cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) + cmm.get_encryption_materials(mock_encryption_materials_request) -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") -def test_GIVEN_native_to_mpl_commmitment_policy_returns_valid_policy_WHEN_call_native_to_mpl_get_encryption_materials_THEN_returns_GetEncryptionMaterialsInput( +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy") +def test_GIVEN_valid_mpl_commitment_policy_WHEN_native_to_mpl_get_encryption_materials_THEN_returns_GetEncryptionMaterialsInput( mock_mpl_commitment_policy ): + # Given: commitment policy is some MPL ESDK commitment policy mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) mock_mpl_commitment_policy.return_value = mock_commitment_policy - output = MPLCMMHandler._native_to_mpl_get_encryption_materials(mock_encryption_materials_request) + # When: _native_to_mpl_get_encryption_materials + output = CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials(mock_encryption_materials_request) - # verify correctness of returned value + # Then: returned GetEncryptionMaterialsInput is correct assert isinstance(output, GetEncryptionMaterialsInput) assert output.encryption_context == mock_encryption_materials_request.encryption_context assert output.commitment_policy == mock_commitment_policy assert output.max_plaintext_length == mock_encryption_materials_request.plaintext_length -def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): +def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): + # Given: native FORBID_ENCRYPT_ALLOW_DECRYPT native_commitment_policy = CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT - output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + # When: _native_to_mpl_commmitment_policy + output = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) + # Then: Returns MPL FORBID_ENCRYPT_ALLOW_DECRYPT assert isinstance(output, CommitmentPolicyESDK) assert output.value == "FORBID_ENCRYPT_ALLOW_DECRYPT" -def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): + # Given: native REQUIRE_ENCRYPT_ALLOW_DECRYPT native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT - output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + # When: _native_to_mpl_commmitment_policy + output = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) + # Then: Returns MPL REQUIRE_ENCRYPT_ALLOW_DECRYPT assert isinstance(output, CommitmentPolicyESDK) assert output.value == "REQUIRE_ENCRYPT_ALLOW_DECRYPT" -def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_call_native_to_mpl_commmitment_policyTHEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): + # Given: native REQUIRE_ENCRYPT_REQUIRE_DECRYPT native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT - output = MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + # When: _native_to_mpl_commmitment_policy + output = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) + # Then: Returns MPL REQUIRE_ENCRYPT_REQUIRE_DECRYPT assert isinstance(output, CommitmentPolicyESDK) assert output.value == "REQUIRE_ENCRYPT_REQUIRE_DECRYPT" -def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_call_native_to_mpl_commmitment_policyTHEN_raise_ValueError(): +def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_native_to_mpl_commmitment_policy_THEN_raise_ValueError(): + # Given: invalid native commitment policy native_commitment_policy = "not a commitment policy" + # Then: Raises ValueError with pytest.raises(ValueError): - MPLCMMHandler._native_to_mpl_commmitment_policy(native_commitment_policy) + # When: _native_to_mpl_commmitment_policy + CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) @patch.object(mock_mpl_cmm, "decrypt_materials") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") -def test_GIVEN_valid_request_WHEN_call_decrypt_materials_THEN_return_MPLDecryptionMaterials( +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request") +def test_GIVEN_valid_request_WHEN_decrypt_materials_THEN_return_DecryptionMaterialsFromMPL( mock_native_to_mpl_decrypt_materials, mock_get_encryption_materials, ): - # Mock: mpl_cmm.get_decryption_materials returns mock MPL decryption materials + # Given: mpl_cmm.get_decryption_materials returns mock MPL decryption materials mock_decrypt_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) mock_decrypt_materials_output.decryption_materials = mock_mpl_decrypt_materials mock_get_encryption_materials.return_value = mock_decrypt_materials_output - # Mock: CMMHandler._create_mpl_decrypt_materials_input_from_request creates a DecryptMaterialsInput + # Given: CMMHandler._create_mpl_decrypt_materials_input_from_request creates a DecryptMaterialsInput mock_decrypt_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) mock_native_to_mpl_decrypt_materials.return_value = mock_decrypt_materials_input - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - output = cmm_handler.decrypt_materials(mock_decryption_materials_request) + # When: decrypt_materials + cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) + output = cmm.decrypt_materials(mock_decryption_materials_request) - # Verify cmm_handler returns MPLDecryptionMaterials - assert isinstance(output, MPLDecryptionMaterials) - # Verify returned MPLDecryptionMaterials uses the output of `decrypt_materials` + # Then: + # Verify cmm returns DecryptionMaterialsFromMPL + assert isinstance(output, DecryptionMaterialsFromMPL) + # Verify returned DecryptionMaterialsFromMPL uses the output of `decrypt_materials` assert output.mpl_materials == mock_mpl_decrypt_materials # Verify we actually called `decrypt_materials` mock_mpl_cmm.decrypt_materials.assert_called_once_with(mock_decrypt_materials_input) -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._create_mpl_decrypt_materials_input_from_request") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request") def test_GIVEN_decrypt_materials_raises_MPL_Exception_WHEN_call_decrypt_materials_THEN_raise_ESDK_Exception( _ ): + # Then: Raises AWSEncryptionSDKClientError with pytest.raises(AWSEncryptionSDKClientError): + # Given: mpl_cmm.decrypt_materials raises MPL exception with patch.object(mock_mpl_cmm, "decrypt_materials", side_effect=AwsCryptographicMaterialProvidersException("any")): - - cmm_handler = MPLCMMHandler(mpl_cmm=mock_mpl_cmm) - cmm_handler.decrypt_materials(mock_decryption_materials_request) + # When: decrypt_materials + cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) + cmm.decrypt_materials(mock_decryption_materials_request) -def test_WHEN_call_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_AlgorithmSuiteIdESDK(): - some_native_algorithm_id = 0x0000 # Not a real algorithm ID, but fits the format +def test_GIVEN_valid_native_algorithm_id_WHEN_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_AlgorithmSuiteIdESDK(): + # Given: any native algorithm ID + some_native_algorithm_id = 0x1234 # Not a real algorithm ID, but fits the format - mpl_output = MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id( + # When: _native_algorithm_id_to_mpl_algorithm_id + mpl_output = CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( some_native_algorithm_id ) + # Then: returns valid MPL algorithm ID assert isinstance(mpl_output, AlgorithmSuiteIdESDK) - assert mpl_output.value == "0x0000" + assert mpl_output.value == "0x1234" -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_algorithm_id_to_mpl_algorithm_id") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.MPLCMMHandler._native_to_mpl_commmitment_policy") -def test__create_mpl_decrypt_materials_input_from_request( +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy") +def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_request_THEN_returns_MPL_DecryptMaterialsInput( mock_mpl_commitment_policy, mock_mpl_algorithm_id, ): + # Given: _native_algorithm_id_to_mpl_algorithm_id returns a valid MPL algorithm ID mock_algorithm_id = "0x1234" # Some fake algorithm ID that fits the format mock_mpl_algorithm_id.return_value = mock_algorithm_id + + # Given: _native_to_mpl_commmitment_policy returns some MPL commitment policy mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) mock_mpl_commitment_policy.return_value = mock_commitment_policy - # mock_decryption_materials_request.algorithm = - - output = MPLCMMHandler._create_mpl_decrypt_materials_input_from_request(mock_decryption_materials_request) - - assert isinstance(output, DecryptMaterialsInput) - assert output.algorithm_suite_id == mock_algorithm_id - assert output.commitment_policy == mock_commitment_policy - assert output.encryption_context == mock_decryption_materials_request.encryption_context - - assert len(output.encrypted_data_keys) == len(mock_decryption_materials_request.encrypted_data_keys) - for i in range(len(output.encrypted_data_keys)): - # Assume input[i] == output[i], seems to work - output_edk = output.encrypted_data_keys[i] - input_edk = mock_decryption_materials_request[i] - assert output_edk.key_provider_id == input_edk.key_provider.provider_id - assert output_edk.key_provider_info == input_edk.key_provider.key_info - assert output_edk.ciphertext == input_edk.encrypted_data_key + no_mock_edks = [ mock_edk ] + one_mock_edk = [ mock_edk ] + two_mock_edks = [ mock_edk, mock_edk ] + + # Given: ESK lists of various lengths + for mock_edks in [ no_mock_edks, one_mock_edk, two_mock_edks ]: + + mock_decryption_materials_request.encrypted_data_keys = mock_edks + + # When: _create_mpl_decrypt_materials_input_from_request + output = CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request(mock_decryption_materials_request) + + # Then: + # Verify general correctness of output structure + assert isinstance(output, DecryptMaterialsInput) + assert output.algorithm_suite_id == mock_algorithm_id + assert output.commitment_policy == mock_commitment_policy + assert output.encryption_context == mock_decryption_materials_request.encryption_context + + assert len(output.encrypted_data_keys) == len(mock_edks) + for i in range(len(output.encrypted_data_keys)): + # Assume input[i] == output[i] to make validation easier + # This is how the src is implemented but is not a requirement. + # If this assumption breaks, we should enhance this test. + output_edk = output.encrypted_data_keys[i] + input_edk = mock_edks[i] + assert output_edk.key_provider_id == input_edk.key_provider.provider_id + assert output_edk.key_provider_info == input_edk.key_provider.key_info + assert output_edk.ciphertext == input_edk.encrypted_data_key \ No newline at end of file diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index bb83b89fd..b39e9bc8d 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -14,13 +14,12 @@ import pytest from mock import MagicMock, patch, PropertyMock -from typing import Dict, List +from typing import Dict, List, Set -from aws_encryption_sdk.identifiers import CommitmentPolicy import aws_encryption_sdk.materials_managers.mpl.materials from aws_encryption_sdk.materials_managers.mpl.materials import ( - MPLEncryptionMaterials, - MPLDecryptionMaterials, + EncryptionMaterialsFromMPL, + DecryptionMaterialsFromMPL, ) from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite @@ -54,124 +53,164 @@ mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) -mock_encryption_materials_handler = MagicMock(__class__=MPLEncryptionMaterials) +mock_encryption_materials_handler = MagicMock(__class__=EncryptionMaterialsFromMPL) mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) +mock_edk = MagicMock(__class__=MPL_EncryptedDataKey) +mock_mpl_key_provider_id = MagicMock(__class__=str) +mock_edk.key_provider_id = mock_mpl_key_provider_id +mock_mpl_key_provider_info = MagicMock(__class__=bytes) +mock_edk.key_provider_info = mock_mpl_key_provider_info +mock_mpl_ciphertext = MagicMock(__class__=bytes) +mock_edk.ciphertext = mock_mpl_ciphertext -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_valid_mpl_cmm_THEN_return_new_MPLCMMHandler(): - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + +def test_GIVEN_valid_mpl_materials_WHEN_create_EncryptionMaterialsFromMPL_THEN_return_new_CryptoMaterialsManagerFromMPL(): + # Given: valid mpl_materials + # When: create EncryptionMaterialsFromMPL + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) + # Then: EncryptionMaterialsFromMPL is valid assert mpl_encryption_materials.mpl_materials == mock_mpl_encryption_materials -def test_GIVEN_test_has_mpl_is_False_WHEN_create_MPLCMMHandler_with_invalid_mpl_cmm_THEN_raise_ValueError(): +def test_GIVEN_invalid_mpl_materials_WHEN_create_EncryptionMaterialsFromMPL_THEN_raise_ValueError(): + # Then: Raise ValueError with pytest.raises(ValueError): - MPLEncryptionMaterials(mpl_materials="not a valid mpl_materials") + # Given: invalid mpl_materials + # When: create EncryptionMaterialsFromMPL + EncryptionMaterialsFromMPL(mpl_materials="not a valid mpl_materials") + -def test_mpl_to_native(): +def test_GIVEN_valid_mpl_algorithm_id_WHEN_mpl_algorithm_id_to_native_algorithm_id_THEN_valid_native_output(): + # Given: any valid MPL algorithm ID some_mpl_algorithm_id = "0x1234" # Not a real algorithm ID, but fits the format + # When: _mpl_algorithm_id_to_native_algorithm_id native_output = aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id( some_mpl_algorithm_id ) + # Then: valid native algorithm ID assert native_output == 0x1234 @patch("aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id") @patch("aws_encryption_sdk.materials_managers.mpl.materials.AlgorithmSuite.get_by_id") -def test_GIVEN_valid_mpl_algorithm_id_WHEN_get_algorithm_THEN_valid_native_algorithm_id( +def test_GIVEN_valid_mpl_algorithm_id_WHEN_EncryptionMaterials_get_algorithm_THEN_valid_native_algorithm_id( mock_algorithm, mock_native_algorithm_id, ): - # Mock valid conversion from MPL to native algorithm ID + # Given: _mpl_algorithm_id_to_native_algorithm_id returns a valid native algorithm ID mock_native_algorithm_id.return_value = 0x1234 - # Mock valid lookup in native AlgorithmSuite lookup + # Given: get_by_id returns a valid native AlgorithmSuite by looking up an ID mock_algorithm.return_value = MagicMock(__class__=AlgorithmSuite) - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + # When: Get algorithm + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) output = mpl_encryption_materials.algorithm + + # Then: output is valid assert output == mock_algorithm() # property calls automatically, we need to call the mock -def test_GecTHEN_valid_native_algorithm_id(): +def test_GIVEN_valid_encryption_context_WHEN_EncryptionMaterials_get_encryption_context_THEN_valid_encryption_context(): + # Given: valid encryption context mock_encryption_context = MagicMock(__class__=Dict[str, str]) mock_mpl_encryption_materials.encryption_context = mock_encryption_context - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + # When: get encryption context + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) output = mpl_encryption_materials.encryption_context + # Then: returns valid encryption context assert output == mock_encryption_context -def test_GecTHEN_valid_nativefadsf_algorithm_id(): - mock_edk = MagicMock(__class__=MPL_EncryptedDataKey) - mock_mpl_key_provider_id = MagicMock(__class__=str) - mock_edk.key_provider_id = mock_mpl_key_provider_id - mock_mpl_key_provider_info = MagicMock(__class__=bytes) - mock_edk.key_provider_info = mock_mpl_key_provider_info - mock_mpl_ciphertext = MagicMock(__class__=bytes) - mock_edk.ciphertext = mock_mpl_ciphertext - - mock_edks = [ mock_edk ] - mock_mpl_encryption_materials.encrypted_data_keys = mock_edks - - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) - output = mpl_encryption_materials.encrypted_data_keys - output_as_list = list(output) - - assert len(output_as_list) == len(mock_edks) - for i in range(len(output_as_list)): - # assume output[i] corresponds to input[i] - native_edk = output_as_list[i] - mpl_edk = mock_edks[i] - - assert native_edk.encrypted_data_key == mpl_edk.ciphertext - assert native_edk.key_provider.provider_id == mpl_edk.key_provider_id - assert native_edk.key_provider.key_info == mpl_edk.key_provider_info - -def test_GecTHEN_valid_nativefadsffadsfa_algorithm_id(): +def test_GIVEN_valid_edks_WHEN_EncryptionMaterials_get_edks_THEN_returns_edks(): + + # Given: lists of mocked EDKs of various lengths + no_mock_edks = [] + one_mock_edk = [ mock_edk ] + two_mocked_edks = [ mock_edk, mock_edk ] + for mock_edks in [ no_mock_edks, one_mock_edk, two_mocked_edks ]: + mock_mpl_encryption_materials.encrypted_data_keys = mock_edks + + # When: get EDKs + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.encrypted_data_keys + + # Then: returns EDKs + output_as_list = list(output) + # Native ESDK Python types the EDKs as a set; + # Ensure the MPL's list is collapsed into a set correctly + assert len(output_as_list) == len(set(mock_edks)) + for i in range(len(output_as_list)): + # Assume input[i] == output[i] to make validation easier + # This is how the src is implemented but is not a requirement. + # If this assumption breaks, we should enhance this test. + native_edk = output_as_list[i] + mpl_edk = mock_edks[i] + + assert native_edk.encrypted_data_key == mpl_edk.ciphertext + assert native_edk.key_provider.provider_id == mpl_edk.key_provider_id + assert native_edk.key_provider.key_info == mpl_edk.key_provider_info + + +def test_GIVEN_valid_data_key_WHEN_EncryptionMaterials_get_data_key_THEN_returns_data_key(): + # Given: Valid MPL data key mock_data_key = MagicMock(__class__=bytes) mock_mpl_encryption_materials.plaintext_data_key = mock_data_key - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + # When: get data key + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) output = mpl_encryption_materials.data_encryption_key + # Then: Returns native data key assert output.key_provider.provider_id == "" assert output.key_provider.key_info == b"" assert output.data_key == mock_data_key assert output.encrypted_data_key == b"" -def test_GecTHEN_valid_nativefasdfasdffadsf_algorithm_id(): +def test_GIVEN_valid_signing_key_WHEN_EncryptionMaterials_get_signing_key_THEN_returns_signing_key(): + # Given: valid signing key mock_signing_key = MagicMock(__class__=bytes) mock_mpl_encryption_materials.signing_key = mock_signing_key - mpl_encryption_materials = MPLEncryptionMaterials(mpl_materials=mock_mpl_encryption_materials) + # When: get signing key + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) output = mpl_encryption_materials.signing_key + # Then: returns signing key assert output == mock_signing_key -def test_GecTHEN_valid_nativeffasdfasdadsffadsfa_algorithm_id(): +def test_GIVEN_valid_data_key_WHEN_DecryptionMaterials_get_data_key_THEN_returns_data_key(): + # Given: valid MPL data key mock_data_key = MagicMock(__class__=bytes) mock_mpl_decrypt_materials.plaintext_data_key = mock_data_key - mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) + # When: get data key + mpl_decryption_materials = DecryptionMaterialsFromMPL(mpl_materials=mock_mpl_decrypt_materials) output = mpl_decryption_materials.data_key + # Then: returns valid native data key assert output.key_provider.provider_id == "" assert output.key_provider.key_info == b"" assert output.data_key == mock_data_key assert output.encrypted_data_key == b"" -def test_GecTHEN_validadsfasdf_nativefasdfasdffadsf_algorithm_id(): +def test_GIVEN_valid_verification_key_WHEN_DecryptionMaterials_get_verification_key_THEN_returns_verification_key(): + # Given: valid verification key mock_verification_key = MagicMock(__class__=bytes) mock_mpl_decrypt_materials.verification_key = mock_verification_key - mpl_decryption_materials = MPLDecryptionMaterials(mpl_materials=mock_mpl_decrypt_materials) + # When: get verification key + mpl_decryption_materials = DecryptionMaterialsFromMPL(mpl_materials=mock_mpl_decrypt_materials) output = mpl_decryption_materials.verification_key + # Then: returns verification key assert output == mock_verification_key From ac6471a921407df409bceb9dc1dafbb5e697544c Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:00:31 -0800 Subject: [PATCH 118/172] test cleanup --- examples/src/keyrings/hierarchical_keyring.py | 6 +- .../unit/test_material_managers_mpl_cmm.py | 56 +++++++++---------- .../test_material_managers_mpl_materials.py | 11 ---- 3 files changed, 31 insertions(+), 42 deletions(-) diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index c71719346..aa87485f9 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -4,12 +4,12 @@ import sys import boto3 -# ignore missing MPL for pylint, but the MPL is required for this example +# Ignore missing MPL for pylint, but the MPL is required for this example # noqa pylint: disable=import-error -from aws_cryptographic_materialproviders.keystore.client import KeyStore +from aws_cryptographic_materialproviders.keystore import KeyStore from aws_cryptographic_materialproviders.keystore.config import KeyStoreConfig from aws_cryptographic_materialproviders.keystore.models import CreateKeyInput, KMSConfigurationKmsKeyArn -from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders +from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.models import ( CacheTypeDefault, diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index 22f8bf63e..52a4b333c 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -28,19 +28,19 @@ from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, + AlgorithmSuiteIdESDK as MPL_AlgorithmSuiteIdESDK, + CommitmentPolicyESDK as MPL_CommitmentPolicyESDK, + DecryptMaterialsInput as MPL_DecryptMaterialsInput, DecryptionMaterials as MPL_DecryptionMaterials, EncryptionMaterials as MPL_EncryptionMaterials, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, + GetEncryptionMaterialsInput as MPL_GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput as MPL_GetEncryptionMaterialsOutput, ) from aws_cryptographic_materialproviders.mpl.references import ( - ICryptographicMaterialsManager + ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, ) -mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) +mock_mpl_cmm = MagicMock(__class__=MPL_ICryptographicMaterialsManager) mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) @@ -89,12 +89,12 @@ def test_GIVEN_valid_request_WHEN_get_encryption_materials_THEN_return_Encryptio mock_get_encryption_materials, ): - # Given: _native_to_mpl_get_encryption_materials creates a GetEncryptionMaterialsInput - mock_get_encryption_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) + # Given: _native_to_mpl_get_encryption_materials creates a MPL_GetEncryptionMaterialsInput + mock_get_encryption_materials_input = MagicMock(__class__=MPL_GetEncryptionMaterialsInput) mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input # Given: mpl_cmm.get_encryption_materials returns mock MPL encryption materials - mock_get_encryption_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) + mock_get_encryption_materials_output = MagicMock(__class__=MPL_GetEncryptionMaterialsOutput) mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials mock_get_encryption_materials.return_value = mock_get_encryption_materials_output @@ -125,24 +125,24 @@ def test_GIVEN_mpl_cmm_raises_MPLException_WHEN_get_encryption_materials_THEN_ra cmm.get_encryption_materials(mock_encryption_materials_request) @patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy") -def test_GIVEN_valid_mpl_commitment_policy_WHEN_native_to_mpl_get_encryption_materials_THEN_returns_GetEncryptionMaterialsInput( +def test_GIVEN_valid_mpl_commitment_policy_WHEN_native_to_mpl_get_encryption_materials_THEN_returns_MPL_GetEncryptionMaterialsInput( mock_mpl_commitment_policy ): # Given: commitment policy is some MPL ESDK commitment policy - mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) + mock_commitment_policy = MagicMock(__class__=MPL_CommitmentPolicyESDK) mock_mpl_commitment_policy.return_value = mock_commitment_policy # When: _native_to_mpl_get_encryption_materials output = CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials(mock_encryption_materials_request) - # Then: returned GetEncryptionMaterialsInput is correct - assert isinstance(output, GetEncryptionMaterialsInput) + # Then: returned MPL_GetEncryptionMaterialsInput is correct + assert isinstance(output, MPL_GetEncryptionMaterialsInput) assert output.encryption_context == mock_encryption_materials_request.encryption_context assert output.commitment_policy == mock_commitment_policy assert output.max_plaintext_length == mock_encryption_materials_request.plaintext_length -def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): +def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): # Given: native FORBID_ENCRYPT_ALLOW_DECRYPT native_commitment_policy = CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT @@ -150,10 +150,10 @@ def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_ output = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) # Then: Returns MPL FORBID_ENCRYPT_ALLOW_DECRYPT - assert isinstance(output, CommitmentPolicyESDK) + assert isinstance(output, MPL_CommitmentPolicyESDK) assert output.value == "FORBID_ENCRYPT_ALLOW_DECRYPT" -def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): # Given: native REQUIRE_ENCRYPT_ALLOW_DECRYPT native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT @@ -161,10 +161,10 @@ def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl output = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) # Then: Returns MPL REQUIRE_ENCRYPT_ALLOW_DECRYPT - assert isinstance(output, CommitmentPolicyESDK) + assert isinstance(output, MPL_CommitmentPolicyESDK) assert output.value == "REQUIRE_ENCRYPT_ALLOW_DECRYPT" -def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): # Given: native REQUIRE_ENCRYPT_REQUIRE_DECRYPT native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT @@ -172,7 +172,7 @@ def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_native_to_m output = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) # Then: Returns MPL REQUIRE_ENCRYPT_REQUIRE_DECRYPT - assert isinstance(output, CommitmentPolicyESDK) + assert isinstance(output, MPL_CommitmentPolicyESDK) assert output.value == "REQUIRE_ENCRYPT_REQUIRE_DECRYPT" def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_native_to_mpl_commmitment_policy_THEN_raise_ValueError(): @@ -192,12 +192,12 @@ def test_GIVEN_valid_request_WHEN_decrypt_materials_THEN_return_DecryptionMateri ): # Given: mpl_cmm.get_decryption_materials returns mock MPL decryption materials - mock_decrypt_materials_output = MagicMock(__class__=GetEncryptionMaterialsOutput) + mock_decrypt_materials_output = MagicMock(__class__=MPL_GetEncryptionMaterialsOutput) mock_decrypt_materials_output.decryption_materials = mock_mpl_decrypt_materials mock_get_encryption_materials.return_value = mock_decrypt_materials_output - # Given: CMMHandler._create_mpl_decrypt_materials_input_from_request creates a DecryptMaterialsInput - mock_decrypt_materials_input = MagicMock(__class__=GetEncryptionMaterialsInput) + # Given: CMMHandler._create_mpl_decrypt_materials_input_from_request creates a MPL_DecryptMaterialsInput + mock_decrypt_materials_input = MagicMock(__class__=MPL_GetEncryptionMaterialsInput) mock_native_to_mpl_decrypt_materials.return_value = mock_decrypt_materials_input # When: decrypt_materials @@ -225,7 +225,7 @@ def test_GIVEN_decrypt_materials_raises_MPL_Exception_WHEN_call_decrypt_material cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) cmm.decrypt_materials(mock_decryption_materials_request) -def test_GIVEN_valid_native_algorithm_id_WHEN_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_AlgorithmSuiteIdESDK(): +def test_GIVEN_valid_native_algorithm_id_WHEN_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_MPL_AlgorithmSuiteIdESDK(): # Given: any native algorithm ID some_native_algorithm_id = 0x1234 # Not a real algorithm ID, but fits the format @@ -235,12 +235,12 @@ def test_GIVEN_valid_native_algorithm_id_WHEN_native_algorithm_id_to_mpl_algorit ) # Then: returns valid MPL algorithm ID - assert isinstance(mpl_output, AlgorithmSuiteIdESDK) + assert isinstance(mpl_output, MPL_AlgorithmSuiteIdESDK) assert mpl_output.value == "0x1234" @patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id") @patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy") -def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_request_THEN_returns_MPL_DecryptMaterialsInput( +def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_request_THEN_returns_MPL_MPL_DecryptMaterialsInput( mock_mpl_commitment_policy, mock_mpl_algorithm_id, ): @@ -249,7 +249,7 @@ def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_reques mock_mpl_algorithm_id.return_value = mock_algorithm_id # Given: _native_to_mpl_commmitment_policy returns some MPL commitment policy - mock_commitment_policy = MagicMock(__class__=CommitmentPolicyESDK) + mock_commitment_policy = MagicMock(__class__=MPL_CommitmentPolicyESDK) mock_mpl_commitment_policy.return_value = mock_commitment_policy no_mock_edks = [ mock_edk ] @@ -266,7 +266,7 @@ def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_reques # Then: # Verify general correctness of output structure - assert isinstance(output, DecryptMaterialsInput) + assert isinstance(output, MPL_DecryptMaterialsInput) assert output.algorithm_suite_id == mock_algorithm_id assert output.commitment_policy == mock_commitment_policy assert output.encryption_context == mock_decryption_materials_request.encryption_context diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index b39e9bc8d..92a8c95df 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -26,32 +26,21 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] -from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException from aws_cryptographic_materialproviders.mpl.models import ( - AlgorithmSuiteIdESDK, - CommitmentPolicyESDK, - DecryptMaterialsInput, DecryptionMaterials as MPL_DecryptionMaterials, EncryptedDataKey as MPL_EncryptedDataKey, EncryptionMaterials as MPL_EncryptionMaterials, - GetEncryptionMaterialsInput, - GetEncryptionMaterialsOutput, -) -from aws_cryptographic_materialproviders.mpl.references import ( - ICryptographicMaterialsManager ) mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) -from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError from aws_encryption_sdk.materials_managers import ( EncryptionMaterialsRequest, DecryptionMaterialsRequest, ) - mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) mock_encryption_materials_handler = MagicMock(__class__=EncryptionMaterialsFromMPL) mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) From a5ebc19c479f4b6de3874dc4735ca0e27ffbbc38 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:18:50 -0800 Subject: [PATCH 119/172] isort --- .../materials_managers/mpl/cmm.py | 5 ++--- .../materials_managers/mpl/materials.py | 1 - test/mpl/unit/test_material_managers_mpl_cmm.py | 14 +++----------- .../unit/test_material_managers_mpl_materials.py | 12 +++--------- 4 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index c97c070f0..1bbd7c89a 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -11,16 +11,15 @@ GetEncryptionMaterialsOutput as MPL_GetEncryptionMaterialsOutput, ) from aws_cryptographic_materialproviders.mpl.references import ( - ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager + ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, ) - from typing import List from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError from aws_encryption_sdk.identifiers import CommitmentPolicy -from aws_encryption_sdk.materials_managers.mpl.materials import EncryptionMaterialsFromMPL, DecryptionMaterialsFromMPL from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index 2bdf3f810..31f7d2a65 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -5,7 +5,6 @@ EncryptedDataKey as MPL_EncryptedDataKey, EncryptionMaterials as MPL_EncryptionMaterials, ) - from typing import Dict, List, Set from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index 52a4b333c..eb795b7c2 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -15,13 +15,9 @@ import pytest from mock import MagicMock, patch - from aws_encryption_sdk.identifiers import CommitmentPolicy from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL -from aws_encryption_sdk.materials_managers.mpl.materials import ( - EncryptionMaterialsFromMPL, - DecryptionMaterialsFromMPL, -) +from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL pytestmark = [pytest.mark.unit, pytest.mark.local] @@ -30,8 +26,8 @@ from aws_cryptographic_materialproviders.mpl.models import ( AlgorithmSuiteIdESDK as MPL_AlgorithmSuiteIdESDK, CommitmentPolicyESDK as MPL_CommitmentPolicyESDK, - DecryptMaterialsInput as MPL_DecryptMaterialsInput, DecryptionMaterials as MPL_DecryptionMaterials, + DecryptMaterialsInput as MPL_DecryptMaterialsInput, EncryptionMaterials as MPL_EncryptionMaterials, GetEncryptionMaterialsInput as MPL_GetEncryptionMaterialsInput, GetEncryptionMaterialsOutput as MPL_GetEncryptionMaterialsOutput, @@ -46,13 +42,9 @@ from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError -from aws_encryption_sdk.materials_managers import ( - EncryptionMaterialsRequest, - DecryptionMaterialsRequest, -) +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey - mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) mock_encryption_materials_handler = MagicMock(__class__=EncryptionMaterialsFromMPL) mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index 92a8c95df..96237998a 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -13,15 +13,12 @@ """Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" import pytest -from mock import MagicMock, patch, PropertyMock +from mock import MagicMock, PropertyMock, patch from typing import Dict, List, Set import aws_encryption_sdk.materials_managers.mpl.materials -from aws_encryption_sdk.materials_managers.mpl.materials import ( - EncryptionMaterialsFromMPL, - DecryptionMaterialsFromMPL, -) from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite +from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL pytestmark = [pytest.mark.unit, pytest.mark.local] @@ -36,10 +33,7 @@ mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) -from aws_encryption_sdk.materials_managers import ( - EncryptionMaterialsRequest, - DecryptionMaterialsRequest, -) +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) mock_encryption_materials_handler = MagicMock(__class__=EncryptionMaterialsFromMPL) From 21f361462ec2542056ffcd25ae08bffdb21a7a8d Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:21:52 -0800 Subject: [PATCH 120/172] fixes --- test/mpl/__init__.py | 2 +- .../unit/test_material_managers_mpl_cmm.py | 28 +++++++++---------- .../test_material_managers_mpl_materials.py | 15 ++++------ 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/test/mpl/__init__.py b/test/mpl/__init__.py index b976c1308..2a6c71715 100644 --- a/test/mpl/__init__.py +++ b/test/mpl/__init__.py @@ -10,4 +10,4 @@ # 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. -"""Module containing tests that REQUIRE the aws-cryptographic-material-providers library to run.""" \ No newline at end of file +"""Module containing tests that REQUIRE the aws-cryptographic-material-providers library to run.""" diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index eb795b7c2..a67c3e5c5 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -13,15 +13,6 @@ """Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" import pytest -from mock import MagicMock, patch - -from aws_encryption_sdk.identifiers import CommitmentPolicy -from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL -from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL - -pytestmark = [pytest.mark.unit, pytest.mark.local] - - from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException from aws_cryptographic_materialproviders.mpl.models import ( AlgorithmSuiteIdESDK as MPL_AlgorithmSuiteIdESDK, @@ -35,20 +26,27 @@ from aws_cryptographic_materialproviders.mpl.references import ( ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, ) - -mock_mpl_cmm = MagicMock(__class__=MPL_ICryptographicMaterialsManager) -mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) -mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) - +from mock import MagicMock, patch from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.identifiers import CommitmentPolicy from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest +from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL +from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey +pytestmark = [pytest.mark.unit, pytest.mark.local] + + mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) -mock_encryption_materials_handler = MagicMock(__class__=EncryptionMaterialsFromMPL) mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) + +mock_mpl_cmm = MagicMock(__class__=MPL_ICryptographicMaterialsManager) +mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) +mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) + + mock_edk = MagicMock(__class__=Native_EncryptedDataKey) mock_mpl_key_provider_id = MagicMock(__class__=str) mock_edk.key_provider.provider_id = mock_mpl_key_provider_id diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index 96237998a..cb3ca7397 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -13,28 +13,25 @@ """Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" import pytest +from aws_cryptographic_materialproviders.mpl.models import ( + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, +) from mock import MagicMock, PropertyMock, patch from typing import Dict, List, Set import aws_encryption_sdk.materials_managers.mpl.materials from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL pytestmark = [pytest.mark.unit, pytest.mark.local] -from aws_cryptographic_materialproviders.mpl.models import ( - DecryptionMaterials as MPL_DecryptionMaterials, - EncryptedDataKey as MPL_EncryptedDataKey, - EncryptionMaterials as MPL_EncryptionMaterials, -) - mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) - -from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest - mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) mock_encryption_materials_handler = MagicMock(__class__=EncryptionMaterialsFromMPL) mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) From 22eabb64dec94e8eaf6ccfb7a3fb14a29fcd09eb Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:27:38 -0800 Subject: [PATCH 121/172] fix --- decrypt_oracle/.chalice/pipeline.py | 2 +- .../src/aws_encryption_sdk_decrypt_oracle/app.py | 3 ++- .../test/integration/integration_test_utils.py | 3 ++- decrypt_oracle/test/test_n_generate_test_vectors.py | 7 ++++--- examples/test/examples_test_utils.py | 2 +- examples/test/test_i_basic_encryption.py | 1 - ...basic_file_encryption_with_multiple_providers.py | 4 +--- ...i_basic_file_encryption_with_raw_key_provider.py | 1 - examples/test/test_i_data_key_caching_basic.py | 1 - examples/test/test_i_discovery_kms_provider.py | 4 +--- examples/test/test_i_mrk_aware_kms_provider.py | 4 +--- examples/test/test_i_multiple_kms_cmk.py | 4 +--- examples/test/test_i_one_kms_cmk.py | 4 +--- examples/test/test_i_one_kms_cmk_streaming_data.py | 1 - examples/test/test_i_one_kms_cmk_unsigned.py | 4 +--- examples/test/test_i_set_commitment.py | 4 +--- .../materials_managers/mpl/__init__.py | 5 ++++- .../materials_managers/mpl/cmm.py | 12 ++++++++---- .../materials_managers/mpl/materials.py | 5 ++++- src/aws_encryption_sdk/streaming_client.py | 4 ++-- test/mpl/__init__.py | 5 ++++- test/mpl/unit/test_material_managers_mpl_cmm.py | 5 ++++- .../unit/test_material_managers_mpl_materials.py | 7 +++++-- .../src/awses_test_vectors/internal/aws_kms.py | 5 +++-- .../src/awses_test_vectors/internal/util.py | 3 +-- .../manifests/full_message/decrypt.py | 9 ++++----- .../manifests/full_message/decrypt_generation.py | 13 ++++++------- .../manifests/full_message/encrypt.py | 7 +++---- .../src/awses_test_vectors/manifests/keys.py | 4 +--- .../src/awses_test_vectors/manifests/master_key.py | 11 +++++------ .../commands/test_i_full_message_encrypt.py | 1 - 31 files changed, 71 insertions(+), 74 deletions(-) diff --git a/decrypt_oracle/.chalice/pipeline.py b/decrypt_oracle/.chalice/pipeline.py index 9d5573646..c05df6739 100644 --- a/decrypt_oracle/.chalice/pipeline.py +++ b/decrypt_oracle/.chalice/pipeline.py @@ -2,7 +2,6 @@ import argparse import getpass import logging -from typing import Iterable import boto3 import troposphere @@ -20,6 +19,7 @@ ) from botocore.exceptions import ClientError from troposphere import GetAtt, Ref, Sub, Template, codebuild, codepipeline, iam, s3 +from typing import Iterable APPLICATION_NAME = "AwsEncryptionSdkDecryptOraclePython" PIPELINE_STACK_NAME = "{}DeployPipeline".format(APPLICATION_NAME) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py index 820b9e015..e250bb3c8 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py @@ -15,10 +15,11 @@ import logging import os +from chalice import Chalice, Response + import aws_encryption_sdk from aws_encryption_sdk.identifiers import CommitmentPolicy from aws_encryption_sdk.key_providers.kms import DiscoveryAwsKmsMasterKeyProvider -from chalice import Chalice, Response from .key_providers.counting import CountingMasterKey from .key_providers.null import NullMasterKey diff --git a/decrypt_oracle/test/integration/integration_test_utils.py b/decrypt_oracle/test/integration/integration_test_utils.py index c03b7f440..9849f1ecc 100644 --- a/decrypt_oracle/test/integration/integration_test_utils.py +++ b/decrypt_oracle/test/integration/integration_test_utils.py @@ -15,10 +15,11 @@ import json import os from collections import namedtuple + +import pytest from typing import Any, Callable, Iterable, Optional, Text import aws_encryption_sdk -import pytest from aws_encryption_sdk.identifiers import CommitmentPolicy from aws_encryption_sdk.key_providers.kms import StrictAwsKmsMasterKeyProvider diff --git a/decrypt_oracle/test/test_n_generate_test_vectors.py b/decrypt_oracle/test/test_n_generate_test_vectors.py index deb3f7c4d..ae9bb7d7d 100644 --- a/decrypt_oracle/test/test_n_generate_test_vectors.py +++ b/decrypt_oracle/test/test_n_generate_test_vectors.py @@ -15,14 +15,15 @@ import binascii import json import os + +import pytest +from aws_encryption_sdk_decrypt_oracle.key_providers.counting import CountingMasterKey +from aws_encryption_sdk_decrypt_oracle.key_providers.null import NullMasterKey from typing import Dict, Iterable, Text import aws_encryption_sdk -import pytest from aws_encryption_sdk.key_providers.base import MasterKeyProvider from aws_encryption_sdk.key_providers.kms import KMSMasterKey -from aws_encryption_sdk_decrypt_oracle.key_providers.counting import CountingMasterKey -from aws_encryption_sdk_decrypt_oracle.key_providers.null import NullMasterKey from .integration.integration_test_utils import test_vectors_filename diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index 8a51f21c8..08e8cf2f5 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -49,7 +49,7 @@ from integration_test_utils import ( # noqa pylint: disable=unused-import,import-error get_cmk_arn, - get_second_cmk_arn, get_mrk_arn, + get_second_cmk_arn, get_second_mrk_arn, ) diff --git a/examples/test/test_i_basic_encryption.py b/examples/test/test_i_basic_encryption.py index f2a4fab51..aa32d61fa 100644 --- a/examples/test/test_i_basic_encryption.py +++ b/examples/test/test_i_basic_encryption.py @@ -17,7 +17,6 @@ from ..src.basic_encryption import cycle_string from .examples_test_utils import get_cmk_arn, static_plaintext - pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py b/examples/test/test_i_basic_file_encryption_with_multiple_providers.py index 282a272ab..0792f4958 100644 --- a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py +++ b/examples/test/test_i_basic_file_encryption_with_multiple_providers.py @@ -18,9 +18,7 @@ import pytest from ..src.basic_file_encryption_with_multiple_providers import cycle_file -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py b/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py index 710c0ccac..046b7f964 100644 --- a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py +++ b/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py @@ -19,7 +19,6 @@ from ..src.basic_file_encryption_with_raw_key_provider import cycle_file from .examples_test_utils import static_plaintext - pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_data_key_caching_basic.py b/examples/test/test_i_data_key_caching_basic.py index 734c35692..7a30f4e53 100644 --- a/examples/test/test_i_data_key_caching_basic.py +++ b/examples/test/test_i_data_key_caching_basic.py @@ -16,7 +16,6 @@ from ..src.data_key_caching_basic import encrypt_with_caching from .examples_test_utils import get_cmk_arn - pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_discovery_kms_provider.py b/examples/test/test_i_discovery_kms_provider.py index e9a1c6e71..0f64cbf59 100644 --- a/examples/test/test_i_discovery_kms_provider.py +++ b/examples/test/test_i_discovery_kms_provider.py @@ -16,9 +16,7 @@ import pytest from ..src.discovery_kms_provider import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_mrk_aware_kms_provider.py b/examples/test/test_i_mrk_aware_kms_provider.py index 8e7a003f8..a90101fa8 100644 --- a/examples/test/test_i_mrk_aware_kms_provider.py +++ b/examples/test/test_i_mrk_aware_kms_provider.py @@ -15,9 +15,7 @@ import pytest from ..src.mrk_aware_kms_provider import encrypt_decrypt -from .examples_test_utils import get_mrk_arn, get_second_mrk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_mrk_arn, get_second_mrk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_multiple_kms_cmk.py b/examples/test/test_i_multiple_kms_cmk.py index 39369cbc6..2915a0fd7 100644 --- a/examples/test/test_i_multiple_kms_cmk.py +++ b/examples/test/test_i_multiple_kms_cmk.py @@ -16,9 +16,7 @@ import pytest from ..src.multiple_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, get_second_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, get_second_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk.py b/examples/test/test_i_one_kms_cmk.py index 71ce74d3d..96dd48dae 100644 --- a/examples/test/test_i_one_kms_cmk.py +++ b/examples/test/test_i_one_kms_cmk.py @@ -16,9 +16,7 @@ import pytest from ..src.one_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk_streaming_data.py b/examples/test/test_i_one_kms_cmk_streaming_data.py index b22fa4232..f0a3094d0 100644 --- a/examples/test/test_i_one_kms_cmk_streaming_data.py +++ b/examples/test/test_i_one_kms_cmk_streaming_data.py @@ -20,7 +20,6 @@ from ..src.one_kms_cmk_streaming_data import encrypt_decrypt_stream from .examples_test_utils import get_cmk_arn, static_plaintext - pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk_unsigned.py b/examples/test/test_i_one_kms_cmk_unsigned.py index 8a2758c96..41f16473d 100644 --- a/examples/test/test_i_one_kms_cmk_unsigned.py +++ b/examples/test/test_i_one_kms_cmk_unsigned.py @@ -16,9 +16,7 @@ import pytest from ..src.one_kms_cmk_unsigned import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_set_commitment.py b/examples/test/test_i_set_commitment.py index 96247334b..c14a379bf 100644 --- a/examples/test/test_i_set_commitment.py +++ b/examples/test/test_i_set_commitment.py @@ -16,9 +16,7 @@ import pytest from ..src.set_commitment import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - +from .examples_test_utils import get_cmk_arn, static_plaintext pytestmark = [pytest.mark.examples] diff --git a/src/aws_encryption_sdk/materials_managers/mpl/__init__.py b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py index 295400d76..7593a3300 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/__init__.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py @@ -10,4 +10,7 @@ # 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. -"""Modules related to the MPL's materials managers interfaces.""" +"""Modules related to the MPL's materials managers interfaces. + +The aws-cryptographic-materials-library MUST be installed to use these modules. +""" diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index 1bbd7c89a..24a10139f 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -1,4 +1,7 @@ -"""Retrieves encryption/decryption materials from the MPL.""" +"""Retrieves encryption/decryption materials from the MPL and interfaces them to EDK components. + +The aws-cryptographic-materials-library MUST be installed to use this module. +""" from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException from aws_cryptographic_materialproviders.mpl.models import ( @@ -54,9 +57,10 @@ def get_encryption_materials( :param request: Request for encryption materials """ try: - mpl_input: MPL_GetEncryptionMaterialsInput = CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials( - request - ) + mpl_input: MPL_GetEncryptionMaterialsInput = \ + CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials( + request + ) mpl_output: MPL_GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) return EncryptionMaterialsFromMPL(mpl_output.encryption_materials) except AwsCryptographicMaterialProvidersException as mpl_exception: diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index 31f7d2a65..c23e2b038 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -1,4 +1,7 @@ -"""Provides encryption/decryption materials from an underlying materials provider.""" +"""Provides encryption/decryption materials from an underlying materials provider from the MPL. + +The aws-cryptographic-materials-library MUST be installed to use this module. +""" from aws_cryptographic_materialproviders.mpl.models import ( DecryptionMaterials as MPL_DecryptionMaterials, diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 72ed4efb7..959b5ff0b 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -555,7 +555,7 @@ def _prep_message(self): # MPL verification key is PEM bytes, not DER bytes. # If the underlying CMM is from the MPL, load PEM bytes. if (_HAS_MPL - and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): self.signer = Signer.from_key_bytes( algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key, encoding=serialization.Encoding.PEM, @@ -923,7 +923,7 @@ def _read_header(self): # MPL verification key is NOT key bytes; it is bytes of the compressed point. # If the underlying CMM is from the MPL, load bytes from encoded point. if (_HAS_MPL - and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): self.verifier = Verifier.from_encoded_point( algorithm=header.algorithm, encoded_point=base64.b64encode(decryption_materials.verification_key) diff --git a/test/mpl/__init__.py b/test/mpl/__init__.py index 2a6c71715..37f482e0b 100644 --- a/test/mpl/__init__.py +++ b/test/mpl/__init__.py @@ -10,4 +10,7 @@ # 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. -"""Module containing tests that REQUIRE the aws-cryptographic-material-providers library to run.""" +"""Module testing components that use the MPL. + +The aws-cryptographic-materials-library MUST be installed to run tests in this module. +""" diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index a67c3e5c5..fa8f76410 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -10,7 +10,10 @@ # 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" +"""Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.cmm logic. + +The aws-cryptographic-materials-library MUST be installed to run tests in this module. +""" import pytest from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index cb3ca7397..60e12c634 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -10,7 +10,10 @@ # 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" +"""Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.cmm logic. + +The aws-cryptographic-materials-library MUST be installed to run tests in this module. +""" import pytest from aws_cryptographic_materialproviders.mpl.models import ( @@ -18,7 +21,7 @@ EncryptedDataKey as MPL_EncryptedDataKey, EncryptionMaterials as MPL_EncryptionMaterials, ) -from mock import MagicMock, PropertyMock, patch +from mock import MagicMock, patch from typing import Dict, List, Set import aws_encryption_sdk.materials_managers.mpl.materials diff --git a/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py b/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py index 14c109e7d..3d2088a73 100644 --- a/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py +++ b/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py @@ -15,14 +15,15 @@ from aws_encryption_sdk.identifiers import AlgorithmSuite except ImportError: from aws_encryption_sdk.identifiers import Algorithm as AlgorithmSuite + +from awses_test_vectors.internal.defaults import ENCODING + from aws_encryption_sdk.key_providers.kms import ( DiscoveryAwsKmsMasterKeyProvider, MRKAwareDiscoveryAwsKmsMasterKeyProvider, StrictAwsKmsMasterKeyProvider, ) -from awses_test_vectors.internal.defaults import ENCODING - # This lets us easily use a single boto3 client per region for all KMS master keys. KMS_MASTER_KEY_PROVIDER = DiscoveryAwsKmsMasterKeyProvider() KMS_MRK_AWARE_MASTER_KEY_PROVIDER = MRKAwareDiscoveryAwsKmsMasterKeyProvider(discovery_region="us-west-2") diff --git a/test_vector_handlers/src/awses_test_vectors/internal/util.py b/test_vector_handlers/src/awses_test_vectors/internal/util.py index da5552f13..67d4ec67f 100644 --- a/test_vector_handlers/src/awses_test_vectors/internal/util.py +++ b/test_vector_handlers/src/awses_test_vectors/internal/util.py @@ -24,12 +24,11 @@ from aws_encryption_sdk.identifiers import Algorithm as AlgorithmSuite try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import Any, Callable, Dict, Iterable, Type # noqa pylint: disable=unused-import - from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import ISINSTANCE, MANIFEST_VERSION, ) + from typing import Any, Callable, Dict, Iterable, Type # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py index c94fd1452..a53f6cc5d 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py @@ -20,11 +20,8 @@ from enum import Enum import attr -import aws_encryption_sdk import pytest import six -from aws_encryption_sdk.identifiers import CommitmentPolicy - from awses_test_vectors.internal.defaults import ENCODING from awses_test_vectors.internal.util import ( dictionary_validator, @@ -35,14 +32,16 @@ from awses_test_vectors.manifests.keys import KeysManifest from awses_test_vectors.manifests.master_key import MasterKeySpec, master_key_provider_from_master_key_specs -try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import CommitmentPolicy +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import DECRYPT_SCENARIO_SPEC, FULL_MESSAGE_DECRYPT_MANIFEST, MASTER_KEY_SPEC, ) + from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py index e407a1b65..48fc1a6b3 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py @@ -22,11 +22,6 @@ import attr import six -from aws_encryption_sdk.caches.local import LocalCryptoMaterialsCache -from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager -from aws_encryption_sdk.materials_managers.caching import CachingCryptoMaterialsManager -from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager - from awses_test_vectors.internal.defaults import ENCODING from awses_test_vectors.internal.util import ( dictionary_validator, @@ -45,6 +40,11 @@ from awses_test_vectors.manifests.full_message.encrypt import MessageEncryptionTestScenario from awses_test_vectors.manifests.keys import KeysManifest +from aws_encryption_sdk.caches.local import LocalCryptoMaterialsCache +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_encryption_sdk.materials_managers.caching import CachingCryptoMaterialsManager +from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager + try: from aws_encryption_sdk.identifiers import AlgorithmSuite except ImportError: @@ -53,12 +53,11 @@ from awses_test_vectors.manifests.master_key import MasterKeySpec, master_key_provider_from_master_key_specs try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import - from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import ENCRYPT_SCENARIO_SPEC, PLAINTEXTS_SPEC, ) + from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py index c77fed1ce..2e88c8a52 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py @@ -19,9 +19,7 @@ import os import attr -import aws_encryption_sdk import six - from awses_test_vectors.internal.defaults import ENCODING from awses_test_vectors.internal.util import ( algorithm_suite_from_string_id, @@ -34,6 +32,8 @@ from awses_test_vectors.manifests.keys import KeysManifest from awses_test_vectors.manifests.master_key import MasterKeySpec, master_key_provider_from_master_key_specs +import aws_encryption_sdk + try: from aws_encryption_sdk.identifiers import AlgorithmSuite, CommitmentPolicy except ImportError: @@ -41,12 +41,11 @@ try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import - from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import ENCRYPT_SCENARIO_SPEC, PLAINTEXTS_SPEC, ) + from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/keys.py b/test_vector_handlers/src/awses_test_vectors/manifests/keys.py index cba6b7e25..546dbb489 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/keys.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/keys.py @@ -19,14 +19,11 @@ import attr import six - from awses_test_vectors.internal.aws_kms import arn_from_key_id from awses_test_vectors.internal.defaults import ENCODING from awses_test_vectors.internal.util import dictionary_validator, membership_validator, validate_manifest_type try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import Dict, Iterable, Optional, cast # noqa pylint: disable=unused-import - from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import AWS_KMS_KEY_SPEC, KEY_SPEC, @@ -34,6 +31,7 @@ MANIFEST_VERSION, MANUAL_KEY_SPEC, ) + from typing import Dict, Iterable, Optional, cast # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py b/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py index a1a7ae4af..8b00870f1 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py @@ -17,6 +17,10 @@ """ import attr import six +from awses_test_vectors.internal.aws_kms import KMS_MASTER_KEY_PROVIDER, KMS_MRK_AWARE_MASTER_KEY_PROVIDER +from awses_test_vectors.internal.util import membership_validator +from awses_test_vectors.manifests.keys import KeysManifest, KeySpec # noqa pylint: disable=unused-import + from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm from aws_encryption_sdk.key_providers.base import MasterKeyProvider # noqa pylint: disable=unused-import from aws_encryption_sdk.key_providers.kms import ( # noqa pylint: disable=unused-import @@ -26,10 +30,6 @@ ) from aws_encryption_sdk.key_providers.raw import RawMasterKey -from awses_test_vectors.internal.aws_kms import KMS_MASTER_KEY_PROVIDER, KMS_MRK_AWARE_MASTER_KEY_PROVIDER -from awses_test_vectors.internal.util import membership_validator -from awses_test_vectors.manifests.keys import KeysManifest, KeySpec # noqa pylint: disable=unused-import - try: from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey except ImportError: @@ -37,9 +37,8 @@ try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import Iterable # noqa pylint: disable=unused-import - from awses_test_vectors.internal.mypy_types import MASTER_KEY_SPEC # noqa pylint: disable=unused-import + from typing import Iterable # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py b/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py index 6305a15da..6928caeba 100644 --- a/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py +++ b/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py @@ -14,7 +14,6 @@ Integration tests for ``awses_test_vectors.commands``. """ import pytest - from awses_test_vectors.commands import full_message_decrypt, full_message_decrypt_generate, full_message_encrypt from ..integration_test_utils import ( # noqa pylint: disable=unused-import From ac0ceb3e60d9b4b8ff3c3ae44b7ff2b9b0a50af2 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:29:19 -0800 Subject: [PATCH 122/172] fix --- src/aws_encryption_sdk/materials_managers/mpl/cmm.py | 4 +++- src/aws_encryption_sdk/materials_managers/mpl/materials.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index 24a10139f..1913505c4 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -2,7 +2,8 @@ The aws-cryptographic-materials-library MUST be installed to use this module. """ - +# pylint should pass even if the MPL isn't installed +# noqa pylint: disable=import-error from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException from aws_cryptographic_materialproviders.mpl.models import ( AlgorithmSuiteIdESDK as MPL_AlgorithmSuiteIdESDK, @@ -16,6 +17,7 @@ from aws_cryptographic_materialproviders.mpl.references import ( ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, ) +# noqa pylint: enable=import-error from typing import List from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index c23e2b038..faa47cb46 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -2,12 +2,14 @@ The aws-cryptographic-materials-library MUST be installed to use this module. """ - +# pylint should pass even if the MPL isn't installed +# noqa pylint: disable=import-error from aws_cryptographic_materialproviders.mpl.models import ( DecryptionMaterials as MPL_DecryptionMaterials, EncryptedDataKey as MPL_EncryptedDataKey, EncryptionMaterials as MPL_EncryptionMaterials, ) +# noqa pylint: enable=import-error from typing import Dict, List, Set from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite From 2fd88584be77ed1be902e56ff473208e674b2d1d Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:32:01 -0800 Subject: [PATCH 123/172] oops --- src/aws_encryption_sdk/materials_managers/mpl/cmm.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index 1913505c4..760808bfe 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -3,7 +3,8 @@ The aws-cryptographic-materials-library MUST be installed to use this module. """ # pylint should pass even if the MPL isn't installed -# noqa pylint: disable=import-error +# Also thinks these imports aren't used if it can't import them +# noqa pylint: disable=import-error,unused-import from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException from aws_cryptographic_materialproviders.mpl.models import ( AlgorithmSuiteIdESDK as MPL_AlgorithmSuiteIdESDK, @@ -17,7 +18,7 @@ from aws_cryptographic_materialproviders.mpl.references import ( ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, ) -# noqa pylint: enable=import-error +# noqa pylint: enable=import-error,unused-import from typing import List from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError From 51c6a9caad271e1288ae06e07355c8b2cc6c0b85 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:33:06 -0800 Subject: [PATCH 124/172] revert --- decrypt_oracle/.chalice/pipeline.py | 2 +- .../src/aws_encryption_sdk_decrypt_oracle/app.py | 3 +-- .../test/integration/integration_test_utils.py | 3 +-- decrypt_oracle/test/test_n_generate_test_vectors.py | 7 +++---- examples/test/examples_test_utils.py | 2 +- examples/test/test_i_basic_encryption.py | 1 + ...basic_file_encryption_with_multiple_providers.py | 4 +++- ...i_basic_file_encryption_with_raw_key_provider.py | 1 + examples/test/test_i_data_key_caching_basic.py | 1 + examples/test/test_i_discovery_kms_provider.py | 4 +++- examples/test/test_i_mrk_aware_kms_provider.py | 4 +++- examples/test/test_i_multiple_kms_cmk.py | 4 +++- examples/test/test_i_one_kms_cmk.py | 4 +++- examples/test/test_i_one_kms_cmk_streaming_data.py | 1 + examples/test/test_i_one_kms_cmk_unsigned.py | 4 +++- examples/test/test_i_set_commitment.py | 4 +++- .../materials_managers/mpl/__init__.py | 5 +---- .../materials_managers/mpl/cmm.py | 7 +++---- .../materials_managers/mpl/materials.py | 5 +++++ src/aws_encryption_sdk/streaming_client.py | 4 ++-- test/mpl/__init__.py | 5 +---- test/mpl/unit/test_material_managers_mpl_cmm.py | 5 +---- .../unit/test_material_managers_mpl_materials.py | 7 ++----- .../src/awses_test_vectors/internal/aws_kms.py | 5 ++--- .../src/awses_test_vectors/internal/util.py | 3 ++- .../manifests/full_message/decrypt.py | 9 +++++---- .../manifests/full_message/decrypt_generation.py | 13 +++++++------ .../manifests/full_message/encrypt.py | 7 ++++--- .../src/awses_test_vectors/manifests/keys.py | 4 +++- .../src/awses_test_vectors/manifests/master_key.py | 11 ++++++----- .../commands/test_i_full_message_encrypt.py | 1 + 31 files changed, 77 insertions(+), 63 deletions(-) diff --git a/decrypt_oracle/.chalice/pipeline.py b/decrypt_oracle/.chalice/pipeline.py index c05df6739..9d5573646 100644 --- a/decrypt_oracle/.chalice/pipeline.py +++ b/decrypt_oracle/.chalice/pipeline.py @@ -2,6 +2,7 @@ import argparse import getpass import logging +from typing import Iterable import boto3 import troposphere @@ -19,7 +20,6 @@ ) from botocore.exceptions import ClientError from troposphere import GetAtt, Ref, Sub, Template, codebuild, codepipeline, iam, s3 -from typing import Iterable APPLICATION_NAME = "AwsEncryptionSdkDecryptOraclePython" PIPELINE_STACK_NAME = "{}DeployPipeline".format(APPLICATION_NAME) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py index e250bb3c8..820b9e015 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py @@ -15,11 +15,10 @@ import logging import os -from chalice import Chalice, Response - import aws_encryption_sdk from aws_encryption_sdk.identifiers import CommitmentPolicy from aws_encryption_sdk.key_providers.kms import DiscoveryAwsKmsMasterKeyProvider +from chalice import Chalice, Response from .key_providers.counting import CountingMasterKey from .key_providers.null import NullMasterKey diff --git a/decrypt_oracle/test/integration/integration_test_utils.py b/decrypt_oracle/test/integration/integration_test_utils.py index 9849f1ecc..c03b7f440 100644 --- a/decrypt_oracle/test/integration/integration_test_utils.py +++ b/decrypt_oracle/test/integration/integration_test_utils.py @@ -15,11 +15,10 @@ import json import os from collections import namedtuple - -import pytest from typing import Any, Callable, Iterable, Optional, Text import aws_encryption_sdk +import pytest from aws_encryption_sdk.identifiers import CommitmentPolicy from aws_encryption_sdk.key_providers.kms import StrictAwsKmsMasterKeyProvider diff --git a/decrypt_oracle/test/test_n_generate_test_vectors.py b/decrypt_oracle/test/test_n_generate_test_vectors.py index ae9bb7d7d..deb3f7c4d 100644 --- a/decrypt_oracle/test/test_n_generate_test_vectors.py +++ b/decrypt_oracle/test/test_n_generate_test_vectors.py @@ -15,15 +15,14 @@ import binascii import json import os - -import pytest -from aws_encryption_sdk_decrypt_oracle.key_providers.counting import CountingMasterKey -from aws_encryption_sdk_decrypt_oracle.key_providers.null import NullMasterKey from typing import Dict, Iterable, Text import aws_encryption_sdk +import pytest from aws_encryption_sdk.key_providers.base import MasterKeyProvider from aws_encryption_sdk.key_providers.kms import KMSMasterKey +from aws_encryption_sdk_decrypt_oracle.key_providers.counting import CountingMasterKey +from aws_encryption_sdk_decrypt_oracle.key_providers.null import NullMasterKey from .integration.integration_test_utils import test_vectors_filename diff --git a/examples/test/examples_test_utils.py b/examples/test/examples_test_utils.py index 08e8cf2f5..8a51f21c8 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/examples_test_utils.py @@ -49,7 +49,7 @@ from integration_test_utils import ( # noqa pylint: disable=unused-import,import-error get_cmk_arn, - get_mrk_arn, get_second_cmk_arn, + get_mrk_arn, get_second_mrk_arn, ) diff --git a/examples/test/test_i_basic_encryption.py b/examples/test/test_i_basic_encryption.py index aa32d61fa..f2a4fab51 100644 --- a/examples/test/test_i_basic_encryption.py +++ b/examples/test/test_i_basic_encryption.py @@ -17,6 +17,7 @@ from ..src.basic_encryption import cycle_string from .examples_test_utils import get_cmk_arn, static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py b/examples/test/test_i_basic_file_encryption_with_multiple_providers.py index 0792f4958..282a272ab 100644 --- a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py +++ b/examples/test/test_i_basic_file_encryption_with_multiple_providers.py @@ -18,7 +18,9 @@ import pytest from ..src.basic_file_encryption_with_multiple_providers import cycle_file -from .examples_test_utils import get_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py b/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py index 046b7f964..710c0ccac 100644 --- a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py +++ b/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py @@ -19,6 +19,7 @@ from ..src.basic_file_encryption_with_raw_key_provider import cycle_file from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_data_key_caching_basic.py b/examples/test/test_i_data_key_caching_basic.py index 7a30f4e53..734c35692 100644 --- a/examples/test/test_i_data_key_caching_basic.py +++ b/examples/test/test_i_data_key_caching_basic.py @@ -16,6 +16,7 @@ from ..src.data_key_caching_basic import encrypt_with_caching from .examples_test_utils import get_cmk_arn + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_discovery_kms_provider.py b/examples/test/test_i_discovery_kms_provider.py index 0f64cbf59..e9a1c6e71 100644 --- a/examples/test/test_i_discovery_kms_provider.py +++ b/examples/test/test_i_discovery_kms_provider.py @@ -16,7 +16,9 @@ import pytest from ..src.discovery_kms_provider import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_mrk_aware_kms_provider.py b/examples/test/test_i_mrk_aware_kms_provider.py index a90101fa8..8e7a003f8 100644 --- a/examples/test/test_i_mrk_aware_kms_provider.py +++ b/examples/test/test_i_mrk_aware_kms_provider.py @@ -15,7 +15,9 @@ import pytest from ..src.mrk_aware_kms_provider import encrypt_decrypt -from .examples_test_utils import get_mrk_arn, get_second_mrk_arn, static_plaintext +from .examples_test_utils import get_mrk_arn, get_second_mrk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_multiple_kms_cmk.py b/examples/test/test_i_multiple_kms_cmk.py index 2915a0fd7..39369cbc6 100644 --- a/examples/test/test_i_multiple_kms_cmk.py +++ b/examples/test/test_i_multiple_kms_cmk.py @@ -16,7 +16,9 @@ import pytest from ..src.multiple_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, get_second_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn, get_second_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk.py b/examples/test/test_i_one_kms_cmk.py index 96dd48dae..71ce74d3d 100644 --- a/examples/test/test_i_one_kms_cmk.py +++ b/examples/test/test_i_one_kms_cmk.py @@ -16,7 +16,9 @@ import pytest from ..src.one_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk_streaming_data.py b/examples/test/test_i_one_kms_cmk_streaming_data.py index f0a3094d0..b22fa4232 100644 --- a/examples/test/test_i_one_kms_cmk_streaming_data.py +++ b/examples/test/test_i_one_kms_cmk_streaming_data.py @@ -20,6 +20,7 @@ from ..src.one_kms_cmk_streaming_data import encrypt_decrypt_stream from .examples_test_utils import get_cmk_arn, static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_one_kms_cmk_unsigned.py b/examples/test/test_i_one_kms_cmk_unsigned.py index 41f16473d..8a2758c96 100644 --- a/examples/test/test_i_one_kms_cmk_unsigned.py +++ b/examples/test/test_i_one_kms_cmk_unsigned.py @@ -16,7 +16,9 @@ import pytest from ..src.one_kms_cmk_unsigned import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/examples/test/test_i_set_commitment.py b/examples/test/test_i_set_commitment.py index c14a379bf..96247334b 100644 --- a/examples/test/test_i_set_commitment.py +++ b/examples/test/test_i_set_commitment.py @@ -16,7 +16,9 @@ import pytest from ..src.set_commitment import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, static_plaintext +from .examples_test_utils import get_cmk_arn +from .examples_test_utils import static_plaintext + pytestmark = [pytest.mark.examples] diff --git a/src/aws_encryption_sdk/materials_managers/mpl/__init__.py b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py index 7593a3300..295400d76 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/__init__.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py @@ -10,7 +10,4 @@ # 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. -"""Modules related to the MPL's materials managers interfaces. - -The aws-cryptographic-materials-library MUST be installed to use these modules. -""" +"""Modules related to the MPL's materials managers interfaces.""" diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index 760808bfe..ead7c48f1 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -60,10 +60,9 @@ def get_encryption_materials( :param request: Request for encryption materials """ try: - mpl_input: MPL_GetEncryptionMaterialsInput = \ - CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials( - request - ) + mpl_input: MPL_GetEncryptionMaterialsInput = CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials( + request + ) mpl_output: MPL_GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) return EncryptionMaterialsFromMPL(mpl_output.encryption_materials) except AwsCryptographicMaterialProvidersException as mpl_exception: diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index faa47cb46..a82a3c372 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -1,9 +1,14 @@ +<<<<<<< HEAD """Provides encryption/decryption materials from an underlying materials provider from the MPL. The aws-cryptographic-materials-library MUST be installed to use this module. """ # pylint should pass even if the MPL isn't installed # noqa pylint: disable=import-error +======= +"""Provides encryption/decryption materials from an underlying materials provider.""" + +>>>>>>> parent of 22eabb6 (fix) from aws_cryptographic_materialproviders.mpl.models import ( DecryptionMaterials as MPL_DecryptionMaterials, EncryptedDataKey as MPL_EncryptedDataKey, diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 959b5ff0b..72ed4efb7 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -555,7 +555,7 @@ def _prep_message(self): # MPL verification key is PEM bytes, not DER bytes. # If the underlying CMM is from the MPL, load PEM bytes. if (_HAS_MPL - and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): self.signer = Signer.from_key_bytes( algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key, encoding=serialization.Encoding.PEM, @@ -923,7 +923,7 @@ def _read_header(self): # MPL verification key is NOT key bytes; it is bytes of the compressed point. # If the underlying CMM is from the MPL, load bytes from encoded point. if (_HAS_MPL - and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): self.verifier = Verifier.from_encoded_point( algorithm=header.algorithm, encoded_point=base64.b64encode(decryption_materials.verification_key) diff --git a/test/mpl/__init__.py b/test/mpl/__init__.py index 37f482e0b..2a6c71715 100644 --- a/test/mpl/__init__.py +++ b/test/mpl/__init__.py @@ -10,7 +10,4 @@ # 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. -"""Module testing components that use the MPL. - -The aws-cryptographic-materials-library MUST be installed to run tests in this module. -""" +"""Module containing tests that REQUIRE the aws-cryptographic-material-providers library to run.""" diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index fa8f76410..a67c3e5c5 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -10,10 +10,7 @@ # 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic. - -The aws-cryptographic-materials-library MUST be installed to run tests in this module. -""" +"""Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" import pytest from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index 60e12c634..cb3ca7397 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -10,10 +10,7 @@ # 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic. - -The aws-cryptographic-materials-library MUST be installed to run tests in this module. -""" +"""Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" import pytest from aws_cryptographic_materialproviders.mpl.models import ( @@ -21,7 +18,7 @@ EncryptedDataKey as MPL_EncryptedDataKey, EncryptionMaterials as MPL_EncryptionMaterials, ) -from mock import MagicMock, patch +from mock import MagicMock, PropertyMock, patch from typing import Dict, List, Set import aws_encryption_sdk.materials_managers.mpl.materials diff --git a/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py b/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py index 3d2088a73..14c109e7d 100644 --- a/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py +++ b/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py @@ -15,15 +15,14 @@ from aws_encryption_sdk.identifiers import AlgorithmSuite except ImportError: from aws_encryption_sdk.identifiers import Algorithm as AlgorithmSuite - -from awses_test_vectors.internal.defaults import ENCODING - from aws_encryption_sdk.key_providers.kms import ( DiscoveryAwsKmsMasterKeyProvider, MRKAwareDiscoveryAwsKmsMasterKeyProvider, StrictAwsKmsMasterKeyProvider, ) +from awses_test_vectors.internal.defaults import ENCODING + # This lets us easily use a single boto3 client per region for all KMS master keys. KMS_MASTER_KEY_PROVIDER = DiscoveryAwsKmsMasterKeyProvider() KMS_MRK_AWARE_MASTER_KEY_PROVIDER = MRKAwareDiscoveryAwsKmsMasterKeyProvider(discovery_region="us-west-2") diff --git a/test_vector_handlers/src/awses_test_vectors/internal/util.py b/test_vector_handlers/src/awses_test_vectors/internal/util.py index 67d4ec67f..da5552f13 100644 --- a/test_vector_handlers/src/awses_test_vectors/internal/util.py +++ b/test_vector_handlers/src/awses_test_vectors/internal/util.py @@ -24,11 +24,12 @@ from aws_encryption_sdk.identifiers import Algorithm as AlgorithmSuite try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Any, Callable, Dict, Iterable, Type # noqa pylint: disable=unused-import + from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import ISINSTANCE, MANIFEST_VERSION, ) - from typing import Any, Callable, Dict, Iterable, Type # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py index a53f6cc5d..c94fd1452 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py @@ -20,8 +20,11 @@ from enum import Enum import attr +import aws_encryption_sdk import pytest import six +from aws_encryption_sdk.identifiers import CommitmentPolicy + from awses_test_vectors.internal.defaults import ENCODING from awses_test_vectors.internal.util import ( dictionary_validator, @@ -32,16 +35,14 @@ from awses_test_vectors.manifests.keys import KeysManifest from awses_test_vectors.manifests.master_key import MasterKeySpec, master_key_provider_from_master_key_specs -import aws_encryption_sdk -from aws_encryption_sdk.identifiers import CommitmentPolicy - try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import + from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import DECRYPT_SCENARIO_SPEC, FULL_MESSAGE_DECRYPT_MANIFEST, MASTER_KEY_SPEC, ) - from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py index 48fc1a6b3..e407a1b65 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py @@ -22,6 +22,11 @@ import attr import six +from aws_encryption_sdk.caches.local import LocalCryptoMaterialsCache +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_encryption_sdk.materials_managers.caching import CachingCryptoMaterialsManager +from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager + from awses_test_vectors.internal.defaults import ENCODING from awses_test_vectors.internal.util import ( dictionary_validator, @@ -40,11 +45,6 @@ from awses_test_vectors.manifests.full_message.encrypt import MessageEncryptionTestScenario from awses_test_vectors.manifests.keys import KeysManifest -from aws_encryption_sdk.caches.local import LocalCryptoMaterialsCache -from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager -from aws_encryption_sdk.materials_managers.caching import CachingCryptoMaterialsManager -from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager - try: from aws_encryption_sdk.identifiers import AlgorithmSuite except ImportError: @@ -53,11 +53,12 @@ from awses_test_vectors.manifests.master_key import MasterKeySpec, master_key_provider_from_master_key_specs try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import + from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import ENCRYPT_SCENARIO_SPEC, PLAINTEXTS_SPEC, ) - from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py index 2e88c8a52..c77fed1ce 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py @@ -19,7 +19,9 @@ import os import attr +import aws_encryption_sdk import six + from awses_test_vectors.internal.defaults import ENCODING from awses_test_vectors.internal.util import ( algorithm_suite_from_string_id, @@ -32,8 +34,6 @@ from awses_test_vectors.manifests.keys import KeysManifest from awses_test_vectors.manifests.master_key import MasterKeySpec, master_key_provider_from_master_key_specs -import aws_encryption_sdk - try: from aws_encryption_sdk.identifiers import AlgorithmSuite, CommitmentPolicy except ImportError: @@ -41,11 +41,12 @@ try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import + from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import ENCRYPT_SCENARIO_SPEC, PLAINTEXTS_SPEC, ) - from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/keys.py b/test_vector_handlers/src/awses_test_vectors/manifests/keys.py index 546dbb489..cba6b7e25 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/keys.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/keys.py @@ -19,11 +19,14 @@ import attr import six + from awses_test_vectors.internal.aws_kms import arn_from_key_id from awses_test_vectors.internal.defaults import ENCODING from awses_test_vectors.internal.util import dictionary_validator, membership_validator, validate_manifest_type try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Dict, Iterable, Optional, cast # noqa pylint: disable=unused-import + from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import AWS_KMS_KEY_SPEC, KEY_SPEC, @@ -31,7 +34,6 @@ MANIFEST_VERSION, MANUAL_KEY_SPEC, ) - from typing import Dict, Iterable, Optional, cast # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py b/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py index 8b00870f1..a1a7ae4af 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py @@ -17,10 +17,6 @@ """ import attr import six -from awses_test_vectors.internal.aws_kms import KMS_MASTER_KEY_PROVIDER, KMS_MRK_AWARE_MASTER_KEY_PROVIDER -from awses_test_vectors.internal.util import membership_validator -from awses_test_vectors.manifests.keys import KeysManifest, KeySpec # noqa pylint: disable=unused-import - from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm from aws_encryption_sdk.key_providers.base import MasterKeyProvider # noqa pylint: disable=unused-import from aws_encryption_sdk.key_providers.kms import ( # noqa pylint: disable=unused-import @@ -30,6 +26,10 @@ ) from aws_encryption_sdk.key_providers.raw import RawMasterKey +from awses_test_vectors.internal.aws_kms import KMS_MASTER_KEY_PROVIDER, KMS_MRK_AWARE_MASTER_KEY_PROVIDER +from awses_test_vectors.internal.util import membership_validator +from awses_test_vectors.manifests.keys import KeysManifest, KeySpec # noqa pylint: disable=unused-import + try: from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey except ImportError: @@ -37,8 +37,9 @@ try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from awses_test_vectors.internal.mypy_types import MASTER_KEY_SPEC # noqa pylint: disable=unused-import from typing import Iterable # noqa pylint: disable=unused-import + + from awses_test_vectors.internal.mypy_types import MASTER_KEY_SPEC # noqa pylint: disable=unused-import except ImportError: # pragma: no cover # We only actually need these imports when running the mypy checks pass diff --git a/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py b/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py index 6928caeba..6305a15da 100644 --- a/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py +++ b/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py @@ -14,6 +14,7 @@ Integration tests for ``awses_test_vectors.commands``. """ import pytest + from awses_test_vectors.commands import full_message_decrypt, full_message_decrypt_generate, full_message_encrypt from ..integration_test_utils import ( # noqa pylint: disable=unused-import From 800f9de0de601bb647ba2ab57e7df3d4eedfa795 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:33:18 -0800 Subject: [PATCH 125/172] revert --- src/aws_encryption_sdk/materials_managers/mpl/materials.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index a82a3c372..faa47cb46 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -1,14 +1,9 @@ -<<<<<<< HEAD """Provides encryption/decryption materials from an underlying materials provider from the MPL. The aws-cryptographic-materials-library MUST be installed to use this module. """ # pylint should pass even if the MPL isn't installed # noqa pylint: disable=import-error -======= -"""Provides encryption/decryption materials from an underlying materials provider.""" - ->>>>>>> parent of 22eabb6 (fix) from aws_cryptographic_materialproviders.mpl.models import ( DecryptionMaterials as MPL_DecryptionMaterials, EncryptedDataKey as MPL_EncryptedDataKey, From ebcb7590c472f82affa86ecc793a5e8a3e494a8b Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:35:14 -0800 Subject: [PATCH 126/172] fix --- src/aws_encryption_sdk/materials_managers/mpl/__init__.py | 5 ++++- src/aws_encryption_sdk/materials_managers/mpl/cmm.py | 7 ++++--- src/aws_encryption_sdk/streaming_client.py | 4 ++-- test/mpl/__init__.py | 5 ++++- test/mpl/unit/test_material_managers_mpl_cmm.py | 5 ++++- test/mpl/unit/test_material_managers_mpl_materials.py | 7 +++++-- 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/__init__.py b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py index 295400d76..7593a3300 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/__init__.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py @@ -10,4 +10,7 @@ # 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. -"""Modules related to the MPL's materials managers interfaces.""" +"""Modules related to the MPL's materials managers interfaces. + +The aws-cryptographic-materials-library MUST be installed to use these modules. +""" diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index ead7c48f1..760808bfe 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -60,9 +60,10 @@ def get_encryption_materials( :param request: Request for encryption materials """ try: - mpl_input: MPL_GetEncryptionMaterialsInput = CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials( - request - ) + mpl_input: MPL_GetEncryptionMaterialsInput = \ + CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials( + request + ) mpl_output: MPL_GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) return EncryptionMaterialsFromMPL(mpl_output.encryption_materials) except AwsCryptographicMaterialProvidersException as mpl_exception: diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 72ed4efb7..959b5ff0b 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -555,7 +555,7 @@ def _prep_message(self): # MPL verification key is PEM bytes, not DER bytes. # If the underlying CMM is from the MPL, load PEM bytes. if (_HAS_MPL - and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): self.signer = Signer.from_key_bytes( algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key, encoding=serialization.Encoding.PEM, @@ -923,7 +923,7 @@ def _read_header(self): # MPL verification key is NOT key bytes; it is bytes of the compressed point. # If the underlying CMM is from the MPL, load bytes from encoded point. if (_HAS_MPL - and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): self.verifier = Verifier.from_encoded_point( algorithm=header.algorithm, encoded_point=base64.b64encode(decryption_materials.verification_key) diff --git a/test/mpl/__init__.py b/test/mpl/__init__.py index 2a6c71715..d3f78d0bf 100644 --- a/test/mpl/__init__.py +++ b/test/mpl/__init__.py @@ -10,4 +10,7 @@ # 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. -"""Module containing tests that REQUIRE the aws-cryptographic-material-providers library to run.""" +"""Module testing components that use the MPL. + +The aws-cryptographic-materials-library MUST be installed to run tests in this module. +""" \ No newline at end of file diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index a67c3e5c5..fa8f76410 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -10,7 +10,10 @@ # 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" +"""Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.cmm logic. + +The aws-cryptographic-materials-library MUST be installed to run tests in this module. +""" import pytest from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index cb3ca7397..6c992ff24 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -10,7 +10,10 @@ # 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 to validate aws_encryption_sdk.materials_managers.mpl.cmm logic.""" +"""Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.materials logic. + +The aws-cryptographic-materials-library MUST be installed to run tests in this module. +""" import pytest from aws_cryptographic_materialproviders.mpl.models import ( @@ -18,7 +21,7 @@ EncryptedDataKey as MPL_EncryptedDataKey, EncryptionMaterials as MPL_EncryptionMaterials, ) -from mock import MagicMock, PropertyMock, patch +from mock import MagicMock, patch from typing import Dict, List, Set import aws_encryption_sdk.materials_managers.mpl.materials From cf26ca3fdb0b2cccbe011d8c2071453fe06bbb09 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:45:15 -0800 Subject: [PATCH 127/172] fix --- .../materials_managers/mpl/cmm.py | 2 +- .../unit/test_material_managers_mpl_cmm.py | 68 ++++++++++++------- .../test_material_managers_mpl_materials.py | 18 ++--- 3 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index 760808bfe..e0879f3fb 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -2,6 +2,7 @@ The aws-cryptographic-materials-library MUST be installed to use this module. """ +from typing import List # pylint should pass even if the MPL isn't installed # Also thinks these imports aren't used if it can't import them # noqa pylint: disable=import-error,unused-import @@ -19,7 +20,6 @@ ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, ) # noqa pylint: enable=import-error,unused-import -from typing import List from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError from aws_encryption_sdk.identifiers import CommitmentPolicy diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index fa8f76410..060f19f95 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -76,16 +76,17 @@ def test_GIVEN_invalid_mpl_cmm_WHEN_create_CryptoMaterialsManagerFromMPL_THEN_ra @patch.object(mock_mpl_cmm, "get_encryption_materials") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_to_mpl_get_encryption_materials") def test_GIVEN_valid_request_WHEN_get_encryption_materials_THEN_return_EncryptionMaterialsFromMPL( mock_native_to_mpl_get_encryption_materials, mock_get_encryption_materials, ): - + # Given: _native_to_mpl_get_encryption_materials creates a MPL_GetEncryptionMaterialsInput mock_get_encryption_materials_input = MagicMock(__class__=MPL_GetEncryptionMaterialsInput) mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input - + # Given: mpl_cmm.get_encryption_materials returns mock MPL encryption materials mock_get_encryption_materials_output = MagicMock(__class__=MPL_GetEncryptionMaterialsOutput) mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials @@ -104,7 +105,8 @@ def test_GIVEN_valid_request_WHEN_get_encryption_materials_THEN_return_Encryptio mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_to_mpl_commmitment_policy") def test_GIVEN_mpl_cmm_raises_MPLException_WHEN_get_encryption_materials_THEN_raise_ESDKException( _ ): @@ -112,13 +114,15 @@ def test_GIVEN_mpl_cmm_raises_MPLException_WHEN_get_encryption_materials_THEN_ra with pytest.raises(AWSEncryptionSDKClientError): # Given: mpl_cmm.get_encryption_materials raises MPL exception with patch.object(mock_mpl_cmm, "get_encryption_materials", - side_effect=AwsCryptographicMaterialProvidersException("any")): + side_effect=AwsCryptographicMaterialProvidersException("any")): # When: get_encryption_materials cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) cmm.get_encryption_materials(mock_encryption_materials_request) -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy") -def test_GIVEN_valid_mpl_commitment_policy_WHEN_native_to_mpl_get_encryption_materials_THEN_returns_MPL_GetEncryptionMaterialsInput( + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_to_mpl_commmitment_policy") +def test_GIVEN_valid_mpl_commitment_policy_WHEN_native_to_mpl_get_encryption_materials_THEN_returns_MPL_GetEncryptionMaterialsInput( # noqa: E501 mock_mpl_commitment_policy ): # Given: commitment policy is some MPL ESDK commitment policy @@ -126,7 +130,9 @@ def test_GIVEN_valid_mpl_commitment_policy_WHEN_native_to_mpl_get_encryption_mat mock_mpl_commitment_policy.return_value = mock_commitment_policy # When: _native_to_mpl_get_encryption_materials - output = CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials(mock_encryption_materials_request) + output = CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials( + mock_encryption_materials_request + ) # Then: returned MPL_GetEncryptionMaterialsInput is correct assert isinstance(output, MPL_GetEncryptionMaterialsInput) @@ -135,7 +141,7 @@ def test_GIVEN_valid_mpl_commitment_policy_WHEN_native_to_mpl_get_encryption_mat assert output.max_plaintext_length == mock_encryption_materials_request.plaintext_length -def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): +def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): # noqa: E501 # Given: native FORBID_ENCRYPT_ALLOW_DECRYPT native_commitment_policy = CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT @@ -146,7 +152,8 @@ def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_ assert isinstance(output, MPL_CommitmentPolicyESDK) assert output.value == "FORBID_ENCRYPT_ALLOW_DECRYPT" -def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): + +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): # noqa: E501 # Given: native REQUIRE_ENCRYPT_ALLOW_DECRYPT native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT @@ -157,7 +164,8 @@ def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl assert isinstance(output, MPL_CommitmentPolicyESDK) assert output.value == "REQUIRE_ENCRYPT_ALLOW_DECRYPT" -def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): + +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_native_to_mpl_commmitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): # noqa: E501 # Given: native REQUIRE_ENCRYPT_REQUIRE_DECRYPT native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT @@ -168,6 +176,7 @@ def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_native_to_m assert isinstance(output, MPL_CommitmentPolicyESDK) assert output.value == "REQUIRE_ENCRYPT_REQUIRE_DECRYPT" + def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_native_to_mpl_commmitment_policy_THEN_raise_ValueError(): # Given: invalid native commitment policy native_commitment_policy = "not a commitment policy" @@ -177,13 +186,14 @@ def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_native_to_mpl_commmitment_poli # When: _native_to_mpl_commmitment_policy CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) + @patch.object(mock_mpl_cmm, "decrypt_materials") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._create_mpl_decrypt_materials_input_from_request") def test_GIVEN_valid_request_WHEN_decrypt_materials_THEN_return_DecryptionMaterialsFromMPL( mock_native_to_mpl_decrypt_materials, mock_get_encryption_materials, ): - # Given: mpl_cmm.get_decryption_materials returns mock MPL decryption materials mock_decrypt_materials_output = MagicMock(__class__=MPL_GetEncryptionMaterialsOutput) mock_decrypt_materials_output.decryption_materials = mock_mpl_decrypt_materials @@ -205,7 +215,9 @@ def test_GIVEN_valid_request_WHEN_decrypt_materials_THEN_return_DecryptionMateri # Verify we actually called `decrypt_materials` mock_mpl_cmm.decrypt_materials.assert_called_once_with(mock_decrypt_materials_input) -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request") + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._create_mpl_decrypt_materials_input_from_request") def test_GIVEN_decrypt_materials_raises_MPL_Exception_WHEN_call_decrypt_materials_THEN_raise_ESDK_Exception( _ ): @@ -213,12 +225,13 @@ def test_GIVEN_decrypt_materials_raises_MPL_Exception_WHEN_call_decrypt_material with pytest.raises(AWSEncryptionSDKClientError): # Given: mpl_cmm.decrypt_materials raises MPL exception with patch.object(mock_mpl_cmm, "decrypt_materials", - side_effect=AwsCryptographicMaterialProvidersException("any")): + side_effect=AwsCryptographicMaterialProvidersException("any")): # When: decrypt_materials cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) cmm.decrypt_materials(mock_decryption_materials_request) -def test_GIVEN_valid_native_algorithm_id_WHEN_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_MPL_AlgorithmSuiteIdESDK(): + +def test_GIVEN_valid_native_algorithm_id_WHEN_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_MPL_AlgorithmSuiteIdESDK(): # noqa: E501 # Given: any native algorithm ID some_native_algorithm_id = 0x1234 # Not a real algorithm ID, but fits the format @@ -231,9 +244,12 @@ def test_GIVEN_valid_native_algorithm_id_WHEN_native_algorithm_id_to_mpl_algorit assert isinstance(mpl_output, MPL_AlgorithmSuiteIdESDK) assert mpl_output.value == "0x1234" -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id") -@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy") -def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_request_THEN_returns_MPL_MPL_DecryptMaterialsInput( + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_algorithm_id_to_mpl_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_to_mpl_commmitment_policy") +def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_request_THEN_returns_MPL_MPL_DecryptMaterialsInput( # noqa: E501 mock_mpl_commitment_policy, mock_mpl_algorithm_id, ): @@ -245,17 +261,19 @@ def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_reques mock_commitment_policy = MagicMock(__class__=MPL_CommitmentPolicyESDK) mock_mpl_commitment_policy.return_value = mock_commitment_policy - no_mock_edks = [ mock_edk ] - one_mock_edk = [ mock_edk ] - two_mock_edks = [ mock_edk, mock_edk ] + no_mock_edks = [mock_edk] + one_mock_edk = [mock_edk] + two_mock_edks = [mock_edk, mock_edk] # Given: ESK lists of various lengths - for mock_edks in [ no_mock_edks, one_mock_edk, two_mock_edks ]: + for mock_edks in [no_mock_edks, one_mock_edk, two_mock_edks]: mock_decryption_materials_request.encrypted_data_keys = mock_edks # When: _create_mpl_decrypt_materials_input_from_request - output = CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request(mock_decryption_materials_request) + output = CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request( + mock_decryption_materials_request + ) # Then: # Verify general correctness of output structure @@ -273,4 +291,4 @@ def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_reques input_edk = mock_edks[i] assert output_edk.key_provider_id == input_edk.key_provider.provider_id assert output_edk.key_provider_info == input_edk.key_provider.key_info - assert output_edk.ciphertext == input_edk.encrypted_data_key \ No newline at end of file + assert output_edk.ciphertext == input_edk.encrypted_data_key diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index 6c992ff24..a2333f267 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -22,10 +22,10 @@ EncryptionMaterials as MPL_EncryptionMaterials, ) from mock import MagicMock, patch -from typing import Dict, List, Set +from typing import Dict import aws_encryption_sdk.materials_managers.mpl.materials -from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite +from aws_encryption_sdk.identifiers import AlgorithmSuite from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL @@ -48,11 +48,11 @@ mock_edk.ciphertext = mock_mpl_ciphertext -def test_GIVEN_valid_mpl_materials_WHEN_create_EncryptionMaterialsFromMPL_THEN_return_new_CryptoMaterialsManagerFromMPL(): +def test_GIVEN_mpl_materials_WHEN_create_EncryptionMaterialsFromMPL_THEN_return_new_CryptoMaterialsManagerFromMPL(): # Given: valid mpl_materials # When: create EncryptionMaterialsFromMPL mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) - + # Then: EncryptionMaterialsFromMPL is valid assert mpl_encryption_materials.mpl_materials == mock_mpl_encryption_materials @@ -93,7 +93,7 @@ def test_GIVEN_valid_mpl_algorithm_id_WHEN_EncryptionMaterials_get_algorithm_THE # When: Get algorithm mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) output = mpl_encryption_materials.algorithm - + # Then: output is valid assert output == mock_algorithm() # property calls automatically, we need to call the mock @@ -112,12 +112,12 @@ def test_GIVEN_valid_encryption_context_WHEN_EncryptionMaterials_get_encryption_ def test_GIVEN_valid_edks_WHEN_EncryptionMaterials_get_edks_THEN_returns_edks(): - + # Given: lists of mocked EDKs of various lengths no_mock_edks = [] - one_mock_edk = [ mock_edk ] - two_mocked_edks = [ mock_edk, mock_edk ] - for mock_edks in [ no_mock_edks, one_mock_edk, two_mocked_edks ]: + one_mock_edk = [mock_edk] + two_mocked_edks = [mock_edk, mock_edk] + for mock_edks in [no_mock_edks, one_mock_edk, two_mocked_edks]: mock_mpl_encryption_materials.encrypted_data_keys = mock_edks # When: get EDKs From 7f27ebdb1cbe0a7b7039d8912ffb8deed032a5e2 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:46:38 -0800 Subject: [PATCH 128/172] fix --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 3daa40e47..952d3c24f 100644 --- a/tox.ini +++ b/tox.ini @@ -112,7 +112,7 @@ passenv = setenv = ######################################################### deps = -rdev_requirements/test-requirements.txt -commands = {[testenv:base-command]commands} test/ -m local --ignore test/unit/mpl/ +commands = {[testenv:base-command]commands} test/ -m local --ignore test/mpl/ # Collect requirements for use in upstream tests [testenv:freeze-upstream-requirements-base] @@ -144,7 +144,7 @@ commands = {[testenv:freeze-upstream-requirements-base]commands} test/upstream-r [testenv:test-upstream-requirements-base] sitepackages = False recreate = True -commands = {[testenv:base-command]commands} test/ -m local --ignore test/unit/mpl/ +commands = {[testenv:base-command]commands} test/ -m local --ignore --ignore test/mpl/ # Test frozen upstream requirements for Python 3.7 [testenv:test-upstream-requirements-py37] From 00f4721542d8529eb2ca6b0ac621b440940055be Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:48:24 -0800 Subject: [PATCH 129/172] fix --- src/aws_encryption_sdk/materials_managers/mpl/materials.py | 2 +- test/mpl/__init__.py | 2 +- tox.ini | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index faa47cb46..39aff2c3c 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -2,6 +2,7 @@ The aws-cryptographic-materials-library MUST be installed to use this module. """ +from typing import Dict, List, Set # pylint should pass even if the MPL isn't installed # noqa pylint: disable=import-error from aws_cryptographic_materialproviders.mpl.models import ( @@ -10,7 +11,6 @@ EncryptionMaterials as MPL_EncryptionMaterials, ) # noqa pylint: enable=import-error -from typing import Dict, List, Set from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite from aws_encryption_sdk.materials_managers import ( diff --git a/test/mpl/__init__.py b/test/mpl/__init__.py index d3f78d0bf..37f482e0b 100644 --- a/test/mpl/__init__.py +++ b/test/mpl/__init__.py @@ -13,4 +13,4 @@ """Module testing components that use the MPL. The aws-cryptographic-materials-library MUST be installed to run tests in this module. -""" \ No newline at end of file +""" diff --git a/tox.ini b/tox.ini index 952d3c24f..3644c973a 100644 --- a/tox.ini +++ b/tox.ini @@ -144,7 +144,7 @@ commands = {[testenv:freeze-upstream-requirements-base]commands} test/upstream-r [testenv:test-upstream-requirements-base] sitepackages = False recreate = True -commands = {[testenv:base-command]commands} test/ -m local --ignore --ignore test/mpl/ +commands = {[testenv:base-command]commands} test/ -m local --ignore test/mpl/ # Test frozen upstream requirements for Python 3.7 [testenv:test-upstream-requirements-py37] From 018b93f3ed2b5eae5bf6c2e4dc5d7837e38a34a3 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 17:55:46 -0800 Subject: [PATCH 130/172] fix --- src/aws_encryption_sdk/materials_managers/mpl/cmm.py | 7 ++++++- src/aws_encryption_sdk/materials_managers/mpl/materials.py | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index e0879f3fb..c262cf7ce 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -2,7 +2,6 @@ The aws-cryptographic-materials-library MUST be installed to use this module. """ -from typing import List # pylint should pass even if the MPL isn't installed # Also thinks these imports aren't used if it can't import them # noqa pylint: disable=import-error,unused-import @@ -21,6 +20,9 @@ ) # noqa pylint: enable=import-error,unused-import +# pylint and isort disagree on where this should go. Choose isort and disable pylint for this. +from typing import List # noqa pylint: disable=wrong-import-order + from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError from aws_encryption_sdk.identifiers import CommitmentPolicy from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest @@ -28,6 +30,9 @@ from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey +# noqa pylint: enable=import-error,unused-import + + class CryptoMaterialsManagerFromMPL(CryptoMaterialsManager): """ diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index 39aff2c3c..43579fac6 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -2,7 +2,6 @@ The aws-cryptographic-materials-library MUST be installed to use this module. """ -from typing import Dict, List, Set # pylint should pass even if the MPL isn't installed # noqa pylint: disable=import-error from aws_cryptographic_materialproviders.mpl.models import ( @@ -12,6 +11,9 @@ ) # noqa pylint: enable=import-error +# pylint and isort disagree on where this should go. Choose isort and disable pylint for this. +from typing import Dict, List, Set # noqa pylint: disable=wrong-import-order + from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite from aws_encryption_sdk.materials_managers import ( DecryptionMaterials as Native_DecryptionMaterials, From d413b65024d398510f1811c8638109ae7c886336 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 18:00:04 -0800 Subject: [PATCH 131/172] fix --- src/aws_encryption_sdk/materials_managers/mpl/cmm.py | 4 ---- src/aws_encryption_sdk/materials_managers/mpl/materials.py | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index c262cf7ce..a0119a588 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -19,7 +19,6 @@ ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, ) # noqa pylint: enable=import-error,unused-import - # pylint and isort disagree on where this should go. Choose isort and disable pylint for this. from typing import List # noqa pylint: disable=wrong-import-order @@ -30,9 +29,6 @@ from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey -# noqa pylint: enable=import-error,unused-import - - class CryptoMaterialsManagerFromMPL(CryptoMaterialsManager): """ diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index 43579fac6..4508d5545 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -9,8 +9,6 @@ EncryptedDataKey as MPL_EncryptedDataKey, EncryptionMaterials as MPL_EncryptionMaterials, ) -# noqa pylint: enable=import-error - # pylint and isort disagree on where this should go. Choose isort and disable pylint for this. from typing import Dict, List, Set # noqa pylint: disable=wrong-import-order From c4ca658d08efdac51f85c04b9aa9cd5cf37a3a60 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 23 Feb 2024 18:09:29 -0800 Subject: [PATCH 132/172] copyright --- examples/src/keyrings/__init__.py | 14 ++------------ .../src/keyrings/example_branch_key_id_supplier.py | 2 ++ examples/test/keyrings/__init__.py | 14 ++------------ .../test/keyrings/test_i_hierarchical_keyring.py | 2 ++ .../materials_managers/mpl/__init__.py | 14 ++------------ .../materials_managers/mpl/cmm.py | 2 ++ .../materials_managers/mpl/materials.py | 2 ++ test/mpl/__init__.py | 14 ++------------ test/mpl/unit/test_material_managers_mpl_cmm.py | 14 ++------------ .../unit/test_material_managers_mpl_materials.py | 14 ++------------ test/unit/test_streaming_client_mpl_import.py | 14 ++------------ 11 files changed, 22 insertions(+), 84 deletions(-) diff --git a/examples/src/keyrings/__init__.py b/examples/src/keyrings/__init__.py index e8fd618b1..120179eda 100644 --- a/examples/src/keyrings/__init__.py +++ b/examples/src/keyrings/__init__.py @@ -1,13 +1,3 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Stub module indicator to make linter configuration simpler.""" diff --git a/examples/src/keyrings/example_branch_key_id_supplier.py b/examples/src/keyrings/example_branch_key_id_supplier.py index ba9ae060c..7b390cdda 100644 --- a/examples/src/keyrings/example_branch_key_id_supplier.py +++ b/examples/src/keyrings/example_branch_key_id_supplier.py @@ -1,3 +1,5 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example implementation of a branch key ID supplier.""" from aws_cryptographic_materialproviders.mpl.models import GetBranchKeyIdInput, GetBranchKeyIdOutput diff --git a/examples/test/keyrings/__init__.py b/examples/test/keyrings/__init__.py index e8fd618b1..120179eda 100644 --- a/examples/test/keyrings/__init__.py +++ b/examples/test/keyrings/__init__.py @@ -1,13 +1,3 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Stub module indicator to make linter configuration simpler.""" diff --git a/examples/test/keyrings/test_i_hierarchical_keyring.py b/examples/test/keyrings/test_i_hierarchical_keyring.py index d80bb565d..4cae478d7 100644 --- a/examples/test/keyrings/test_i_hierarchical_keyring.py +++ b/examples/test/keyrings/test_i_hierarchical_keyring.py @@ -1,3 +1,5 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for the hierarchical keyring example.""" import pytest diff --git a/src/aws_encryption_sdk/materials_managers/mpl/__init__.py b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py index 7593a3300..be75f3566 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/__init__.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Modules related to the MPL's materials managers interfaces. The aws-cryptographic-materials-library MUST be installed to use these modules. diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index a0119a588..53a4b3505 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -1,3 +1,5 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Retrieves encryption/decryption materials from the MPL and interfaces them to EDK components. The aws-cryptographic-materials-library MUST be installed to use this module. diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index 4508d5545..dfd1bd6fc 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -1,3 +1,5 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Provides encryption/decryption materials from an underlying materials provider from the MPL. The aws-cryptographic-materials-library MUST be installed to use this module. diff --git a/test/mpl/__init__.py b/test/mpl/__init__.py index 37f482e0b..79522d342 100644 --- a/test/mpl/__init__.py +++ b/test/mpl/__init__.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Module testing components that use the MPL. The aws-cryptographic-materials-library MUST be installed to run tests in this module. diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index 060f19f95..80d6f00ee 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.cmm logic. The aws-cryptographic-materials-library MUST be installed to run tests in this module. diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index a2333f267..9e76556a2 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.materials logic. The aws-cryptographic-materials-library MUST be installed to run tests in this module. diff --git a/test/unit/test_streaming_client_mpl_import.py b/test/unit/test_streaming_client_mpl_import.py index a4ca87e2a..638b04fd6 100644 --- a/test/unit/test_streaming_client_mpl_import.py +++ b/test/unit/test_streaming_client_mpl_import.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite to validate aws_encryption_sdk.streaming_client MPL import logic.""" import pytest From d99b6667bb1a7d65a36af598889edeab2beecfc6 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 26 Feb 2024 12:40:24 -0800 Subject: [PATCH 133/172] more unit tests --- src/aws_encryption_sdk/streaming_client.py | 34 +++-- .../unit/test_crypto_authentication_signer.py | 63 +++++++-- test/unit/test_streaming_client_configs.py | 96 +++++++++++++ .../test_streaming_client_stream_decryptor.py | 132 +++++++++++++++++- .../test_streaming_client_stream_encryptor.py | 79 +++++++++++ test/unit/test_utils.py | 25 ++++ 6 files changed, 401 insertions(+), 28 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 959b5ff0b..2cfcc9a02 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -73,8 +73,9 @@ try: # pylint should pass even if the MPL isn't installed # noqa pylint: disable=import-error - from aws_cryptographic_materialproviders.mpl.client import AwsCryptographicMaterialProviders + from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig + from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput from aws_cryptographic_materialproviders.mpl.references import IKeyring _HAS_MPL = True @@ -147,9 +148,6 @@ def _has_mpl_attrs_post_init(self): """If the MPL is present in the runtime, perform MPL-specific post-init logic to validate the new object has a valid state. """ - if not hasattr(self, "keyring"): - self._no_mpl_attrs_post_init() - return if not exactly_one_arg_is_not_none(self.materials_manager, self.key_provider, self.keyring): raise TypeError("Exactly one of keyring, materials_manager, or key_provider must be provided") if self.materials_manager is None: @@ -159,21 +157,21 @@ def _has_mpl_attrs_post_init(self): master_key_provider=self.key_provider ) elif self.keyring is not None: - # No CMM, provided MPL keyring => create MPL's DefaultCryptographicMaterialsManager - if not isinstance(self.keyring, IKeyring): - raise ValueError(f"Argument provided to keyring MUST be a {IKeyring}. \ - Found {self.keyring.__class__.__name__}") - - mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( - config=MaterialProvidersConfig() - ) - cmm = mat_prov.create_default_cryptographic_materials_manager( - CreateDefaultCryptographicMaterialsManagerInput( - keyring=self.keyring + try: + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() ) - ) - cmm_handler: CryptoMaterialsManager = CryptoMaterialsManagerFromMPL(cmm) - self.materials_manager = cmm_handler + cmm = mat_prov.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=self.keyring + ) + ) + cmm_handler: CryptoMaterialsManager = CryptoMaterialsManagerFromMPL(cmm) + self.materials_manager = cmm_handler + except AwsCryptographicMaterialProvidersException as mpl_exception: + # Wrap MPL error into the ESDK error type + # so customers only have to catch ESDK error types. + raise AWSEncryptionSDKClientError(mpl_exception) def _no_mpl_attrs_post_init(self): """If the MPL is NOT present in the runtime, perform post-init logic diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index bd7227fd3..c37c97bde 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -12,7 +12,8 @@ # 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 +import cryptography.hazmat.primitives.serialization from pytest_mock import mocker # noqa pylint: disable=unused-import import aws_encryption_sdk.internal.crypto.authentication @@ -75,28 +76,72 @@ def test_f_signer_from_key_bytes(): def test_f_signer_key_bytes(): test = Signer(algorithm=ALGORITHM, key=VALUES["ecc_private_key_prime"]) assert test.key_bytes() == VALUES["ecc_private_key_prime_private_bytes"] + +def test_GIVEN_no_encoding_WHEN_signer_from_key_bytes_THEN_load_der_private_key( + patch_default_backend, + 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) -def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): + # 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: + Signer.from_key_bytes( + algorithm=_algorithm, + key_bytes=sentinel.key_bytes, + ) + + 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 +): mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) - # Explicitly pass in patched serialization module. - # Patching the module introduces namespace issues - # which causes the method's `isinstance` checks to fail - # by changing the namespace from `serialization.Encoding.DER` to `Encoding.DER`. signer = Signer.from_key_bytes( algorithm=_algorithm, key_bytes=sentinel.key_bytes, - encoding=patch_serialization.Encoding.DER + encoding=patch_serialization.Encoding.PEM ) - patch_serialization.load_der_private_key.assert_called_once_with( + 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 +): + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + + with pytest.raises(ValueError): + signer = Signer.from_key_bytes( + algorithm=_algorithm, + key_bytes=sentinel.key_bytes, + encoding="not an encoding" + ) def test_signer_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 426f8f85f..c76a64ea7 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -15,6 +15,7 @@ import pytest import six +from mock import patch from aws_encryption_sdk import CommitmentPolicy from aws_encryption_sdk.internal.defaults import ALGORITHM, FRAME_LENGTH, LINE_LENGTH @@ -28,6 +29,22 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + from aws_cryptographic_materialproviders.mpl.references import ( + IKeyring, + ) + HAS_MPL = True + + from aws_encryption_sdk.materials_managers.mpl.cmm import ( + CryptoMaterialsManagerFromMPL, + ) +except ImportError: + HAS_MPL = False + + class FakeCryptoMaterialsManager(CryptoMaterialsManager): def get_encryption_materials(self, request): return @@ -42,6 +59,14 @@ class FakeMasterKeyProvider(MasterKeyProvider): def _new_master_key(self, key_id): return + +if HAS_MPL: + class FakeKeyring(IKeyring): + def on_encrypt(self, param): + return + + def on_decrypt(self, param): + return BASE_KWARGS = dict( @@ -126,6 +151,18 @@ def test_client_config_defaults(): assert test.max_encrypted_data_keys is None +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_client_config_with_mpl_attr(): + test = _ClientConfig(**BASE_KWARGS) + assert hasattr(test, "keyring") + + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_client_config_no_mpl(): + test = _ClientConfig(**BASE_KWARGS) + assert not hasattr(test, "keyring") + + def test_encryptor_config_defaults(): test = EncryptorConfig(**BASE_KWARGS) assert test.encryption_context == {} @@ -154,3 +191,62 @@ def test_client_config_converts(kwargs, stream_type): assert isinstance(test.source, stream_type) if test.key_provider is not None: assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) + + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +@patch.object(_ClientConfig, "_no_mpl_attrs_post_init") +def test_GIVEN_no_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method( + mock_no_mpl_attrs_post_init, +): + _ClientConfig(**BASE_KWARGS) + mock_no_mpl_attrs_post_init.assert_called_once_with() + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch.object(_ClientConfig, "_has_mpl_attrs_post_init") +def test_GIVEN_has_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method( + _has_mpl_attrs_post_init, +): + _ClientConfig(**BASE_KWARGS) + _has_mpl_attrs_post_init.assert_called_once_with() + + +@pytest.mark.parametrize( + "kwargs, stream_type", + ( + (dict(source=b"", materials_manager=FakeCryptoMaterialsManager()), io.BytesIO), + (dict(source=b"", key_provider=FakeMasterKeyProvider()), io.BytesIO), + (dict(source="", materials_manager=FakeCryptoMaterialsManager()), io.BytesIO), + (dict(source=io.BytesIO(), materials_manager=FakeCryptoMaterialsManager()), io.BytesIO), + (dict(source=six.StringIO(), materials_manager=FakeCryptoMaterialsManager()), six.StringIO), + (dict(source=b"", keyring=FakeKeyring()), io.BytesIO), + ), +) +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_client_configs_with_mpl( + kwargs, + stream_type +): + kwargs["commitment_policy"] = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + + test = _ClientConfig(**kwargs) + + # In all cases, config should have a materials manager + assert test.materials_manager is not None + + # If materials manager was provided, it should be directly used + if hasattr(kwargs, "materials_manager"): + assert kwargs["materials_manager"] == test.materials_manager + + # If MPL keyring was provided, it should be wrapped in MPL materials manager + if hasattr(kwargs, "keyring"): + assert test.keyring is not None + assert test.keyring == kwargs["keyring"] + assert isinstance(test.keyring, IKeyring) + assert isinstance(test.materials_manager, CryptoMaterialsManagerFromMPL) + + # If native key_provider was provided, it should be wrapped in native materials manager + if hasattr(kwargs, "key_provider"): + assert test.key_provider is not None + assert test.key_provider == kwargs["key_provider"] + assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index 157755094..c8a17e650 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -33,14 +33,36 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + from aws_cryptographic_materialproviders.mpl.references import ( + IKeyring, + ) + HAS_MPL = True + + from aws_encryption_sdk.materials_managers.mpl.cmm import ( + CryptoMaterialsManagerFromMPL, + ) +except ImportError: + HAS_MPL = False + + class TestStreamDecryptor(object): @pytest.fixture(autouse=True) def apply_fixtures(self): self.mock_key_provider = MagicMock(__class__=MasterKeyProvider) self.mock_materials_manager = MagicMock(__class__=CryptoMaterialsManager) - self.mock_materials_manager.decrypt_materials.return_value = MagicMock( + self.mock_decrypt_materials = MagicMock( data_key=VALUES["data_key_obj"], verification_key=sentinel.verification_key ) + self.mock_materials_manager.decrypt_materials.return_value = self.mock_decrypt_materials + + if HAS_MPL: + self.mock_mpl_materials_manager = MagicMock(__class__=CryptoMaterialsManagerFromMPL) + self.mock_mpl_materials_manager.decrypt_materials.return_value = self.mock_decrypt_materials + self.mock_header = MagicMock() self.mock_header.version = SerializationVersion.V1 self.mock_header.algorithm = MagicMock( @@ -213,6 +235,114 @@ def test_read_header(self, mock_derive_datakey, mock_decrypt_materials_request, assert test_header is self.mock_header assert test_header_auth is sentinel.header_auth + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.Verifier") + @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") + def test_GIVEN_verification_key_AND_no_mpl_WHEN_read_header_THEN_calls_from_key_bytes( + self, + mock_verifier, + *_, + ): + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + ct_stream = io.BytesIO(VALUES["data_128"]) + mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=ct_stream, + commitment_policy=mock_commitment_policy, + ) + test_decryptor.source_stream = ct_stream + test_decryptor._stream_length = len(VALUES["data_128"]) + + test_decryptor._read_header() + + mock_verifier.from_key_bytes.assert_called_once_with( + algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key + ) + + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.Verifier") + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_verification_key_AND_has_mpl_AND_not_MPLCMM_WHEN_read_header_THEN_calls_from_key_bytes( + self, + mock_verifier, + *_, + ): + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + ct_stream = io.BytesIO(VALUES["data_128"]) + mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=ct_stream, + commitment_policy=mock_commitment_policy, + ) + test_decryptor.source_stream = ct_stream + test_decryptor._stream_length = len(VALUES["data_128"]) + + test_decryptor._read_header() + + mock_verifier.from_key_bytes.assert_called_once_with( + algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key + ) + + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.Verifier") + @patch("base64.b64encode") + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_verification_key_AND_has_mpl_AND_has_MPLCMM_WHEN_read_header_THEN_calls_from_encoded_point( + self, + mock_b64encoding, + mock_verifier, + *_, + ): + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + ct_stream = io.BytesIO(VALUES["data_128"]) + mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) + test_decryptor = StreamDecryptor( + materials_manager=self.mock_mpl_materials_manager, + source=ct_stream, + commitment_policy=mock_commitment_policy, + ) + test_decryptor.source_stream = ct_stream + test_decryptor._stream_length = len(VALUES["data_128"]) + + test_decryptor._read_header() + + mock_verifier.from_encoded_point.assert_called_once_with( + algorithm=self.mock_header.algorithm, encoded_point=mock_b64encoding() + ) + + # @patch("aws_encryption_sdk.streaming_client.Verifier") + # @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + # def test_GIVEN_verification_key_AND_has_mpl_AND_not_MPLCMM_WHEN_read_header_THEN_calls_from_key_bytes( + # self, + # mock_verifier, + # ): + # mock_verifier_instance = MagicMock() + # mock_verifier.from_key_bytes.return_value = mock_verifier_instance + # ct_stream = io.BytesIO(VALUES["data_128"]) + # mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) + # test_decryptor = StreamDecryptor( + # materials_manager=self.mock_materials_manager, + # source=ct_stream, + # commitment_policy=mock_commitment_policy, + # ) + # test_decryptor.source_stream = ct_stream + # test_decryptor._stream_length = len(VALUES["data_128"]) + + # test_decryptor._read_header() + + # mock_verifier.from_key_bytes.assert_called_once_with( + # algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key + # ) + + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") def test_read_header_frame_too_large(self, mock_derive_datakey): self.mock_header.content_type = ContentType.FRAMED_DATA diff --git a/test/unit/test_streaming_client_stream_encryptor.py b/test/unit/test_streaming_client_stream_encryptor.py index 5bfd0c903..11664411a 100644 --- a/test/unit/test_streaming_client_stream_encryptor.py +++ b/test/unit/test_streaming_client_stream_encryptor.py @@ -13,6 +13,7 @@ """Unit test suite for aws_encryption_sdk.streaming_client.StreamEncryptor""" import io +from cryptography.hazmat.primitives import serialization import pytest import six from mock import MagicMock, call, patch, sentinel @@ -37,6 +38,22 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + from aws_cryptographic_materialproviders.mpl.references import ( + IKeyring, + ) + HAS_MPL = True + + from aws_encryption_sdk.materials_managers.mpl.cmm import ( + CryptoMaterialsManagerFromMPL, + ) +except ImportError: + HAS_MPL = False + + class TestStreamEncryptor(object): @pytest.fixture(autouse=True) def apply_fixtures(self): @@ -60,6 +77,10 @@ def apply_fixtures(self): self.mock_master_keys_set, ) + if HAS_MPL: + self.mock_mpl_materials_manager = MagicMock(__class__=CryptoMaterialsManagerFromMPL) + self.mock_mpl_materials_manager.get_encryption_materials.return_value = self.mock_encryption_materials + self.mock_master_key = MagicMock(__class__=MasterKey) self.mock_frame_length = MagicMock(__class__=int) @@ -366,6 +387,64 @@ def test_prep_message_non_framed_message(self, mock_write_header, mock_prep_non_ test_encryptor._prep_message() mock_prep_non_framed.assert_called_once_with() + @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") + def test_GIVEN_no_mpl_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_default_encoding(self): + self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 + test_encryptor = StreamEncryptor( + source=VALUES["data_128"], + materials_manager=self.mock_materials_manager, + frame_length=self.mock_frame_length, + algorithm=Algorithm.AES_128_GCM_IV12_TAG16, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.content_type = ContentType.FRAMED_DATA + with patch.object(self.mock_signer, "from_key_bytes"): + test_encryptor._prep_message() + self.mock_signer.from_key_bytes.assert_called_once_with( + algorithm=self.mock_encryption_materials.algorithm, + key_bytes=self.mock_encryption_materials.signing_key + ) + + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_mpl_AND_not_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_default_encoding(self): + self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 + test_encryptor = StreamEncryptor( + source=VALUES["data_128"], + materials_manager=self.mock_materials_manager, + frame_length=self.mock_frame_length, + algorithm=Algorithm.AES_128_GCM_IV12_TAG16, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.content_type = ContentType.FRAMED_DATA + with patch.object(self.mock_signer, "from_key_bytes"): + test_encryptor._prep_message() + self.mock_signer.from_key_bytes.assert_called_once_with( + algorithm=self.mock_encryption_materials.algorithm, + key_bytes=self.mock_encryption_materials.signing_key + ) + + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_mpl_AND_has_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_default_encoding(self): + self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 + test_encryptor = StreamEncryptor( + source=VALUES["data_128"], + materials_manager=self.mock_mpl_materials_manager, + frame_length=self.mock_frame_length, + algorithm=Algorithm.AES_128_GCM_IV12_TAG16, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.content_type = ContentType.FRAMED_DATA + with patch.object(self.mock_signer, "from_key_bytes"): + test_encryptor._prep_message() + self.mock_signer.from_key_bytes.assert_called_once_with( + algorithm=self.mock_encryption_materials.algorithm, + key_bytes=self.mock_encryption_materials.signing_key, + encoding=serialization.Encoding.PEM + ) + def test_prep_message_no_signer(self): self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 test_encryptor = StreamEncryptor( diff --git a/test/unit/test_utils.py b/test/unit/test_utils.py index c6d565108..d717b51c7 100644 --- a/test/unit/test_utils.py +++ b/test/unit/test_utils.py @@ -265,3 +265,28 @@ def test_source_data_key_length_check_invalid(self): source_data_key=mock_data_key, algorithm=mock_algorithm ) excinfo.match("Invalid Source Data Key length 4 for algorithm required: 5") + + def test_exactly_one_arg_is_not_none(self): + # No args => no args are not None + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none() is False + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + None + ) is False + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + "not None" + ) is True + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + "not None", "also not None" + ) is False + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + "not None", None + ) is True + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + "not None", "also not None" + ) is False + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + None, "not None" + ) is True + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + None, None + ) is False \ No newline at end of file From 49cb7c8d8b57f125c22fcddd206f18a31347e7fc Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 26 Feb 2024 12:45:57 -0800 Subject: [PATCH 134/172] more unit tests --- test/unit/test_streaming_client_configs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index c76a64ea7..3e49e6747 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -211,6 +211,7 @@ def test_GIVEN_has_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method( _has_mpl_attrs_post_init.assert_called_once_with() +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") @pytest.mark.parametrize( "kwargs, stream_type", ( @@ -222,7 +223,6 @@ def test_GIVEN_has_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method( (dict(source=b"", keyring=FakeKeyring()), io.BytesIO), ), ) -@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") def test_client_configs_with_mpl( kwargs, stream_type From 705113a3ce2fcb2bda264c3f453125bd20db6a96 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 26 Feb 2024 12:54:24 -0800 Subject: [PATCH 135/172] more unit tests --- test/unit/test_streaming_client_configs.py | 38 ++++++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 3e49e6747..120931cb8 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -215,17 +215,15 @@ def test_GIVEN_has_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method( @pytest.mark.parametrize( "kwargs, stream_type", ( - (dict(source=b"", materials_manager=FakeCryptoMaterialsManager()), io.BytesIO), - (dict(source=b"", key_provider=FakeMasterKeyProvider()), io.BytesIO), - (dict(source="", materials_manager=FakeCryptoMaterialsManager()), io.BytesIO), - (dict(source=io.BytesIO(), materials_manager=FakeCryptoMaterialsManager()), io.BytesIO), - (dict(source=six.StringIO(), materials_manager=FakeCryptoMaterialsManager()), six.StringIO), - (dict(source=b"", keyring=FakeKeyring()), io.BytesIO), + (dict(source=b"", materials_manager=FakeCryptoMaterialsManager())), + (dict(source=b"", key_provider=FakeMasterKeyProvider())), + (dict(source="", materials_manager=FakeCryptoMaterialsManager())), + (dict(source=io.BytesIO(), materials_manager=FakeCryptoMaterialsManager())), + (dict(source=six.StringIO(), materials_manager=FakeCryptoMaterialsManager())), ), ) def test_client_configs_with_mpl( kwargs, - stream_type ): kwargs["commitment_policy"] = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT @@ -250,3 +248,29 @@ def test_client_configs_with_mpl( assert test.key_provider is not None assert test.key_provider == kwargs["key_provider"] assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) + + +# This needs its own test; pytest parametrize cannot use a conditionally-loaded type +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_keyring_client_config_with_mpl( +): + kwargs = { + "source": b"", + "keyring": FakeKeyring() + } + + test = _ClientConfig(**kwargs) + + # In all cases, config should have a materials manager + assert test.materials_manager is not None + + # If materials manager was provided, it should be directly used + if hasattr(kwargs, "materials_manager"): + assert kwargs["materials_manager"] == test.materials_manager + + # If MPL keyring was provided, it should be wrapped in MPL materials manager + if hasattr(kwargs, "keyring"): + assert test.keyring is not None + assert test.keyring == kwargs["keyring"] + assert isinstance(test.keyring, IKeyring) + assert isinstance(test.materials_manager, CryptoMaterialsManagerFromMPL) From f76d7f9f76c1eeaaf14aafbeade594680066515b Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 26 Feb 2024 12:56:49 -0800 Subject: [PATCH 136/172] more unit tests --- test/unit/test_streaming_client_configs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 120931cb8..1a3fc89bc 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -213,7 +213,7 @@ def test_GIVEN_has_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method( @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") @pytest.mark.parametrize( - "kwargs, stream_type", + "kwargs", ( (dict(source=b"", materials_manager=FakeCryptoMaterialsManager())), (dict(source=b"", key_provider=FakeMasterKeyProvider())), From 0da2a4f2c7dee0c0bd333bb42872b83f322af6e9 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 26 Feb 2024 13:10:59 -0800 Subject: [PATCH 137/172] more unit tests --- .../unit/test_crypto_authentication_signer.py | 9 ++++ test/unit/test_streaming_client_configs.py | 10 +++- .../test_streaming_client_stream_decryptor.py | 46 +++++++------------ .../test_streaming_client_stream_encryptor.py | 20 +++++--- 4 files changed, 47 insertions(+), 38 deletions(-) diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index c37c97bde..58cad2a7e 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -94,11 +94,14 @@ def test_GIVEN_no_encoding_WHEN_signer_from_key_bytes_THEN_load_der_private_key( 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 ) @@ -113,12 +116,15 @@ def test_GIVEN_PEM_encoding_WHEN_signer_from_key_bytes_THEN_load_pem_private_key mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + # When: from_key_bytes signer = Signer.from_key_bytes( algorithm=_algorithm, key_bytes=sentinel.key_bytes, + # Given: PEM encoding encoding=patch_serialization.Encoding.PEM ) + # 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 ) @@ -136,10 +142,13 @@ def test_GIVEN_unrecognized_encoding_WHEN_signer_from_key_bytes_THEN_raise_Value mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + # Then: Raises ValueError with pytest.raises(ValueError): + # When: from_key_bytes signer = Signer.from_key_bytes( algorithm=_algorithm, key_bytes=sentinel.key_bytes, + # Given: Invalid encoding encoding="not an encoding" ) diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 1a3fc89bc..38f6de930 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -193,22 +193,28 @@ def test_client_config_converts(kwargs, stream_type): assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) +# Given: no MPL @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") @patch.object(_ClientConfig, "_no_mpl_attrs_post_init") def test_GIVEN_no_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method( mock_no_mpl_attrs_post_init, ): + # When: attrs_post_init _ClientConfig(**BASE_KWARGS) + # Then: calls _no_mpl_attrs_post_init mock_no_mpl_attrs_post_init.assert_called_once_with() +# Given: has MPL @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") @patch.object(_ClientConfig, "_has_mpl_attrs_post_init") def test_GIVEN_has_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method( - _has_mpl_attrs_post_init, + mock_has_mpl_attrs_post_init, ): + # When: attrs_post_init _ClientConfig(**BASE_KWARGS) - _has_mpl_attrs_post_init.assert_called_once_with() + # Then: calls _has_mpl_attrs_post_init + mock_has_mpl_attrs_post_init.assert_called_once_with() @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index c8a17e650..fc45cc393 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -37,14 +37,11 @@ # Ideally, this logic would be based on mocking imports and testing logic, # but doing that introduces errors that cause other tests to fail. try: - from aws_cryptographic_materialproviders.mpl.references import ( - IKeyring, - ) - HAS_MPL = True - from aws_encryption_sdk.materials_managers.mpl.cmm import ( CryptoMaterialsManagerFromMPL, ) + HAS_MPL = True + except ImportError: HAS_MPL = False @@ -238,12 +235,14 @@ def test_read_header(self, mock_derive_datakey, mock_decrypt_materials_request, @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") @patch("aws_encryption_sdk.streaming_client.Verifier") + # Given: no MPL @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") def test_GIVEN_verification_key_AND_no_mpl_WHEN_read_header_THEN_calls_from_key_bytes( self, mock_verifier, *_, ): + # Given: verification key mock_verifier_instance = MagicMock() mock_verifier.from_key_bytes.return_value = mock_verifier_instance ct_stream = io.BytesIO(VALUES["data_128"]) @@ -256,8 +255,10 @@ def test_GIVEN_verification_key_AND_no_mpl_WHEN_read_header_THEN_calls_from_key_ test_decryptor.source_stream = ct_stream test_decryptor._stream_length = len(VALUES["data_128"]) + # When: read header test_decryptor._read_header() + # Then: calls from_key_bytes mock_verifier.from_key_bytes.assert_called_once_with( algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key ) @@ -265,17 +266,20 @@ def test_GIVEN_verification_key_AND_no_mpl_WHEN_read_header_THEN_calls_from_key_ @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") @patch("aws_encryption_sdk.streaming_client.Verifier") + # Given: has MPL @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") def test_GIVEN_verification_key_AND_has_mpl_AND_not_MPLCMM_WHEN_read_header_THEN_calls_from_key_bytes( self, mock_verifier, *_, ): + # Given: verification key mock_verifier_instance = MagicMock() mock_verifier.from_key_bytes.return_value = mock_verifier_instance ct_stream = io.BytesIO(VALUES["data_128"]) mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) test_decryptor = StreamDecryptor( + # Given: native CMM materials_manager=self.mock_materials_manager, source=ct_stream, commitment_policy=mock_commitment_policy, @@ -283,8 +287,10 @@ def test_GIVEN_verification_key_AND_has_mpl_AND_not_MPLCMM_WHEN_read_header_THEN test_decryptor.source_stream = ct_stream test_decryptor._stream_length = len(VALUES["data_128"]) + # When: read_header test_decryptor._read_header() + # Then: calls from_key_bytess mock_verifier.from_key_bytes.assert_called_once_with( algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key ) @@ -293,6 +299,7 @@ def test_GIVEN_verification_key_AND_has_mpl_AND_not_MPLCMM_WHEN_read_header_THEN @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") @patch("aws_encryption_sdk.streaming_client.Verifier") @patch("base64.b64encode") + # Given: has MPL @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") def test_GIVEN_verification_key_AND_has_mpl_AND_has_MPLCMM_WHEN_read_header_THEN_calls_from_encoded_point( self, @@ -300,11 +307,13 @@ def test_GIVEN_verification_key_AND_has_mpl_AND_has_MPLCMM_WHEN_read_header_THEN mock_verifier, *_, ): + # Given: Verification key mock_verifier_instance = MagicMock() mock_verifier.from_key_bytes.return_value = mock_verifier_instance ct_stream = io.BytesIO(VALUES["data_128"]) mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) test_decryptor = StreamDecryptor( + # Given: MPL CMM materials_manager=self.mock_mpl_materials_manager, source=ct_stream, commitment_policy=mock_commitment_policy, @@ -312,37 +321,14 @@ def test_GIVEN_verification_key_AND_has_mpl_AND_has_MPLCMM_WHEN_read_header_THEN test_decryptor.source_stream = ct_stream test_decryptor._stream_length = len(VALUES["data_128"]) + # When: read header test_decryptor._read_header() + # Then: calls from_encoded_point mock_verifier.from_encoded_point.assert_called_once_with( algorithm=self.mock_header.algorithm, encoded_point=mock_b64encoding() ) - # @patch("aws_encryption_sdk.streaming_client.Verifier") - # @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") - # def test_GIVEN_verification_key_AND_has_mpl_AND_not_MPLCMM_WHEN_read_header_THEN_calls_from_key_bytes( - # self, - # mock_verifier, - # ): - # mock_verifier_instance = MagicMock() - # mock_verifier.from_key_bytes.return_value = mock_verifier_instance - # ct_stream = io.BytesIO(VALUES["data_128"]) - # mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) - # test_decryptor = StreamDecryptor( - # materials_manager=self.mock_materials_manager, - # source=ct_stream, - # commitment_policy=mock_commitment_policy, - # ) - # test_decryptor.source_stream = ct_stream - # test_decryptor._stream_length = len(VALUES["data_128"]) - - # test_decryptor._read_header() - - # mock_verifier.from_key_bytes.assert_called_once_with( - # algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key - # ) - - @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") def test_read_header_frame_too_large(self, mock_derive_datakey): self.mock_header.content_type = ContentType.FRAMED_DATA diff --git a/test/unit/test_streaming_client_stream_encryptor.py b/test/unit/test_streaming_client_stream_encryptor.py index 11664411a..bb4ba1c5e 100644 --- a/test/unit/test_streaming_client_stream_encryptor.py +++ b/test/unit/test_streaming_client_stream_encryptor.py @@ -42,14 +42,11 @@ # Ideally, this logic would be based on mocking imports and testing logic, # but doing that introduces errors that cause other tests to fail. try: - from aws_cryptographic_materialproviders.mpl.references import ( - IKeyring, - ) - HAS_MPL = True - from aws_encryption_sdk.materials_managers.mpl.cmm import ( CryptoMaterialsManagerFromMPL, ) + HAS_MPL = True + except ImportError: HAS_MPL = False @@ -387,6 +384,7 @@ def test_prep_message_non_framed_message(self, mock_write_header, mock_prep_non_ test_encryptor._prep_message() mock_prep_non_framed.assert_called_once_with() + # Given: no MPL @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") def test_GIVEN_no_mpl_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_default_encoding(self): self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 @@ -400,17 +398,21 @@ def test_GIVEN_no_mpl_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_default ) test_encryptor.content_type = ContentType.FRAMED_DATA with patch.object(self.mock_signer, "from_key_bytes"): + # When: prep message test_encryptor._prep_message() + # Then: calls from_key_bytes with default encoding self.mock_signer.from_key_bytes.assert_called_once_with( algorithm=self.mock_encryption_materials.algorithm, key_bytes=self.mock_encryption_materials.signing_key ) + # Given: has MPL @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") def test_GIVEN_has_mpl_AND_not_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_default_encoding(self): self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 test_encryptor = StreamEncryptor( source=VALUES["data_128"], + # Given: native CMM materials_manager=self.mock_materials_manager, frame_length=self.mock_frame_length, algorithm=Algorithm.AES_128_GCM_IV12_TAG16, @@ -419,17 +421,21 @@ def test_GIVEN_has_mpl_AND_not_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_sig ) test_encryptor.content_type = ContentType.FRAMED_DATA with patch.object(self.mock_signer, "from_key_bytes"): + # When: prep_message test_encryptor._prep_message() + # Then: calls from_key_bytes with default encoding self.mock_signer.from_key_bytes.assert_called_once_with( algorithm=self.mock_encryption_materials.algorithm, key_bytes=self.mock_encryption_materials.signing_key ) + # Given: has MPL @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") - def test_GIVEN_has_mpl_AND_has_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_default_encoding(self): + def test_GIVEN_has_mpl_AND_has_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_PEM_encoding(self): self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 test_encryptor = StreamEncryptor( source=VALUES["data_128"], + # Given: MPL CMM materials_manager=self.mock_mpl_materials_manager, frame_length=self.mock_frame_length, algorithm=Algorithm.AES_128_GCM_IV12_TAG16, @@ -438,10 +444,12 @@ def test_GIVEN_has_mpl_AND_has_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_sig ) test_encryptor.content_type = ContentType.FRAMED_DATA with patch.object(self.mock_signer, "from_key_bytes"): + # When: prep_message test_encryptor._prep_message() self.mock_signer.from_key_bytes.assert_called_once_with( algorithm=self.mock_encryption_materials.algorithm, key_bytes=self.mock_encryption_materials.signing_key, + # Then: calls from_key_bytes with PEM encoding encoding=serialization.Encoding.PEM ) From 0040b2c67af302cf5e624838f2f54222d7aa85f3 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 26 Feb 2024 13:16:37 -0800 Subject: [PATCH 138/172] cleanup --- src/aws_encryption_sdk/streaming_client.py | 2 +- .../unit/test_crypto_authentication_signer.py | 15 ++++++----- test/unit/test_streaming_client_configs.py | 25 ++++++++----------- .../test_streaming_client_stream_decryptor.py | 8 +++--- .../test_streaming_client_stream_encryptor.py | 6 ++--- test/unit/test_utils.py | 16 ++++++------ 6 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 2cfcc9a02..cc9a6bb0f 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -171,7 +171,7 @@ def _has_mpl_attrs_post_init(self): except AwsCryptographicMaterialProvidersException as mpl_exception: # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. - raise AWSEncryptionSDKClientError(mpl_exception) + raise AWSEncryptionSDKClientError(mpl_exception) def _no_mpl_attrs_post_init(self): """If the MPL is NOT present in the runtime, perform post-init logic diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index 58cad2a7e..425f672ed 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -11,9 +11,9 @@ # 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 import cryptography.hazmat.primitives.serialization +import pytest +from mock import MagicMock, patch, sentinel from pytest_mock import mocker # noqa pylint: disable=unused-import import aws_encryption_sdk.internal.crypto.authentication @@ -76,7 +76,7 @@ def test_f_signer_from_key_bytes(): def test_f_signer_key_bytes(): test = Signer(algorithm=ALGORITHM, key=VALUES["ecc_private_key_prime"]) assert test.key_bytes() == VALUES["ecc_private_key_prime_private_bytes"] - + def test_GIVEN_no_encoding_WHEN_signer_from_key_bytes_THEN_load_der_private_key( patch_default_backend, @@ -93,7 +93,10 @@ def test_GIVEN_no_encoding_WHEN_signer_from_key_bytes_THEN_load_der_private_key( # 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: + 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, @@ -106,7 +109,7 @@ def test_GIVEN_no_encoding_WHEN_signer_from_key_bytes_THEN_load_der_private_key( 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, @@ -145,7 +148,7 @@ def test_GIVEN_unrecognized_encoding_WHEN_signer_from_key_bytes_THEN_raise_Value # Then: Raises ValueError with pytest.raises(ValueError): # When: from_key_bytes - signer = Signer.from_key_bytes( + Signer.from_key_bytes( algorithm=_algorithm, key_bytes=sentinel.key_bytes, # Given: Invalid encoding diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 38f6de930..26ef86be8 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -33,14 +33,10 @@ # Ideally, this logic would be based on mocking imports and testing logic, # but doing that introduces errors that cause other tests to fail. try: - from aws_cryptographic_materialproviders.mpl.references import ( - IKeyring, - ) + from aws_cryptographic_materialproviders.mpl.references import IKeyring HAS_MPL = True - from aws_encryption_sdk.materials_managers.mpl.cmm import ( - CryptoMaterialsManagerFromMPL, - ) + from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL except ImportError: HAS_MPL = False @@ -59,14 +55,15 @@ class FakeMasterKeyProvider(MasterKeyProvider): def _new_master_key(self, key_id): return - + + if HAS_MPL: class FakeKeyring(IKeyring): def on_encrypt(self, param): - return - + return + def on_decrypt(self, param): - return + return BASE_KWARGS = dict( @@ -234,10 +231,10 @@ def test_client_configs_with_mpl( kwargs["commitment_policy"] = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT test = _ClientConfig(**kwargs) - + # In all cases, config should have a materials manager assert test.materials_manager is not None - + # If materials manager was provided, it should be directly used if hasattr(kwargs, "materials_manager"): assert kwargs["materials_manager"] == test.materials_manager @@ -266,10 +263,10 @@ def test_keyring_client_config_with_mpl( } test = _ClientConfig(**kwargs) - + # In all cases, config should have a materials manager assert test.materials_manager is not None - + # If materials manager was provided, it should be directly used if hasattr(kwargs, "materials_manager"): assert kwargs["materials_manager"] == test.materials_manager diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index fc45cc393..e06cad308 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -37,9 +37,7 @@ # Ideally, this logic would be based on mocking imports and testing logic, # but doing that introduces errors that cause other tests to fail. try: - from aws_encryption_sdk.materials_managers.mpl.cmm import ( - CryptoMaterialsManagerFromMPL, - ) + from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL HAS_MPL = True except ImportError: @@ -55,7 +53,7 @@ def apply_fixtures(self): data_key=VALUES["data_key_obj"], verification_key=sentinel.verification_key ) self.mock_materials_manager.decrypt_materials.return_value = self.mock_decrypt_materials - + if HAS_MPL: self.mock_mpl_materials_manager = MagicMock(__class__=CryptoMaterialsManagerFromMPL) self.mock_mpl_materials_manager.decrypt_materials.return_value = self.mock_decrypt_materials @@ -258,7 +256,7 @@ def test_GIVEN_verification_key_AND_no_mpl_WHEN_read_header_THEN_calls_from_key_ # When: read header test_decryptor._read_header() - # Then: calls from_key_bytes + # Then: calls from_key_bytes mock_verifier.from_key_bytes.assert_called_once_with( algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key ) diff --git a/test/unit/test_streaming_client_stream_encryptor.py b/test/unit/test_streaming_client_stream_encryptor.py index bb4ba1c5e..e43752689 100644 --- a/test/unit/test_streaming_client_stream_encryptor.py +++ b/test/unit/test_streaming_client_stream_encryptor.py @@ -13,9 +13,9 @@ """Unit test suite for aws_encryption_sdk.streaming_client.StreamEncryptor""" import io -from cryptography.hazmat.primitives import serialization import pytest import six +from cryptography.hazmat.primitives import serialization from mock import MagicMock, call, patch, sentinel import aws_encryption_sdk.internal.defaults @@ -42,9 +42,7 @@ # Ideally, this logic would be based on mocking imports and testing logic, # but doing that introduces errors that cause other tests to fail. try: - from aws_encryption_sdk.materials_managers.mpl.cmm import ( - CryptoMaterialsManagerFromMPL, - ) + from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL HAS_MPL = True except ImportError: diff --git a/test/unit/test_utils.py b/test/unit/test_utils.py index d717b51c7..69f9f060d 100644 --- a/test/unit/test_utils.py +++ b/test/unit/test_utils.py @@ -268,25 +268,25 @@ def test_source_data_key_length_check_invalid(self): def test_exactly_one_arg_is_not_none(self): # No args => no args are not None - assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none() is False + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none() is False assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( None - ) is False + ) is False assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( "not None" - ) is True + ) is True assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( "not None", "also not None" - ) is False + ) is False assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( "not None", None - ) is True + ) is True assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( "not None", "also not None" - ) is False + ) is False assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( None, "not None" - ) is True + ) is True assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( None, None - ) is False \ No newline at end of file + ) is False From 9131433f84f2f796a7e29f221e774020008aeeca Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 26 Feb 2024 13:20:17 -0800 Subject: [PATCH 139/172] cleanup --- src/aws_encryption_sdk/streaming_client.py | 2 +- test/unit/test_streaming_client_configs.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index cc9a6bb0f..5bf953244 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -171,7 +171,7 @@ def _has_mpl_attrs_post_init(self): except AwsCryptographicMaterialProvidersException as mpl_exception: # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. - raise AWSEncryptionSDKClientError(mpl_exception) + raise AWSEncryptionSDKClientError(mpl_exception) def _no_mpl_attrs_post_init(self): """If the MPL is NOT present in the runtime, perform post-init logic diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 26ef86be8..18886f65b 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -259,7 +259,8 @@ def test_keyring_client_config_with_mpl( ): kwargs = { "source": b"", - "keyring": FakeKeyring() + "keyring": FakeKeyring(), + "commitment_policy": CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT } test = _ClientConfig(**kwargs) From e6826eb3fdc5773dc00f5bfb1113a7d8f0c67fd3 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 12:20:56 -0800 Subject: [PATCH 140/172] poc impl --- src/aws_encryption_sdk/__init__.py | 1 + .../internal/formatting/serialize.py | 35 +++++-- .../materials_managers/mpl/cmm.py | 1 + .../materials_managers/mpl/materials.py | 11 +++ src/aws_encryption_sdk/streaming_client.py | 93 +++++++++++++++---- 5 files changed, 115 insertions(+), 26 deletions(-) diff --git a/src/aws_encryption_sdk/__init__.py b/src/aws_encryption_sdk/__init__.py index 661d41ee6..4b35e6744 100644 --- a/src/aws_encryption_sdk/__init__.py +++ b/src/aws_encryption_sdk/__init__.py @@ -185,6 +185,7 @@ def decrypt(self, **kwargs): If source_length is not provided and read() is called, will attempt to seek() to the end of the stream and tell() to find the length of source data. + :param dict encryption_context: Dictionary defining encryption context :param int max_body_length: Maximum frame size (or content length for non-framed messages) in bytes to read from ciphertext message. :returns: Tuple containing the decrypted plaintext and the message header object diff --git a/src/aws_encryption_sdk/internal/formatting/serialize.py b/src/aws_encryption_sdk/internal/formatting/serialize.py index b4d866099..718d4ad7d 100644 --- a/src/aws_encryption_sdk/internal/formatting/serialize.py +++ b/src/aws_encryption_sdk/internal/formatting/serialize.py @@ -218,7 +218,13 @@ def _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer=Non return output -def _serialize_header_auth_v2(algorithm, header, data_encryption_key, signer=None): +def _serialize_header_auth_v2( + algorithm, + header, + data_encryption_key, + signer=None, + required_encryption_context_bytes=None + ): """Creates serialized header authentication data for messages in serialization version V2. :param algorithm: Algorithm to use for encryption @@ -230,13 +236,22 @@ def _serialize_header_auth_v2(algorithm, header, data_encryption_key, signer=Non :returns: Serialized header authentication data :rtype: bytes """ - header_auth = encrypt( - algorithm=algorithm, - key=data_encryption_key, - plaintext=b"", - associated_data=header, - iv=header_auth_iv(algorithm), - ) + if required_encryption_context_bytes is None: + header_auth = encrypt( + algorithm=algorithm, + key=data_encryption_key, + plaintext=b"", + associated_data=header, + iv=header_auth_iv(algorithm), + ) + else: + header_auth = encrypt( + algorithm=algorithm, + key=data_encryption_key, + plaintext=b"", + associated_data=header + required_encryption_context_bytes, + iv=header_auth_iv(algorithm), + ) output = struct.pack( ">{tag_len}s".format(tag_len=algorithm.tag_len), header_auth.tag, @@ -246,7 +261,7 @@ def _serialize_header_auth_v2(algorithm, header, data_encryption_key, signer=Non return output -def serialize_header_auth(version, algorithm, header, data_encryption_key, signer=None): +def serialize_header_auth(version, algorithm, header, data_encryption_key, signer=None, required_encryption_context_bytes=None): """Creates serialized header authentication data. :param version: The serialization version of the message @@ -263,7 +278,7 @@ def serialize_header_auth(version, algorithm, header, data_encryption_key, signe if version == SerializationVersion.V1: return _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer) elif version == SerializationVersion.V2: - return _serialize_header_auth_v2(algorithm, header, data_encryption_key, signer) + return _serialize_header_auth_v2(algorithm, header, data_encryption_key, signer, required_encryption_context_bytes) else: raise SerializationError("Unrecognized message format version: {}".format(version)) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index 53a4b3505..8df42bf48 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -143,5 +143,6 @@ def _create_mpl_decrypt_materials_input_from_request( ), encrypted_data_keys=list_edks, encryption_context=request.encryption_context, + reproduced_encryption_context=request.reproduced_encryption_context, ) return output diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index dfd1bd6fc..d2abf182c 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -95,6 +95,12 @@ def data_encryption_key(self) -> DataKey: def signing_key(self) -> bytes: """Materials' signing key.""" return self.mpl_materials.signing_key + + + @property + def required_encryption_context_keys(self) -> bytes: + """Materials' required encryption context keys.""" + return self.mpl_materials.required_encryption_context_keys class DecryptionMaterialsFromMPL(Native_DecryptionMaterials): @@ -136,3 +142,8 @@ def data_key(self) -> DataKey: def verification_key(self) -> bytes: """Materials' verification key.""" return self.mpl_materials.verification_key + + @property + def required_encryption_context_keys(self) -> bytes: + """Materials' required encryption context keys.""" + return self.mpl_materials.required_encryption_context_keys diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 5bf953244..6d779c79e 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -593,11 +593,23 @@ def generate_header(self, message_id): if self._encryption_materials.algorithm.message_format_version == 0x02: version = SerializationVersion.V2 + if hasattr(self._encryption_materials, "required_encryption_context_keys"): + self._required_encryption_context = {} + self._stored_encryption_context = {} + for (k, v) in self._encryption_materials.encryption_context: + if k in self._encryption_materials.required_encryption_context_keys: + self._required_encryption_context[k] = v + else: + self._stored_encryption_context[k] = v + else: + self._stored_encryption_context = self._encryption_materials.encryption_context, + self._required_encryption_context = None + kwargs = dict( version=version, algorithm=self._encryption_materials.algorithm, message_id=message_id, - encryption_context=self._encryption_materials.encryption_context, + encryption_context=self._stored_encryption_context, encrypted_data_keys=self._encryption_materials.encrypted_data_keys, content_type=self.content_type, frame_length=self.config.frame_length, @@ -621,13 +633,27 @@ def generate_header(self, message_id): def _write_header(self): """Builds the message header and writes it to the output stream.""" self.output_buffer += serialize_header(header=self._header, signer=self.signer) - self.output_buffer += serialize_header_auth( - version=self._header.version, - algorithm=self._encryption_materials.algorithm, - header=self.output_buffer, - data_encryption_key=self._derived_data_key, - signer=self.signer, - ) + + if self._required_encryption_context is not None: + required_ec_serialized = aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( + self._required_encryption_context + ) + self.output_buffer += serialize_header_auth( + version=self._header.version, + algorithm=self._encryption_materials.algorithm, + header=self.output_buffer, + data_encryption_key=self._derived_data_key, + signer=self.signer, + required_encryption_context_bytes=required_ec_serialized, + ) + else: + self.output_buffer += serialize_header_auth( + version=self._header.version, + algorithm=self._encryption_materials.algorithm, + header=self.output_buffer, + data_encryption_key=self._derived_data_key, + signer=self.signer, + ) def _prep_non_framed(self): """Prepare the opening data for a non-framed message.""" @@ -907,14 +933,32 @@ def _read_header(self): found=header.frame_length, custom=self.config.max_body_length ) ) - - decrypt_materials_request = DecryptionMaterialsRequest( - encrypted_data_keys=header.encrypted_data_keys, - algorithm=header.algorithm, - encryption_context=header.encryption_context, - commitment_policy=self.config.commitment_policy, - ) + + if hasattr(self, "encryption_context"): + decrypt_materials_request = DecryptionMaterialsRequest( + encrypted_data_keys=header.encrypted_data_keys, + algorithm=header.algorithm, + encryption_context=header.encryption_context, + commitment_policy=self.config.commitment_policy, + reproduced_encryption_context=self.encryption_context + ) + else: + decrypt_materials_request = DecryptionMaterialsRequest( + encrypted_data_keys=header.encrypted_data_keys, + algorithm=header.algorithm, + encryption_context=header.encryption_context, + commitment_policy=self.config.commitment_policy, + ) decryption_materials = self.config.materials_manager.decrypt_materials(request=decrypt_materials_request) + + if hasattr(decryption_materials, "required_encryption_context_keys"): + self._required_encryption_context = {} + for (k, v) in self._encryption_materials.encryption_context: + if k in self._encryption_materials.required_encryption_context_keys: + self._required_encryption_context[k] = v + else: + self._required_encryption_context = None + if decryption_materials.verification_key is None: self.verifier = None else: @@ -953,7 +997,24 @@ def _read_header(self): "message. Halting processing of this message." ) - validate_header(header=header, header_auth=header_auth, raw_header=raw_header, data_key=self._derived_data_key) + if required_ec_serialized is not None: + required_ec_serialized = aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( + self._required_encryption_context + ) + + validate_header( + header=header, + header_auth=header_auth, + raw_header=raw_header + required_ec_serialized, + data_key=self._derived_data_key + ) + else: + validate_header( + header=header, + header_auth=header_auth, + raw_header=raw_header, + data_key=self._derived_data_key + ) return header, header_auth From a9fa1a5579dde63c6b5556991452075738329608 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 13:34:13 -0800 Subject: [PATCH 141/172] passing --- .../materials_managers/__init__.py | 5 ++ .../materials_managers/mpl/materials.py | 5 ++ src/aws_encryption_sdk/streaming_client.py | 63 +++++++++++++++---- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/__init__.py b/src/aws_encryption_sdk/materials_managers/__init__.py index 9db1dafae..f1eb30023 100644 --- a/src/aws_encryption_sdk/materials_managers/__init__.py +++ b/src/aws_encryption_sdk/materials_managers/__init__.py @@ -89,11 +89,16 @@ class DecryptionMaterialsRequest(object): :param encrypted_data_keys: Set of encrypted data keys :type encrypted_data_keys: set of `aws_encryption_sdk.structures.EncryptedDataKey` :param dict encryption_context: Encryption context to provide to master keys for underlying decrypt requests + :param dict reproduced_encryption_context: TODO """ algorithm = attr.ib(validator=attr.validators.instance_of(Algorithm)) encrypted_data_keys = attr.ib(validator=attr.validators.instance_of(set)) encryption_context = attr.ib(validator=attr.validators.instance_of(dict)) + reproduced_encryption_context = attr.ib( + default=None, + validator=attr.validators.optional(attr.validators.instance_of(dict)) + ) commitment_policy = attr.ib( default=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT, validator=attr.validators.optional(attr.validators.instance_of(CommitmentPolicy)), diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index d2abf182c..5b066c7c7 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -143,6 +143,11 @@ def verification_key(self) -> bytes: """Materials' verification key.""" return self.mpl_materials.verification_key + @property + def encryption_context(self) -> Dict[str, str]: + """Materials' encryption context.""" + return self.mpl_materials.encryption_context + @property def required_encryption_context_keys(self) -> bytes: """Materials' required encryption context keys.""" diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 6d779c79e..f678c4b77 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -76,8 +76,13 @@ from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException - from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput - from aws_cryptographic_materialproviders.mpl.references import IKeyring + from aws_cryptographic_materialproviders.mpl.models import ( + CreateDefaultCryptographicMaterialsManagerInput, + ) + from aws_cryptographic_materialproviders.mpl.references import ( + ICryptographicMaterialsManager, + IKeyring, + ) _HAS_MPL = True # Import internal ESDK modules that depend on the MPL @@ -126,9 +131,30 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes max_encrypted_data_keys = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(int)) ) - materials_manager = attr.ib( - hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(CryptoMaterialsManager)) - ) + if _HAS_MPL: + # With the MPL, the provided materials_manager can be an instance of + # either the native interface or an MPL interface. + # If it implements the MPL interface, this constructor will + # internally wrap it in a native interface. + materials_manager = attr.ib( + hash=True, + default=None, + validator=attr.validators.optional( + attr.validators.instance_of( + (CryptoMaterialsManager, ICryptographicMaterialsManager) + ) + ) + ) + else: + materials_manager = attr.ib( + hash=True, + default=None, + validator=attr.validators.optional( + attr.validators.instance_of( + CryptoMaterialsManager + ) + ) + ) key_provider = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(MasterKeyProvider)) ) @@ -172,6 +198,12 @@ def _has_mpl_attrs_post_init(self): # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. raise AWSEncryptionSDKClientError(mpl_exception) + # TODO-MPL: MUST wrap MPL with native + elif (self.materials_manager is not None + and isinstance(self.materials_manager, ICryptographicMaterialsManager)): + # If the provided materials manager implements an MPL interface, + # wrap it in a native interface. + self.materials_manager = CryptoMaterialsManagerFromMPL(self.materials_manager) def _no_mpl_attrs_post_init(self): """If the MPL is NOT present in the runtime, perform post-init logic @@ -596,7 +628,8 @@ def generate_header(self, message_id): if hasattr(self._encryption_materials, "required_encryption_context_keys"): self._required_encryption_context = {} self._stored_encryption_context = {} - for (k, v) in self._encryption_materials.encryption_context: + print(f"{self._encryption_materials.encryption_context=}") + for (k, v) in self._encryption_materials.encryption_context.items(): if k in self._encryption_materials.required_encryption_context_keys: self._required_encryption_context[k] = v else: @@ -856,6 +889,11 @@ class DecryptorConfig(_ClientConfig): max_body_length = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(six.integer_types)) ) + encryption_context = attr.ib( + hash=False, # dictionaries are not hashable + default=attr.Factory(dict), + validator=attr.validators.instance_of(dict), + ) class StreamDecryptor(_EncryptionStream): # pylint: disable=too-many-instance-attributes @@ -934,13 +972,15 @@ def _read_header(self): ) ) - if hasattr(self, "encryption_context"): + print(f"{self.config.encryption_context=}") + + if hasattr(self.config, "encryption_context"): decrypt_materials_request = DecryptionMaterialsRequest( encrypted_data_keys=header.encrypted_data_keys, algorithm=header.algorithm, encryption_context=header.encryption_context, commitment_policy=self.config.commitment_policy, - reproduced_encryption_context=self.encryption_context + reproduced_encryption_context=self.config.encryption_context ) else: decrypt_materials_request = DecryptionMaterialsRequest( @@ -949,12 +989,13 @@ def _read_header(self): encryption_context=header.encryption_context, commitment_policy=self.config.commitment_policy, ) + print(f"{decrypt_materials_request=}") decryption_materials = self.config.materials_manager.decrypt_materials(request=decrypt_materials_request) if hasattr(decryption_materials, "required_encryption_context_keys"): self._required_encryption_context = {} - for (k, v) in self._encryption_materials.encryption_context: - if k in self._encryption_materials.required_encryption_context_keys: + for (k, v) in decryption_materials.encryption_context.items(): + if k in decryption_materials.required_encryption_context_keys: self._required_encryption_context[k] = v else: self._required_encryption_context = None @@ -997,7 +1038,7 @@ def _read_header(self): "message. Halting processing of this message." ) - if required_ec_serialized is not None: + if self._required_encryption_context is not None: required_ec_serialized = aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( self._required_encryption_context ) From 4eeb85889b498004eb865762e6503a6991bbee84 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 14:03:10 -0800 Subject: [PATCH 142/172] cleanup --- src/aws_encryption_sdk/__init__.py | 4 +- .../internal/formatting/serialize.py | 37 +++++++++++++++---- .../materials_managers/__init__.py | 4 +- src/aws_encryption_sdk/streaming_client.py | 36 ++++++++++++++---- 4 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/aws_encryption_sdk/__init__.py b/src/aws_encryption_sdk/__init__.py index 4b35e6744..96898d446 100644 --- a/src/aws_encryption_sdk/__init__.py +++ b/src/aws_encryption_sdk/__init__.py @@ -185,7 +185,9 @@ def decrypt(self, **kwargs): If source_length is not provided and read() is called, will attempt to seek() to the end of the stream and tell() to find the length of source data. - :param dict encryption_context: Dictionary defining encryption context + :param dict encryption_context: Dictionary defining encryption context to validate + on decrypt. This is ONLY validated on decrypt if using the required encryption + context CMM from the aws-cryptographic-materialproviders library. :param int max_body_length: Maximum frame size (or content length for non-framed messages) in bytes to read from ciphertext message. :returns: Tuple containing the decrypted plaintext and the message header object diff --git a/src/aws_encryption_sdk/internal/formatting/serialize.py b/src/aws_encryption_sdk/internal/formatting/serialize.py index 718d4ad7d..344c94703 100644 --- a/src/aws_encryption_sdk/internal/formatting/serialize.py +++ b/src/aws_encryption_sdk/internal/formatting/serialize.py @@ -219,12 +219,12 @@ def _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer=Non def _serialize_header_auth_v2( - algorithm, - header, - data_encryption_key, - signer=None, - required_encryption_context_bytes=None - ): + algorithm, + header, + data_encryption_key, + signer=None, + required_encryption_context_bytes=None +): """Creates serialized header authentication data for messages in serialization version V2. :param algorithm: Algorithm to use for encryption @@ -233,6 +233,11 @@ def _serialize_header_auth_v2( :param bytes data_encryption_key: Data key with which to encrypt message :param signer: Cryptographic signer object (optional) :type signer: aws_encryption_sdk.Signer + :param required_encryption_context_bytes: Serialized encryption context items + for all items whose keys are in the required_encryption_context list. + This is ONLY processed if using the aws-cryptographic-materialproviders library + AND its required encryption context CMM. (optional) + :type required_encryption_context_bytes: bytes :returns: Serialized header authentication data :rtype: bytes """ @@ -249,6 +254,11 @@ def _serialize_header_auth_v2( algorithm=algorithm, key=data_encryption_key, plaintext=b"", + # The AAD MUST be the concatenation of the serialized message header body and the serialization + # of encryption context to only authenticate. The encryption context to only authenticate MUST + # be the encryption context in the encryption materials filtered to only contain key value + # pairs listed in the encryption material's required encryption context keys serialized + # according to the encryption context serialization specification. associated_data=header + required_encryption_context_bytes, iv=header_auth_iv(algorithm), ) @@ -261,7 +271,14 @@ def _serialize_header_auth_v2( return output -def serialize_header_auth(version, algorithm, header, data_encryption_key, signer=None, required_encryption_context_bytes=None): +def serialize_header_auth( + version, + algorithm, + header, + data_encryption_key, + signer=None, + required_encryption_context_bytes=None +): """Creates serialized header authentication data. :param version: The serialization version of the message @@ -272,6 +289,12 @@ def serialize_header_auth(version, algorithm, header, data_encryption_key, signe :param bytes data_encryption_key: Data key with which to encrypt message :param signer: Cryptographic signer object (optional) :type signer: aws_encryption_sdk.Signer + :param required_encryption_context_bytes: Serialized encryption context items + for all items whose keys are in the required_encryption_context list. + This is ONLY processed if using the aws-cryptographic-materialproviders library + AND its required encryption context CMM + AND if using the v2 message format. (optional) + :type required_encryption_context_bytes: bytes :returns: Serialized header authentication data :rtype: bytes """ diff --git a/src/aws_encryption_sdk/materials_managers/__init__.py b/src/aws_encryption_sdk/materials_managers/__init__.py index f1eb30023..cc8cdcf6f 100644 --- a/src/aws_encryption_sdk/materials_managers/__init__.py +++ b/src/aws_encryption_sdk/materials_managers/__init__.py @@ -89,7 +89,9 @@ class DecryptionMaterialsRequest(object): :param encrypted_data_keys: Set of encrypted data keys :type encrypted_data_keys: set of `aws_encryption_sdk.structures.EncryptedDataKey` :param dict encryption_context: Encryption context to provide to master keys for underlying decrypt requests - :param dict reproduced_encryption_context: TODO + :param dict reproduced_encryption_context: Encryption context to provide on decrypt. + This is ONLY processed if using the required encryption context CMM from the + aws-cryptographic-materialproviders library. """ algorithm = attr.ib(validator=attr.validators.instance_of(Algorithm)) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index f678c4b77..568543863 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -198,11 +198,11 @@ def _has_mpl_attrs_post_init(self): # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. raise AWSEncryptionSDKClientError(mpl_exception) - # TODO-MPL: MUST wrap MPL with native + + # If the provided materials_manager is directly from the MPL, wrap it in a native interface + # for internal use. elif (self.materials_manager is not None and isinstance(self.materials_manager, ICryptographicMaterialsManager)): - # If the provided materials manager implements an MPL interface, - # wrap it in a native interface. self.materials_manager = CryptoMaterialsManagerFromMPL(self.materials_manager) def _no_mpl_attrs_post_init(self): @@ -625,15 +625,18 @@ def generate_header(self, message_id): if self._encryption_materials.algorithm.message_format_version == 0x02: version = SerializationVersion.V2 + # If the underlying materials_provider provided required_encryption_context_keys + # (ex. if the materials_provider is a required encryption context CMM), + # then partition the encryption context based on those keys. if hasattr(self._encryption_materials, "required_encryption_context_keys"): self._required_encryption_context = {} self._stored_encryption_context = {} - print(f"{self._encryption_materials.encryption_context=}") for (k, v) in self._encryption_materials.encryption_context.items(): if k in self._encryption_materials.required_encryption_context_keys: self._required_encryption_context[k] = v else: self._stored_encryption_context[k] = v + # Otherwise, store all encryption context with the message. else: self._stored_encryption_context = self._encryption_materials.encryption_context, self._required_encryption_context = None @@ -667,6 +670,8 @@ def _write_header(self): """Builds the message header and writes it to the output stream.""" self.output_buffer += serialize_header(header=self._header, signer=self.signer) + # If there is _required_encryption_context, + # serialize it, then authenticate it if self._required_encryption_context is not None: required_ec_serialized = aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( self._required_encryption_context @@ -679,6 +684,7 @@ def _write_header(self): signer=self.signer, required_encryption_context_bytes=required_ec_serialized, ) + # Otherwise, do not pass in any required encryption context else: self.output_buffer += serialize_header_auth( version=self._header.version, @@ -884,6 +890,9 @@ class DecryptorConfig(_ClientConfig): :param int max_body_length: Maximum frame size (or content length for non-framed messages) in bytes to read from ciphertext message. + :param dict encryption_context: Dictionary defining encryption context to validate + on decrypt. This is ONLY validated on decrypt if using the required encryption + context CMM from the aws-cryptographic-materialproviders library. """ max_body_length = attr.ib( @@ -971,9 +980,9 @@ def _read_header(self): found=header.frame_length, custom=self.config.max_body_length ) ) - - print(f"{self.config.encryption_context=}") - + + # If encryption_context is provided on decrypt, + # pass it to the DecryptionMaterialsRequest if hasattr(self.config, "encryption_context"): decrypt_materials_request = DecryptionMaterialsRequest( encrypted_data_keys=header.encrypted_data_keys, @@ -989,9 +998,12 @@ def _read_header(self): encryption_context=header.encryption_context, commitment_policy=self.config.commitment_policy, ) - print(f"{decrypt_materials_request=}") + decryption_materials = self.config.materials_manager.decrypt_materials(request=decrypt_materials_request) + # If the materials_manager passed required_encryption_context_keys, + # get the items out of the encryption_context with the keys. + # The items are used in header validation. if hasattr(decryption_materials, "required_encryption_context_keys"): self._required_encryption_context = {} for (k, v) in decryption_materials.encryption_context.items(): @@ -1038,7 +1050,12 @@ def _read_header(self): "message. Halting processing of this message." ) + # If _required_encryption_context is present, + # serialize it and pass it to validate_header. if self._required_encryption_context is not None: + # The authenticated only encryption context is all encryption context key-value pairs where the + # key exists in Required Encryption Context Keys. It is then serialized according to the + # message header Key Value Pairs. required_ec_serialized = aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( self._required_encryption_context ) @@ -1046,6 +1063,9 @@ def _read_header(self): validate_header( header=header, header_auth=header_auth, + # When verifying the header, the AAD input to the authenticated encryption algorithm + # specified by the algorithm suite is the message header body and the serialized + # authenticated only encryption context. raw_header=raw_header + required_ec_serialized, data_key=self._derived_data_key ) From 21a8c938eb44f352a76fd358e1c865f453e3f75a Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 16:08:02 -0800 Subject: [PATCH 143/172] protect --- examples/src/basic_encryption.py | 2 +- examples/test/test_i_basic_encryption.py | 2 +- src/aws_encryption_sdk/streaming_client.py | 15 ++++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/examples/src/basic_encryption.py b/examples/src/basic_encryption.py index cfe8ac791..68be5c594 100644 --- a/examples/src/basic_encryption.py +++ b/examples/src/basic_encryption.py @@ -38,7 +38,7 @@ def cycle_string(key_arn, source_plaintext, botocore_session=None): ciphertext, encryptor_header = client.encrypt(source=source_plaintext, key_provider=master_key_provider) # Decrypt the ciphertext - cycled_plaintext, decrypted_header = client.decrypt(source=ciphertext, key_provider=master_key_provider) + cycled_plaintext, decrypted_header = client.decrypt(source=ciphertext, key_provider=master_key_provider, encryption_context={"a": "v"}) # Verify that the "cycled" (encrypted, then decrypted) plaintext is identical to the source plaintext assert cycled_plaintext == source_plaintext diff --git a/examples/test/test_i_basic_encryption.py b/examples/test/test_i_basic_encryption.py index f2a4fab51..5f509800e 100644 --- a/examples/test/test_i_basic_encryption.py +++ b/examples/test/test_i_basic_encryption.py @@ -23,5 +23,5 @@ def test_cycle_string(): plaintext = static_plaintext - cmk_arn = get_cmk_arn() + cmk_arn = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" cycle_string(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 568543863..e3513de97 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -638,7 +638,7 @@ def generate_header(self, message_id): self._stored_encryption_context[k] = v # Otherwise, store all encryption context with the message. else: - self._stored_encryption_context = self._encryption_materials.encryption_context, + self._stored_encryption_context = self._encryption_materials.encryption_context self._required_encryption_context = None kwargs = dict( @@ -1001,6 +1001,19 @@ def _read_header(self): decryption_materials = self.config.materials_manager.decrypt_materials(request=decrypt_materials_request) + # Guard against possible misunderstanding of "encryption context on decrypt". + # The `encryption_context` parameter on the client's `decrypt` method + # is ONLY meant to be used in conjunction with a `materials_manager` + # that validates the encryption context provided to the decrypt method + if hasattr(self.config, "encryption_context"): + try: + assert hasattr(decryption_materials, "required_encryption_context_keys") + except AssertionError as e: + raise ValueError("encryption_context on decrypt is not supported with the configured CMM: " + f"{self.config.materials_manager}. " + "You MUST pass a CMM that supports required encryption context keys to " + "validate encryption context on decrypt.") + # If the materials_manager passed required_encryption_context_keys, # get the items out of the encryption_context with the keys. # The items are used in header validation. From de870b8495dcb34bb795edfa2bd72e24bf8f3c61 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 16:08:23 -0800 Subject: [PATCH 144/172] ex --- .../required_encryption_context_cmm.py | 130 ++++++++++++++++++ .../test_i_required_encryption_context_cmm.py | 13 ++ src/aws_encryption_sdk/streaming_client.py | 6 +- 3 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 examples/src/keyrings/required_encryption_context_cmm.py create mode 100644 examples/test/keyrings/test_i_required_encryption_context_cmm.py diff --git a/examples/src/keyrings/required_encryption_context_cmm.py b/examples/src/keyrings/required_encryption_context_cmm.py new file mode 100644 index 000000000..c36a4b2bd --- /dev/null +++ b/examples/src/keyrings/required_encryption_context_cmm.py @@ -0,0 +1,130 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Example showing basic encryption and decryption of a value already in memory.""" +import sys + +import boto3 +# Ignore missing MPL for pylint, but the MPL is required for this example +# noqa pylint: disable=import-error +from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig +from aws_cryptographic_materialproviders.mpl.models import ( + CacheTypeDefault, + CreateAwsKmsKeyringInput, + CreateDefaultCryptographicMaterialsManagerInput, + CreateRequiredEncryptionContextCMMInput, + DefaultCache, +) +from aws_cryptographic_materialproviders.mpl.references import ( + IKeyring, + ICryptographicMaterialsManager, +) +from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL +from typing import Dict + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError + +from .example_branch_key_id_supplier import ExampleBranchKeyIdSupplier + +module_root_dir = '/'.join(__file__.split("/")[:-1]) + +sys.path.append(module_root_dir) + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + kms_key_id: str +): + """Creates a hierarchical keyring using the provided resources, then encrypts and decrypts a string with it.""" + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 7. Create an encryption context. + #// Most encrypted data should have an associated encryption context + #// to protect integrity. This sample uses placeholder values. + #// For more information see: + #// blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management + encryption_context: Dict[str, str] = { + "key1": "value1", + "key2": "value2", + "requiredKey1": "requiredValue1", + "requiredKey2": "requiredValue2", + } + + #// 3. Create list of required encryption context keys. + #// This is a list of keys that must be present in the encryption context. + required_encryption_context_keys: List[str] = ["requiredKey1", "requiredKey2"] + + #// 4. Create the AWS KMS keyring. + mpl: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=boto3.client('kms', region_name="us-west-2") + ) + kms_keyring: IKeyring = mpl.create_aws_kms_keyring(keyring_input) + + #// 5. Create the required encryption context CMM. + underlying_cmm: ICryptographicMaterialsManager = \ + mpl.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=kms_keyring + ) + ) + + required_ec_cmm: ICryptographicMaterialsManager = \ + mpl.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + required_encryption_context_keys=required_encryption_context_keys, + underlying_cmm=underlying_cmm, + ) + ) + + # 6. Encrypt the data + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + materials_manager=required_ec_cmm, + encryption_context=encryption_context + ) + + # // 7. Reproduce the encryption context. + # // The reproduced encryption context MUST contain a value for + # // every key in the configured required encryption context keys during encryption with + # // Required Encryption Context CMM. + reproduced_encryption_context: Dict[str, str] = { + "requiredKey1": "requiredValue1", + "requiredKey2": "requiredValue2", + } + + # 8. Decrypt the data + plaintext_bytes_A, _ = client.decrypt( + source=ciphertext, + materials_manager=required_ec_cmm, + encryption_context=reproduced_encryption_context + ) + assert plaintext_bytes_A == EXAMPLE_DATA + + + # 9. If we don't provide the required encryption context, this should fail + try: + plaintext_bytes_A, _ = client.decrypt( + source=ciphertext, + materials_manager=required_ec_cmm, + # no encryption context while using required encryption context CMM makes decryption fail + ) + assert plaintext_bytes_A == EXAMPLE_DATA + except AWSEncryptionSDKClientError: + pass \ No newline at end of file diff --git a/examples/test/keyrings/test_i_required_encryption_context_cmm.py b/examples/test/keyrings/test_i_required_encryption_context_cmm.py new file mode 100644 index 000000000..9512a06ee --- /dev/null +++ b/examples/test/keyrings/test_i_required_encryption_context_cmm.py @@ -0,0 +1,13 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the hierarchical keyring example.""" +import pytest + +from ...src.keyrings.required_encryption_context_cmm import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + key_arn = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + encrypt_and_decrypt_with_keyring(key_arn) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index e3513de97..58cd52051 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -1003,8 +1003,10 @@ def _read_header(self): # Guard against possible misunderstanding of "encryption context on decrypt". # The `encryption_context` parameter on the client's `decrypt` method - # is ONLY meant to be used in conjunction with a `materials_manager` - # that validates the encryption context provided to the decrypt method + # is ONLY meant to be used in conjunction with a `materials_manager` + # that validates the encryption context provided to the decrypt method. + # This guards against accidentially passing encryption context on decrypt + # and not realizing nothing is being validated. if hasattr(self.config, "encryption_context"): try: assert hasattr(decryption_materials, "required_encryption_context_keys") From eedf1a3c269473117dc284c52b0d34f63943915b Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 16:24:03 -0800 Subject: [PATCH 145/172] changes --- examples/src/basic_encryption.py | 2 +- .../keyrings/required_encryption_context_cmm.py | 10 +++++----- src/aws_encryption_sdk/streaming_client.py | 17 +---------------- 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/examples/src/basic_encryption.py b/examples/src/basic_encryption.py index 68be5c594..cfe8ac791 100644 --- a/examples/src/basic_encryption.py +++ b/examples/src/basic_encryption.py @@ -38,7 +38,7 @@ def cycle_string(key_arn, source_plaintext, botocore_session=None): ciphertext, encryptor_header = client.encrypt(source=source_plaintext, key_provider=master_key_provider) # Decrypt the ciphertext - cycled_plaintext, decrypted_header = client.decrypt(source=ciphertext, key_provider=master_key_provider, encryption_context={"a": "v"}) + cycled_plaintext, decrypted_header = client.decrypt(source=ciphertext, key_provider=master_key_provider) # Verify that the "cycled" (encrypted, then decrypted) plaintext is identical to the source plaintext assert cycled_plaintext == source_plaintext diff --git a/examples/src/keyrings/required_encryption_context_cmm.py b/examples/src/keyrings/required_encryption_context_cmm.py index c36a4b2bd..51455fd4f 100644 --- a/examples/src/keyrings/required_encryption_context_cmm.py +++ b/examples/src/keyrings/required_encryption_context_cmm.py @@ -109,7 +109,7 @@ def encrypt_and_decrypt_with_keyring( "requiredKey2": "requiredValue2", } - # 8. Decrypt the data + # # 8. Decrypt the data plaintext_bytes_A, _ = client.decrypt( source=ciphertext, materials_manager=required_ec_cmm, @@ -117,14 +117,14 @@ def encrypt_and_decrypt_with_keyring( ) assert plaintext_bytes_A == EXAMPLE_DATA - - # 9. If we don't provide the required encryption context, this should fail + # 9. If we don't provide the required encryption context, + # decryption will fail. try: plaintext_bytes_A, _ = client.decrypt( source=ciphertext, materials_manager=required_ec_cmm, # no encryption context while using required encryption context CMM makes decryption fail ) - assert plaintext_bytes_A == EXAMPLE_DATA + raise Exception("If this exception is raised, decryption somehow succeeded!") except AWSEncryptionSDKClientError: - pass \ No newline at end of file + pass diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 58cd52051..7e6ede8cd 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -1000,22 +1000,7 @@ def _read_header(self): ) decryption_materials = self.config.materials_manager.decrypt_materials(request=decrypt_materials_request) - - # Guard against possible misunderstanding of "encryption context on decrypt". - # The `encryption_context` parameter on the client's `decrypt` method - # is ONLY meant to be used in conjunction with a `materials_manager` - # that validates the encryption context provided to the decrypt method. - # This guards against accidentially passing encryption context on decrypt - # and not realizing nothing is being validated. - if hasattr(self.config, "encryption_context"): - try: - assert hasattr(decryption_materials, "required_encryption_context_keys") - except AssertionError as e: - raise ValueError("encryption_context on decrypt is not supported with the configured CMM: " - f"{self.config.materials_manager}. " - "You MUST pass a CMM that supports required encryption context keys to " - "validate encryption context on decrypt.") - + # If the materials_manager passed required_encryption_context_keys, # get the items out of the encryption_context with the keys. # The items are used in header validation. From 1db73ebee0d6df0e2ca76b4f16998f018a042d0d Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 16:58:26 -0800 Subject: [PATCH 146/172] changes --- examples/src/keyrings/hierarchical_keyring.py | 33 ++++++++++++++- .../required_encryption_context_cmm.py | 40 +++++++++++-------- .../keyrings/test_i_hierarchical_keyring.py | 2 +- .../test_i_required_encryption_context_cmm.py | 2 +- examples/test/test_i_basic_encryption.py | 2 +- src/aws_encryption_sdk/streaming_client.py | 28 ++++++------- 6 files changed, 72 insertions(+), 35 deletions(-) diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index aa87485f9..b75421359 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -1,6 +1,36 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 -"""Example showing basic encryption and decryption of a value already in memory.""" +""" +This example sets up the Hierarchical Keyring, which establishes a key hierarchy where "branch" +keys are persisted in DynamoDb. These branch keys are used to protect your data keys, and these +branch keys are themselves protected by a KMS Key. + +Establishing a key hierarchy like this has two benefits: +First, by caching the branch key material, and only calling KMS to re-establish authentication +regularly according to your configured TTL, you limit how often you need to call KMS to protect +your data. This is a performance security tradeoff, where your authentication, audit, and logging +from KMS is no longer one-to-one with every encrypt or decrypt call. Additionally, KMS Cloudtrail +cannot be used to distinguish Encrypt and Decrypt calls, and you cannot restrict who has +Encryption rights from who has Decryption rights since they both ONLY need KMS:Decrypt. However, +the benefit is that you no longer have to make a network call to KMS for every encrypt or +decrypt. + +Second, this key hierarchy facilitates cryptographic isolation of a tenant's data in a +multi-tenant data store. Each tenant can have a unique Branch Key, that is only used to protect +the tenant's data. You can either statically configure a single branch key to ensure you are +restricting access to a single tenant, or you can implement an interface that selects the Branch +Key based on the Encryption Context. + +This example demonstrates configuring a Hierarchical Keyring with a Branch Key ID Supplier to +encrypt and decrypt data for two separate tenants. + +This example requires access to the DDB Table where you are storing the Branch Keys. This +table must be configured with the following primary key configuration: - Partition key is named +"partition_key" with type (S) - Sort key is named "sort_key" with type (S) + +This example also requires using a KMS Key. You need the following access on this key: - +GenerateDataKeyWithoutPlaintext - Decrypt +""" import sys import boto3 @@ -25,6 +55,7 @@ from .example_branch_key_id_supplier import ExampleBranchKeyIdSupplier +# TODO-MPL: Remove this as part of removing PYTHONPATH hacks module_root_dir = '/'.join(__file__.split("/")[:-1]) sys.path.append(module_root_dir) diff --git a/examples/src/keyrings/required_encryption_context_cmm.py b/examples/src/keyrings/required_encryption_context_cmm.py index 51455fd4f..f3d58c922 100644 --- a/examples/src/keyrings/required_encryption_context_cmm.py +++ b/examples/src/keyrings/required_encryption_context_cmm.py @@ -1,6 +1,11 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 -"""Example showing basic encryption and decryption of a value already in memory.""" +""" +Demonstrate an encrypt/decrypt cycle using a Required Encryption Context CMM. +A required encryption context CMM asks for required keys in the encryption context field +on encrypt such that they will not be stored on the message, but WILL be included in the header signature. +On decrypt, the client MUST supply the key/value pair(s) that were not stored to successfully decrypt the message. +""" import sys import boto3 @@ -28,6 +33,7 @@ from .example_branch_key_id_supplier import ExampleBranchKeyIdSupplier +# TODO-MPL: Remove this as part of removing PYTHONPATH hacks module_root_dir = '/'.join(__file__.split("/")[:-1]) sys.path.append(module_root_dir) @@ -51,11 +57,11 @@ def encrypt_and_decrypt_with_keyring( commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT ) - # 7. Create an encryption context. - #// Most encrypted data should have an associated encryption context - #// to protect integrity. This sample uses placeholder values. - #// For more information see: - #// blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management + # 2. Create an encryption context. + # Most encrypted data should have an associated encryption context + # to protect integrity. This sample uses placeholder values. + # For more information see: + # blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management # noqa: E501 encryption_context: Dict[str, str] = { "key1": "value1", "key2": "value2", @@ -63,11 +69,11 @@ def encrypt_and_decrypt_with_keyring( "requiredKey2": "requiredValue2", } - #// 3. Create list of required encryption context keys. - #// This is a list of keys that must be present in the encryption context. + # 3. Create list of required encryption context keys. + # This is a list of keys that must be present in the encryption context. required_encryption_context_keys: List[str] = ["requiredKey1", "requiredKey2"] - #// 4. Create the AWS KMS keyring. + # 4. Create the AWS KMS keyring. mpl: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( config=MaterialProvidersConfig() ) @@ -77,7 +83,7 @@ def encrypt_and_decrypt_with_keyring( ) kms_keyring: IKeyring = mpl.create_aws_kms_keyring(keyring_input) - #// 5. Create the required encryption context CMM. + # 5. Create the required encryption context CMM. underlying_cmm: ICryptographicMaterialsManager = \ mpl.create_default_cryptographic_materials_manager( CreateDefaultCryptographicMaterialsManagerInput( @@ -100,16 +106,16 @@ def encrypt_and_decrypt_with_keyring( encryption_context=encryption_context ) - # // 7. Reproduce the encryption context. - # // The reproduced encryption context MUST contain a value for - # // every key in the configured required encryption context keys during encryption with - # // Required Encryption Context CMM. + # 7. Reproduce the encryption context. + # The reproduced encryption context MUST contain a value for + # every key in the configured required encryption context keys during encryption with + # Required Encryption Context CMM. reproduced_encryption_context: Dict[str, str] = { "requiredKey1": "requiredValue1", "requiredKey2": "requiredValue2", } - # # 8. Decrypt the data + # 8. Decrypt the data plaintext_bytes_A, _ = client.decrypt( source=ciphertext, materials_manager=required_ec_cmm, @@ -117,13 +123,13 @@ def encrypt_and_decrypt_with_keyring( ) assert plaintext_bytes_A == EXAMPLE_DATA - # 9. If we don't provide the required encryption context, + # 9. Extra: Demonstrate that if we don't provide the required encryption context, # decryption will fail. try: plaintext_bytes_A, _ = client.decrypt( source=ciphertext, materials_manager=required_ec_cmm, - # no encryption context while using required encryption context CMM makes decryption fail + # No encryption context while using required encryption context CMM makes decryption fail. ) raise Exception("If this exception is raised, decryption somehow succeeded!") except AWSEncryptionSDKClientError: diff --git a/examples/test/keyrings/test_i_hierarchical_keyring.py b/examples/test/keyrings/test_i_hierarchical_keyring.py index 4cae478d7..c4583534a 100644 --- a/examples/test/keyrings/test_i_hierarchical_keyring.py +++ b/examples/test/keyrings/test_i_hierarchical_keyring.py @@ -1,6 +1,6 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 -"""Unit test suite for the hierarchical keyring example.""" +"""Test suite for the hierarchical keyring example.""" import pytest from ...src.keyrings.hierarchical_keyring import encrypt_and_decrypt_with_keyring diff --git a/examples/test/keyrings/test_i_required_encryption_context_cmm.py b/examples/test/keyrings/test_i_required_encryption_context_cmm.py index 9512a06ee..724705faa 100644 --- a/examples/test/keyrings/test_i_required_encryption_context_cmm.py +++ b/examples/test/keyrings/test_i_required_encryption_context_cmm.py @@ -1,6 +1,6 @@ # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 -"""Unit test suite for the hierarchical keyring example.""" +"""Test suite for the required encryption context CMM example.""" import pytest from ...src.keyrings.required_encryption_context_cmm import encrypt_and_decrypt_with_keyring diff --git a/examples/test/test_i_basic_encryption.py b/examples/test/test_i_basic_encryption.py index 5f509800e..f2a4fab51 100644 --- a/examples/test/test_i_basic_encryption.py +++ b/examples/test/test_i_basic_encryption.py @@ -23,5 +23,5 @@ def test_cycle_string(): plaintext = static_plaintext - cmk_arn = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + cmk_arn = get_cmk_arn() cycle_string(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 7e6ede8cd..34ba01c59 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -80,8 +80,8 @@ CreateDefaultCryptographicMaterialsManagerInput, ) from aws_cryptographic_materialproviders.mpl.references import ( - ICryptographicMaterialsManager, - IKeyring, + ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, + IKeyring as MPL_IKeyring, ) _HAS_MPL = True @@ -141,7 +141,7 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes default=None, validator=attr.validators.optional( attr.validators.instance_of( - (CryptoMaterialsManager, ICryptographicMaterialsManager) + (CryptoMaterialsManager, MPL_ICryptographicMaterialsManager) ) ) ) @@ -161,7 +161,7 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes if _HAS_MPL: # Keyrings are only available if the MPL is installed in the runtime keyring = attr.ib( - hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(IKeyring)) + hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(MPL_IKeyring)) ) source_length = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(six.integer_types)) @@ -202,7 +202,7 @@ def _has_mpl_attrs_post_init(self): # If the provided materials_manager is directly from the MPL, wrap it in a native interface # for internal use. elif (self.materials_manager is not None - and isinstance(self.materials_manager, ICryptographicMaterialsManager)): + and isinstance(self.materials_manager, MPL_ICryptographicMaterialsManager)): self.materials_manager = CryptoMaterialsManagerFromMPL(self.materials_manager) def _no_mpl_attrs_post_init(self): @@ -437,10 +437,10 @@ class EncryptorConfig(_ClientConfig): :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library + :param keyring: `MPL_IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.mpl.references.IKeyring + aws_cryptographic_materialproviders.mpl.references.MPL_IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -492,10 +492,10 @@ class StreamEncryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library + :param keyring: `MPL_IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.mpl.references.IKeyring + aws_cryptographic_materialproviders.mpl.references.MPL_IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -878,10 +878,10 @@ class DecryptorConfig(_ClientConfig): :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption (either `keyring`, `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library + :param keyring: `MPL_IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.mpl.references.IKeyring + aws_cryptographic_materialproviders.mpl.references.MPL_IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -926,10 +926,10 @@ class StreamDecryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library + :param keyring: `MPL_IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.mpl.references.IKeyring + aws_cryptographic_materialproviders.mpl.references.MPL_IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -1000,7 +1000,7 @@ def _read_header(self): ) decryption_materials = self.config.materials_manager.decrypt_materials(request=decrypt_materials_request) - + # If the materials_manager passed required_encryption_context_keys, # get the items out of the encryption_context with the keys. # The items are used in header validation. From 8415c2cbb2eb138e330816374dccc0f15cce6a38 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 17:00:17 -0800 Subject: [PATCH 147/172] cleanup --- src/aws_encryption_sdk/streaming_client.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 34ba01c59..953a82a66 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -437,10 +437,10 @@ class EncryptorConfig(_ClientConfig): :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `MPL_IKeyring` from the aws_cryptographic_materialproviders library + :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.mpl.references.MPL_IKeyring + aws_cryptographic_materialproviders.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -492,10 +492,10 @@ class StreamEncryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `MPL_IKeyring` from the aws_cryptographic_materialproviders library + :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.mpl.references.MPL_IKeyring + aws_cryptographic_materialproviders.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -878,10 +878,10 @@ class DecryptorConfig(_ClientConfig): :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption (either `keyring`, `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `MPL_IKeyring` from the aws_cryptographic_materialproviders library + :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.mpl.references.MPL_IKeyring + aws_cryptographic_materialproviders.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -926,10 +926,10 @@ class StreamDecryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider - :param keyring: `MPL_IKeyring` from the aws_cryptographic_materialproviders library + :param keyring: `IKeyring` from the aws_cryptographic_materialproviders library which handles encryption and decryption :type keyring: - aws_cryptographic_materialproviders.mpl.references.MPL_IKeyring + aws_cryptographic_materialproviders.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: From 20bdaffca7b83444348fdfb0e6abab7c3af5c8c9 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 18:21:42 -0800 Subject: [PATCH 148/172] cleanup --- src/aws_encryption_sdk/streaming_client.py | 2 +- .../test_streaming_client_stream_decryptor.py | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 953a82a66..4a742b91f 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -672,7 +672,7 @@ def _write_header(self): # If there is _required_encryption_context, # serialize it, then authenticate it - if self._required_encryption_context is not None: + if hasattr(self, "_required_encryption_context"): required_ec_serialized = aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( self._required_encryption_context ) diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index e06cad308..4929646b5 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -193,6 +193,9 @@ def test_read_header(self, mock_derive_datakey, mock_decrypt_materials_request, test_decryptor.source_stream = ct_stream test_decryptor._stream_length = len(VALUES["data_128"]) + # Mock: hasattr(self.config, "encryption_context") returns False + del test_decryptor.config.encryption_context + test_header, test_header_auth = test_decryptor._read_header() self.mock_deserialize_header.assert_called_once_with(ct_stream, None) @@ -230,6 +233,45 @@ def test_read_header(self, mock_derive_datakey, mock_decrypt_materials_request, assert test_header is self.mock_header assert test_header_auth is sentinel.header_auth + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.Verifier") + # Given: no MPL + @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") + def test_GIVEN_verification_key_AND_no_mpl_WHEN_read_header_THEN_calls_from_key_bytes( + self, + mock_verifier, + mock_decrypt_materials_request, + *_, + ): + + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + ct_stream = io.BytesIO(VALUES["data_128"]) + mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=ct_stream, + commitment_policy=mock_commitment_policy, + ) + test_decryptor.source_stream = ct_stream + test_decryptor._stream_length = len(VALUES["data_128"]) + # Given: self.config has "encryption_context" + any_reproduced_ec = {"some": "ec"} + test_decryptor.config.encryption_context = any_reproduced_ec + + # When: read header + test_decryptor._read_header() + + # Then: calls decrypt_materials with reproduced_encryption_context + mock_decrypt_materials_request.assert_called_once_with( + encrypted_data_keys=self.mock_header.encrypted_data_keys, + algorithm=self.mock_header.algorithm, + encryption_context=sentinel.encryption_context, + commitment_policy=mock_commitment_policy, + reproduced_encryption_context=any_reproduced_ec, + ) + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") @patch("aws_encryption_sdk.streaming_client.Verifier") From 6bf6094eff239f36c7dffb010e2b59cb7e948ab4 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 18:29:50 -0800 Subject: [PATCH 149/172] cleanup --- .../internal/formatting/serialize.py | 8 +++++--- .../materials_managers/mpl/materials.py | 1 - src/aws_encryption_sdk/streaming_client.py | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/aws_encryption_sdk/internal/formatting/serialize.py b/src/aws_encryption_sdk/internal/formatting/serialize.py index 344c94703..5a054989c 100644 --- a/src/aws_encryption_sdk/internal/formatting/serialize.py +++ b/src/aws_encryption_sdk/internal/formatting/serialize.py @@ -237,7 +237,7 @@ def _serialize_header_auth_v2( for all items whose keys are in the required_encryption_context list. This is ONLY processed if using the aws-cryptographic-materialproviders library AND its required encryption context CMM. (optional) - :type required_encryption_context_bytes: bytes + :type required_encryption_context_bytes: bytes :returns: Serialized header authentication data :rtype: bytes """ @@ -294,14 +294,16 @@ def serialize_header_auth( This is ONLY processed if using the aws-cryptographic-materialproviders library AND its required encryption context CMM AND if using the v2 message format. (optional) - :type required_encryption_context_bytes: bytes + :type required_encryption_context_bytes: bytes :returns: Serialized header authentication data :rtype: bytes """ if version == SerializationVersion.V1: return _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer) elif version == SerializationVersion.V2: - return _serialize_header_auth_v2(algorithm, header, data_encryption_key, signer, required_encryption_context_bytes) + return _serialize_header_auth_v2( + algorithm, header, data_encryption_key, signer, required_encryption_context_bytes + ) else: raise SerializationError("Unrecognized message format version: {}".format(version)) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index 5b066c7c7..5e4a66318 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -95,7 +95,6 @@ def data_encryption_key(self) -> DataKey: def signing_key(self) -> bytes: """Materials' signing key.""" return self.mpl_materials.signing_key - @property def required_encryption_context_keys(self) -> bytes: diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 4a742b91f..048a6caa3 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -198,7 +198,7 @@ def _has_mpl_attrs_post_init(self): # Wrap MPL error into the ESDK error type # so customers only have to catch ESDK error types. raise AWSEncryptionSDKClientError(mpl_exception) - + # If the provided materials_manager is directly from the MPL, wrap it in a native interface # for internal use. elif (self.materials_manager is not None @@ -673,9 +673,10 @@ def _write_header(self): # If there is _required_encryption_context, # serialize it, then authenticate it if hasattr(self, "_required_encryption_context"): - required_ec_serialized = aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( - self._required_encryption_context - ) + required_ec_serialized = \ + aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( + self._required_encryption_context + ) self.output_buffer += serialize_header_auth( version=self._header.version, algorithm=self._encryption_materials.algorithm, @@ -955,7 +956,7 @@ def _prep_message(self): self._prep_non_framed() self._message_prepped = True - def _read_header(self): + def _read_header(self): # noqa: C901 """Reads the message header from the input stream. :returns: tuple containing deserialized header and header_auth objects @@ -1056,9 +1057,10 @@ def _read_header(self): # The authenticated only encryption context is all encryption context key-value pairs where the # key exists in Required Encryption Context Keys. It is then serialized according to the # message header Key Value Pairs. - required_ec_serialized = aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( - self._required_encryption_context - ) + required_ec_serialized = \ + aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( + self._required_encryption_context + ) validate_header( header=header, From febe6dba05160087e6ac905f2f1582c79c56a1e8 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 18:38:46 -0800 Subject: [PATCH 150/172] cleanup --- .../keyrings/required_encryption_context_cmm.py | 14 +++----------- .../internal/formatting/serialize.py | 10 +++++----- .../materials_managers/mpl/materials.py | 4 ++++ src/aws_encryption_sdk/streaming_client.py | 17 +++++++++-------- .../test_streaming_client_stream_decryptor.py | 2 +- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/examples/src/keyrings/required_encryption_context_cmm.py b/examples/src/keyrings/required_encryption_context_cmm.py index f3d58c922..9f8de9976 100644 --- a/examples/src/keyrings/required_encryption_context_cmm.py +++ b/examples/src/keyrings/required_encryption_context_cmm.py @@ -14,25 +14,17 @@ from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.models import ( - CacheTypeDefault, CreateAwsKmsKeyringInput, CreateDefaultCryptographicMaterialsManagerInput, CreateRequiredEncryptionContextCMMInput, - DefaultCache, ) -from aws_cryptographic_materialproviders.mpl.references import ( - IKeyring, - ICryptographicMaterialsManager, -) -from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL -from typing import Dict +from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager, IKeyring +from typing import Dict, List import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError -from .example_branch_key_id_supplier import ExampleBranchKeyIdSupplier - # TODO-MPL: Remove this as part of removing PYTHONPATH hacks module_root_dir = '/'.join(__file__.split("/")[:-1]) @@ -98,7 +90,7 @@ def encrypt_and_decrypt_with_keyring( underlying_cmm=underlying_cmm, ) ) - + # 6. Encrypt the data ciphertext, _ = client.encrypt( source=EXAMPLE_DATA, diff --git a/src/aws_encryption_sdk/internal/formatting/serialize.py b/src/aws_encryption_sdk/internal/formatting/serialize.py index 5a054989c..66f4800de 100644 --- a/src/aws_encryption_sdk/internal/formatting/serialize.py +++ b/src/aws_encryption_sdk/internal/formatting/serialize.py @@ -223,7 +223,7 @@ def _serialize_header_auth_v2( header, data_encryption_key, signer=None, - required_encryption_context_bytes=None + required_ec_bytes=None ): """Creates serialized header authentication data for messages in serialization version V2. @@ -241,7 +241,7 @@ def _serialize_header_auth_v2( :returns: Serialized header authentication data :rtype: bytes """ - if required_encryption_context_bytes is None: + if required_ec_bytes is None: header_auth = encrypt( algorithm=algorithm, key=data_encryption_key, @@ -259,7 +259,7 @@ def _serialize_header_auth_v2( # be the encryption context in the encryption materials filtered to only contain key value # pairs listed in the encryption material's required encryption context keys serialized # according to the encryption context serialization specification. - associated_data=header + required_encryption_context_bytes, + associated_data=header + required_ec_bytes, iv=header_auth_iv(algorithm), ) output = struct.pack( @@ -277,7 +277,7 @@ def serialize_header_auth( header, data_encryption_key, signer=None, - required_encryption_context_bytes=None + required_ec_bytes=None ): """Creates serialized header authentication data. @@ -302,7 +302,7 @@ def serialize_header_auth( return _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer) elif version == SerializationVersion.V2: return _serialize_header_auth_v2( - algorithm, header, data_encryption_key, signer, required_encryption_context_bytes + algorithm, header, data_encryption_key, signer, required_ec_bytes ) else: raise SerializationError("Unrecognized message format version: {}".format(version)) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py index 5e4a66318..54ea21b39 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/materials.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -97,6 +97,8 @@ def signing_key(self) -> bytes: return self.mpl_materials.signing_key @property + # Pylint thinks this name is too long, but it's the best descriptor for this... + # pylint: disable=invalid-name def required_encryption_context_keys(self) -> bytes: """Materials' required encryption context keys.""" return self.mpl_materials.required_encryption_context_keys @@ -148,6 +150,8 @@ def encryption_context(self) -> Dict[str, str]: return self.mpl_materials.encryption_context @property + # Pylint thinks this name is too long, but it's the best descriptor for this... + # pylint: disable=invalid-name def required_encryption_context_keys(self) -> bytes: """Materials' required encryption context keys.""" return self.mpl_materials.required_encryption_context_keys diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 048a6caa3..bbae73bef 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -76,9 +76,7 @@ from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.errors import AwsCryptographicMaterialProvidersException - from aws_cryptographic_materialproviders.mpl.models import ( - CreateDefaultCryptographicMaterialsManagerInput, - ) + from aws_cryptographic_materialproviders.mpl.models import CreateDefaultCryptographicMaterialsManagerInput from aws_cryptographic_materialproviders.mpl.references import ( ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, IKeyring as MPL_IKeyring, @@ -631,11 +629,11 @@ def generate_header(self, message_id): if hasattr(self._encryption_materials, "required_encryption_context_keys"): self._required_encryption_context = {} self._stored_encryption_context = {} - for (k, v) in self._encryption_materials.encryption_context.items(): - if k in self._encryption_materials.required_encryption_context_keys: - self._required_encryption_context[k] = v + for (key, value) in self._encryption_materials.encryption_context.items(): + if key in self._encryption_materials.required_encryption_context_keys: + self._required_encryption_context[key] = value else: - self._stored_encryption_context[k] = v + self._stored_encryption_context[key] = value # Otherwise, store all encryption context with the message. else: self._stored_encryption_context = self._encryption_materials.encryption_context @@ -956,7 +954,10 @@ def _prep_message(self): self._prep_non_framed() self._message_prepped = True - def _read_header(self): # noqa: C901 + # TODO-MPL: Refactor this function, remove these linter disablers + # noqa: C901 + # pylint: disable=too-many-branches + def _read_header(self): """Reads the message header from the input stream. :returns: tuple containing deserialized header and header_auth objects diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index 4929646b5..2066dcbdb 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -238,7 +238,7 @@ def test_read_header(self, mock_derive_datakey, mock_decrypt_materials_request, @patch("aws_encryption_sdk.streaming_client.Verifier") # Given: no MPL @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") - def test_GIVEN_verification_key_AND_no_mpl_WHEN_read_header_THEN_calls_from_key_bytes( + def test_GIVEN_decrypt_config_has_ec_WHEN_read_header_THEN_calls_decrypt_materials_with_reproduced_ec( self, mock_verifier, mock_decrypt_materials_request, From dc8abca6925ce2cd139ef53c7f3b2c8f9b3e09ce Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 18:40:26 -0800 Subject: [PATCH 151/172] cleanup --- src/aws_encryption_sdk/streaming_client.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index bbae73bef..9488c9f08 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -954,10 +954,8 @@ def _prep_message(self): self._prep_non_framed() self._message_prepped = True - # TODO-MPL: Refactor this function, remove these linter disablers - # noqa: C901 - # pylint: disable=too-many-branches - def _read_header(self): + # TODO-MPL: Refactor this function, remove linter disablers + def _read_header(self): # noqa pylint: disable=too-many-branches """Reads the message header from the input stream. :returns: tuple containing deserialized header and header_auth objects From 8ff46f4520e510767d89365634c0e2da6a139f58 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 18:41:15 -0800 Subject: [PATCH 152/172] cleanup --- src/aws_encryption_sdk/streaming_client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 9488c9f08..ffef9cd3a 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -1006,9 +1006,9 @@ def _read_header(self): # noqa pylint: disable=too-many-branches # The items are used in header validation. if hasattr(decryption_materials, "required_encryption_context_keys"): self._required_encryption_context = {} - for (k, v) in decryption_materials.encryption_context.items(): - if k in decryption_materials.required_encryption_context_keys: - self._required_encryption_context[k] = v + for (key, value) in decryption_materials.encryption_context.items(): + if key in decryption_materials.required_encryption_context_keys: + self._required_encryption_context[key] = value else: self._required_encryption_context = None From aba7cccad05ba9ba60b49fb14a9ee3153354b66b Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 28 Feb 2024 18:42:53 -0800 Subject: [PATCH 153/172] cleanup --- src/aws_encryption_sdk/streaming_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index ffef9cd3a..fb0935ff2 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -681,7 +681,7 @@ def _write_header(self): header=self.output_buffer, data_encryption_key=self._derived_data_key, signer=self.signer, - required_encryption_context_bytes=required_ec_serialized, + required_ec_bytes=required_ec_serialized, ) # Otherwise, do not pass in any required encryption context else: From 40fecc05de1e42eca0d860ac2b04bee5670d504c Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 29 Feb 2024 10:54:22 -0800 Subject: [PATCH 154/172] all message format versions --- .../internal/formatting/serialize.py | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/aws_encryption_sdk/internal/formatting/serialize.py b/src/aws_encryption_sdk/internal/formatting/serialize.py index 66f4800de..310cf1436 100644 --- a/src/aws_encryption_sdk/internal/formatting/serialize.py +++ b/src/aws_encryption_sdk/internal/formatting/serialize.py @@ -189,7 +189,13 @@ def serialize_header(header, signer=None): raise SerializationError("Unrecognized message format version: {}".format(header.version)) -def _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer=None): +def _serialize_header_auth_v1( + algorithm, + header, + data_encryption_key, + signer=None, + required_ec_bytes=None +): """Creates serialized header authentication data for messages in serialization version V1. :param algorithm: Algorithm to use for encryption @@ -198,16 +204,35 @@ def _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer=Non :param bytes data_encryption_key: Data key with which to encrypt message :param signer: Cryptographic signer object (optional) :type signer: aws_encryption_sdk.Signer + :param required_encryption_context_bytes: Serialized encryption context items + for all items whose keys are in the required_encryption_context list. + This is ONLY processed if using the aws-cryptographic-materialproviders library + AND its required encryption context CMM. (optional) + :type required_encryption_context_bytes: bytes :returns: Serialized header authentication data :rtype: bytes """ - header_auth = encrypt( - algorithm=algorithm, - key=data_encryption_key, - plaintext=b"", - associated_data=header, - iv=header_auth_iv(algorithm), - ) + if required_ec_bytes is None: + header_auth = encrypt( + algorithm=algorithm, + key=data_encryption_key, + plaintext=b"", + associated_data=header, + iv=header_auth_iv(algorithm), + ) + else: + header_auth = encrypt( + algorithm=algorithm, + key=data_encryption_key, + plaintext=b"", + # The AAD MUST be the concatenation of the serialized message header body and the serialization + # of encryption context to only authenticate. The encryption context to only authenticate MUST + # be the encryption context in the encryption materials filtered to only contain key value + # pairs listed in the encryption material's required encryption context keys serialized + # according to the encryption context serialization specification. + associated_data=header + required_ec_bytes, + iv=header_auth_iv(algorithm), + ) output = struct.pack( ">{iv_len}s{tag_len}s".format(iv_len=algorithm.iv_len, tag_len=algorithm.tag_len), header_auth.iv, @@ -292,8 +317,7 @@ def serialize_header_auth( :param required_encryption_context_bytes: Serialized encryption context items for all items whose keys are in the required_encryption_context list. This is ONLY processed if using the aws-cryptographic-materialproviders library - AND its required encryption context CMM - AND if using the v2 message format. (optional) + AND its required encryption context CMM. (optional) :type required_encryption_context_bytes: bytes :returns: Serialized header authentication data :rtype: bytes From 52043b9fd3c5cb3ce40b222f7cfe2e3e0427a771 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 29 Feb 2024 10:55:07 -0800 Subject: [PATCH 155/172] sync upstream --- src/aws_encryption_sdk/internal/formatting/serialize.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/aws_encryption_sdk/internal/formatting/serialize.py b/src/aws_encryption_sdk/internal/formatting/serialize.py index 310cf1436..9f1325f98 100644 --- a/src/aws_encryption_sdk/internal/formatting/serialize.py +++ b/src/aws_encryption_sdk/internal/formatting/serialize.py @@ -323,7 +323,9 @@ def serialize_header_auth( :rtype: bytes """ if version == SerializationVersion.V1: - return _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer) + return _serialize_header_auth_v1( + algorithm, header, data_encryption_key, signer, required_ec_bytes + ) elif version == SerializationVersion.V2: return _serialize_header_auth_v2( algorithm, header, data_encryption_key, signer, required_ec_bytes From 7374fcb51d1bd1c9d0efd6a4fe9db364f84610ef Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 26 Mar 2024 16:11:55 -0700 Subject: [PATCH 156/172] unit tests --- src/aws_encryption_sdk/streaming_client.py | 123 +++++----- .../unit/test_material_managers_mpl_cmm.py | 3 + .../test_material_managers_mpl_materials.py | 39 ++++ test/unit/test_serialize.py | 50 +++++ test/unit/test_streaming_client_configs.py | 58 +++-- .../test_streaming_client_stream_decryptor.py | 210 ++++++++++++++++++ .../test_streaming_client_stream_encryptor.py | 154 +++++++++++++ 7 files changed, 565 insertions(+), 72 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index fb0935ff2..4d45918e3 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -954,8 +954,72 @@ def _prep_message(self): self._prep_non_framed() self._message_prepped = True - # TODO-MPL: Refactor this function, remove linter disablers - def _read_header(self): # noqa pylint: disable=too-many-branches + def _create_decrypt_materials_request(self, header): + """ + Create a DecryptionMaterialsRequest based on whether + the StreamDecryptor was provided encryption_context on decrypt + (i.e. expects to use required encryption context CMM from the MPL). + """ + # If encryption_context is provided on decrypt, + # pass it to the DecryptionMaterialsRequest as reproduced_encryption_context + if hasattr(self.config, "encryption_context"): + return DecryptionMaterialsRequest( + encrypted_data_keys=header.encrypted_data_keys, + algorithm=header.algorithm, + encryption_context=header.encryption_context, + commitment_policy=self.config.commitment_policy, + reproduced_encryption_context=self.config.encryption_context + ) + return DecryptionMaterialsRequest( + encrypted_data_keys=header.encrypted_data_keys, + algorithm=header.algorithm, + encryption_context=header.encryption_context, + commitment_policy=self.config.commitment_policy, + ) + + def _validate_parsed_header( + self, + header, + header_auth, + raw_header, + ): + """ + Pass arguments from this StreamDecryptor to validate_header based on whether + the StreamDecryptor has the _required_encryption_context attribute + (i.e. is using the required encryption context CMM from the MPL). + """ + # If _required_encryption_context is present, + # serialize it and pass it to validate_header. + if hasattr(self, "_required_encryption_context") \ + and self._required_encryption_context is not None: + # The authenticated only encryption context is all encryption context key-value pairs where the + # key exists in Required Encryption Context Keys. It is then serialized according to the + # message header Key Value Pairs. + required_ec_serialized = \ + aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( + self._required_encryption_context + ) + + validate_header( + header=header, + header_auth=header_auth, + # When verifying the header, the AAD input to the authenticated encryption algorithm + # specified by the algorithm suite is the message header body and the serialized + # authenticated only encryption context. + raw_header=raw_header + required_ec_serialized, + data_key=self._derived_data_key + ) + else: + validate_header( + header=header, + header_auth=header_auth, + raw_header=raw_header, + data_key=self._derived_data_key + ) + + return header, header_auth + + def _read_header(self): """Reads the message header from the input stream. :returns: tuple containing deserialized header and header_auth objects @@ -981,24 +1045,7 @@ def _read_header(self): # noqa pylint: disable=too-many-branches ) ) - # If encryption_context is provided on decrypt, - # pass it to the DecryptionMaterialsRequest - if hasattr(self.config, "encryption_context"): - decrypt_materials_request = DecryptionMaterialsRequest( - encrypted_data_keys=header.encrypted_data_keys, - algorithm=header.algorithm, - encryption_context=header.encryption_context, - commitment_policy=self.config.commitment_policy, - reproduced_encryption_context=self.config.encryption_context - ) - else: - decrypt_materials_request = DecryptionMaterialsRequest( - encrypted_data_keys=header.encrypted_data_keys, - algorithm=header.algorithm, - encryption_context=header.encryption_context, - commitment_policy=self.config.commitment_policy, - ) - + decrypt_materials_request = self._create_decrypt_materials_request(header) decryption_materials = self.config.materials_manager.decrypt_materials(request=decrypt_materials_request) # If the materials_manager passed required_encryption_context_keys, @@ -1049,36 +1096,12 @@ def _read_header(self): # noqa pylint: disable=too-many-branches "Key commitment validation failed. Key identity does not match the identity asserted in the " "message. Halting processing of this message." ) - - # If _required_encryption_context is present, - # serialize it and pass it to validate_header. - if self._required_encryption_context is not None: - # The authenticated only encryption context is all encryption context key-value pairs where the - # key exists in Required Encryption Context Keys. It is then serialized according to the - # message header Key Value Pairs. - required_ec_serialized = \ - aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( - self._required_encryption_context - ) - - validate_header( - header=header, - header_auth=header_auth, - # When verifying the header, the AAD input to the authenticated encryption algorithm - # specified by the algorithm suite is the message header body and the serialized - # authenticated only encryption context. - raw_header=raw_header + required_ec_serialized, - data_key=self._derived_data_key - ) - else: - validate_header( - header=header, - header_auth=header_auth, - raw_header=raw_header, - data_key=self._derived_data_key - ) - - return header, header_auth + + return self._validate_parsed_header( + header=header, + header_auth=header_auth, + raw_header=raw_header, + ) def _prep_non_framed(self): """Prepare the opening data for a non-framed message.""" diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index 80d6f00ee..0551e8f30 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -38,6 +38,7 @@ mock_mpl_cmm = MagicMock(__class__=MPL_ICryptographicMaterialsManager) mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) +mock_reproduced_encryption_context = MagicMock(__class_=dict) mock_edk = MagicMock(__class__=Native_EncryptedDataKey) @@ -259,6 +260,7 @@ def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_reques for mock_edks in [no_mock_edks, one_mock_edk, two_mock_edks]: mock_decryption_materials_request.encrypted_data_keys = mock_edks + mock_decryption_materials_request.reproduced_encryption_context = mock_reproduced_encryption_context # When: _create_mpl_decrypt_materials_input_from_request output = CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request( @@ -271,6 +273,7 @@ def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_reques assert output.algorithm_suite_id == mock_algorithm_id assert output.commitment_policy == mock_commitment_policy assert output.encryption_context == mock_decryption_materials_request.encryption_context + assert output.reproduced_encryption_context == mock_reproduced_encryption_context assert len(output.encrypted_data_keys) == len(mock_edks) for i in range(len(output.encrypted_data_keys)): diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index 9e76556a2..94136f806 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -160,6 +160,19 @@ def test_GIVEN_valid_signing_key_WHEN_EncryptionMaterials_get_signing_key_THEN_r assert output == mock_signing_key +def test_GIVEN_valid_required_encryption_context_keys_WHEN_EncryptionMaterials_get_required_encryption_context_keys_THEN_returns_required_encryption_context_keys(): + # Given: valid required encryption context keys + mock_required_encryption_context_keys = MagicMock(__class__=bytes) + mock_mpl_encryption_materials.required_encryption_context_keys = mock_required_encryption_context_keys + + # When: get required encryption context keys + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.required_encryption_context_keys + + # Then: returns required encryption context keys + assert output == mock_required_encryption_context_keys + + def test_GIVEN_valid_data_key_WHEN_DecryptionMaterials_get_data_key_THEN_returns_data_key(): # Given: valid MPL data key mock_data_key = MagicMock(__class__=bytes) @@ -187,3 +200,29 @@ def test_GIVEN_valid_verification_key_WHEN_DecryptionMaterials_get_verification_ # Then: returns verification key assert output == mock_verification_key + + +def test_GIVEN_valid_encryption_context_WHEN_DecryptionMaterials_get_encryption_context_THEN_returns_encryption_context(): + # Given: valid encryption context + mock_encryption_context = MagicMock(__class__=Dict[str, str]) + mock_mpl_decrypt_materials.encryption_context = mock_encryption_context + + # When: get encryption context + mpl_decryption_materials = DecryptionMaterialsFromMPL(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.encryption_context + + # Then: returns valid encryption context + assert output == mock_encryption_context + + +def test_GIVEN_valid_required_encryption_context_keys_WHEN_DecryptionMaterials_get_required_encryption_context_keys_THEN_returns_required_encryption_context_keys(): + # Given: valid required encryption context keys + mock_required_encryption_context_keys = MagicMock(__class__=bytes) + mock_mpl_decrypt_materials.required_encryption_context_keys = mock_required_encryption_context_keys + + # When: get required encryption context keys + mpl_decryption_materials = DecryptionMaterialsFromMPL(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.required_encryption_context_keys + + # Then: returns required encryption context keys + assert output == mock_required_encryption_context_keys diff --git a/test/unit/test_serialize.py b/test/unit/test_serialize.py index 56da114b4..5e294bf51 100644 --- a/test/unit/test_serialize.py +++ b/test/unit/test_serialize.py @@ -79,6 +79,7 @@ def apply_fixtures(self): "aws_encryption_sdk.internal.formatting.serialize.aws_encryption_sdk.internal.utils.validate_frame_length" ) self.mock_valid_frame_length = self.mock_valid_frame_length_patcher.start() + self.mock_required_ec_bytes = MagicMock() # Set up mock signer self.mock_signer = MagicMock() self.mock_signer.update.return_value = None @@ -167,6 +168,31 @@ def test_serialize_header_auth_v1_no_signer(self): data_encryption_key=VALUES["data_key_obj"], ) + @patch("aws_encryption_sdk.internal.formatting.serialize.header_auth_iv") + def test_GIVEN_required_ec_bytes_WHEN_serialize_header_auth_v1_THEN_aad_has_required_ec_bytes(self, mock_header_auth_iv): + """Validate that the _create_header_auth function + behaves as expected for SerializationVersion.V1 + when required_ec_bytes are provided. + """ + self.mock_encrypt.return_value = VALUES["header_auth_base"] + test = aws_encryption_sdk.internal.formatting.serialize.serialize_header_auth( + version=SerializationVersion.V1, + algorithm=self.mock_algorithm, + header=VALUES["serialized_header"], + data_encryption_key=sentinel.encryption_key, + signer=self.mock_signer, + required_ec_bytes=self.mock_required_ec_bytes, + ) + self.mock_encrypt.assert_called_once_with( + algorithm=self.mock_algorithm, + key=sentinel.encryption_key, + plaintext=b"", + associated_data=VALUES["serialized_header"] + self.mock_required_ec_bytes, + iv=mock_header_auth_iv.return_value, + ) + self.mock_signer.update.assert_called_once_with(VALUES["serialized_header_auth"]) + assert test == VALUES["serialized_header_auth"] + @patch("aws_encryption_sdk.internal.formatting.serialize.header_auth_iv") def test_serialize_header_auth_v2(self, mock_header_auth_iv): """Validate that the _create_header_auth function @@ -203,6 +229,30 @@ def test_serialize_header_auth_v2_no_signer(self): data_encryption_key=VALUES["data_key_obj"], ) + @patch("aws_encryption_sdk.internal.formatting.serialize.header_auth_iv") + def test_GIVEN_required_ec_bytes_WHEN_serialize_header_auth_v2_THEN_aad_has_required_ec_bytes(self, mock_header_auth_iv): + """Validate that the _create_header_auth function + behaves as expected for SerializationVersion.V2. + """ + self.mock_encrypt.return_value = VALUES["header_auth_base"] + test = aws_encryption_sdk.internal.formatting.serialize.serialize_header_auth( + version=SerializationVersion.V2, + algorithm=self.mock_algorithm, + header=VALUES["serialized_header_v2_committing"], + data_encryption_key=sentinel.encryption_key, + signer=self.mock_signer, + required_ec_bytes=self.mock_required_ec_bytes, + ) + self.mock_encrypt.assert_called_once_with( + algorithm=self.mock_algorithm, + key=sentinel.encryption_key, + plaintext=b"", + associated_data=VALUES["serialized_header_v2_committing"] + self.mock_required_ec_bytes, + iv=mock_header_auth_iv.return_value, + ) + self.mock_signer.update.assert_called_once_with(VALUES["serialized_header_auth_v2"]) + assert test == VALUES["serialized_header_auth_v2"] + def test_serialize_non_framed_open(self): """Validate that the serialize_non_framed_open function behaves as expected. diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 18886f65b..6ddba3f60 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -15,7 +15,7 @@ import pytest import six -from mock import patch +from mock import MagicMock, patch from aws_encryption_sdk import CommitmentPolicy from aws_encryption_sdk.internal.defaults import ALGORITHM, FRAME_LENGTH, LINE_LENGTH @@ -33,7 +33,10 @@ # Ideally, this logic would be based on mocking imports and testing logic, # but doing that introduces errors that cause other tests to fail. try: - from aws_cryptographic_materialproviders.mpl.references import IKeyring + from aws_cryptographic_materialproviders.mpl.references import ( + ICryptographicMaterialsManager, + IKeyring, + ) HAS_MPL = True from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL @@ -236,24 +239,21 @@ def test_client_configs_with_mpl( assert test.materials_manager is not None # If materials manager was provided, it should be directly used - if hasattr(kwargs, "materials_manager"): + if "materials_manager" in kwargs: assert kwargs["materials_manager"] == test.materials_manager - # If MPL keyring was provided, it should be wrapped in MPL materials manager - if hasattr(kwargs, "keyring"): - assert test.keyring is not None - assert test.keyring == kwargs["keyring"] - assert isinstance(test.keyring, IKeyring) - assert isinstance(test.materials_manager, CryptoMaterialsManagerFromMPL) - # If native key_provider was provided, it should be wrapped in native materials manager - if hasattr(kwargs, "key_provider"): + elif "key_provider" in kwargs: assert test.key_provider is not None assert test.key_provider == kwargs["key_provider"] assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) + else: + raise ValueError(f"Test did not find materials_manager or key_provider. {kwargs}") + -# This needs its own test; pytest parametrize cannot use a conditionally-loaded type +# This is an addition to test_client_configs_with_mpl; +# This needs its own test; pytest's parametrize cannot use a conditionally-loaded type (IKeyring) @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") def test_keyring_client_config_with_mpl( ): @@ -265,16 +265,30 @@ def test_keyring_client_config_with_mpl( test = _ClientConfig(**kwargs) - # In all cases, config should have a materials manager assert test.materials_manager is not None - # If materials manager was provided, it should be directly used - if hasattr(kwargs, "materials_manager"): - assert kwargs["materials_manager"] == test.materials_manager + assert test.keyring is not None + assert test.keyring == kwargs["keyring"] + assert isinstance(test.keyring, IKeyring) + assert isinstance(test.materials_manager, CryptoMaterialsManagerFromMPL) + + +# This is an addition to test_client_configs_with_mpl; +# This needs its own test; pytest's parametrize cannot use a conditionally-loaded type (MPL CMM) +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_mpl_cmm_client_config_with_mpl( +): + mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) + kwargs = { + "source": b"", + "materials_manager": mock_mpl_cmm, + "commitment_policy": CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + } + + test = _ClientConfig(**kwargs) - # If MPL keyring was provided, it should be wrapped in MPL materials manager - if hasattr(kwargs, "keyring"): - assert test.keyring is not None - assert test.keyring == kwargs["keyring"] - assert isinstance(test.keyring, IKeyring) - assert isinstance(test.materials_manager, CryptoMaterialsManagerFromMPL) + assert test.materials_manager is not None + # Assert that the MPL CMM is wrapped in the native interface + assert isinstance(test.materials_manager, CryptoMaterialsManagerFromMPL) + # Assert the MPL CMM is used by the native interface + assert test.materials_manager.mpl_cmm == mock_mpl_cmm diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index 2066dcbdb..74d01e5bd 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -924,3 +924,213 @@ def test_close_no_footer(self, mock_close): with pytest.raises(SerializationError) as excinfo: test_decryptor.close() excinfo.match("Footer not read") + + @patch("aws_encryption_sdk.streaming_client.validate_header") + def test_GIVEN_does_not_have_required_EC_WHEN_validate_parsed_header_THEN_validate_header( + self, + mock_validate_header + ): + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + test_decryptor._derived_data_key = sentinel.derived_data_key + # Given: test_decryptor does not have _required_encryption_context attribute + # When: _validate_parsed_header + test_decryptor._validate_parsed_header( + header=self.mock_header, + header_auth=sentinel.header_auth, + raw_header=self.mock_raw_header + ) + # Then: validate_header + mock_validate_header.assert_called_once_with( + header=self.mock_header, + header_auth=sentinel.header_auth, + raw_header=self.mock_raw_header, + data_key=sentinel.derived_data_key, + ) + + @patch("aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context") + @patch("aws_encryption_sdk.streaming_client.validate_header") + def test_GIVEN_has_required_EC_WHEN_validate_parsed_header_THEN_validate_header_with_serialized_required_EC( + self, + mock_validate_header, + mock_serialize_encryption_context, + ): + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + test_decryptor._derived_data_key = sentinel.derived_data_key + # Given: test_decryptor has _required_encryption_context attribute + mock_required_ec = MagicMock(__class__=dict) + test_decryptor._required_encryption_context = mock_required_ec + mock_serialized_required_ec = MagicMock(__class__=bytes) + mock_serialize_encryption_context.return_value = mock_serialized_required_ec + # When: _validate_parsed_header + test_decryptor._validate_parsed_header( + header=self.mock_header, + header_auth=sentinel.header_auth, + raw_header=self.mock_raw_header + ) + # Then: call validate_header with serialized required EC + mock_validate_header.assert_called_once_with( + header=self.mock_header, + header_auth=sentinel.header_auth, + raw_header=self.mock_raw_header + mock_serialized_required_ec, + data_key=sentinel.derived_data_key, + ) + + def test_GIVEN_config_has_EC_WHEN_create_decrypt_materials_request_THEN_provide_reproduced_EC( + self, + ): + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + + # Given: StreamDecryptor.config has encryption_context attribute + mock_reproduced_encryption_context = MagicMock(__class__=dict) + test_decryptor.config.encryption_context = mock_reproduced_encryption_context + # Type checking on header encryption context seems to require concrete instance, + # neither MagicMock nor sentinel value work + self.mock_header.encryption_context = {"some_key_to_pass_type_validation": "some_value"} + + # When: _create_decrypt_materials_request + output = test_decryptor._create_decrypt_materials_request( + header=self.mock_header, + ) + + # Then: decrypt_materials_request has reproduced_encryption_context attribute + assert hasattr(output, "reproduced_encryption_context") + assert output.reproduced_encryption_context == mock_reproduced_encryption_context + + def test_GIVEN_config_does_not_have_EC_WHEN_create_decrypt_materials_request_THEN_request_does_not_have_reproduced_EC( + self, + ): + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + + # Given: StreamDecryptor.config does not have an encryption_context attribute + del test_decryptor.config.encryption_context + # Type checking on header encryption context seems to require concrete instance, + # neither MagicMock nor sentinel value work + self.mock_header.encryption_context = {"some_key_to_pass_type_validation": "some_value"} + + # When: _create_decrypt_materials_request + output = test_decryptor._create_decrypt_materials_request( + header=self.mock_header, + ) + + # Then: decrypt_materials_request.reproduced_encryption_context is None + assert output.reproduced_encryption_context is None + + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.Verifier") + def test_GIVEN_materials_has_no_required_encryption_context_keys_attr_WHEN_read_header_THEN_required_EC_is_None( + self, + mock_verifier, + *_ + ): + + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + + # Given: decryption_materials does not have a required_encryption_context_keys attribute + del self.mock_decrypt_materials.required_encryption_context_keys + + # When: _read_header + test_decryptor._read_header() + + # Then: StreamDecryptor._required_encryption_context is None + assert test_decryptor._required_encryption_context is None + + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.Verifier") + def test_GIVEN_materials_has_required_encryption_context_keys_attr_WHEN_read_header_THEN_creates_correct_required_EC( + self, + mock_verifier, + *_ + ): + required_encryption_context_keys_values = [ + # Case of empty encryption context list is not allowed; + # if a list is provided, it must be non-empty. + # The MPL enforces this behavior on construction. + ["one_key"], + ["one_key", "two_key"], + ["one_key", "two_key", "red_key"], + ["one_key", "two_key", "red_key", "blue_key"], + ] + + encryption_context_values = [ + {}, + {"one_key": "some_value"}, + { + "one_key": "some_value", + "two_key": "some_other_value", + }, + { + "one_key": "some_value", + "two_key": "some_other_value", + "red_key": "some_red_value", + }, + { + "one_key": "some_value", + "two_key": "some_other_value", + "red_key": "some_red_value", + "blue_key": "some_blue_value", + } + ] + + for required_encryption_context_keys in required_encryption_context_keys_values: + + # Given: decryption_materials has required_encryption_context_keys + self.mock_decrypt_materials.required_encryption_context_keys = \ + required_encryption_context_keys + + for encryption_context in encryption_context_values: + + self.mock_decrypt_materials.encryption_context = encryption_context + + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + + # When: _read_header + test_decryptor._read_header() + + # Then: Assert correctness of partitioned EC + for k in encryption_context: + # If a key is in required_encryption_context_keys, then ... + if k in required_encryption_context_keys: + # ... its EC is in the StreamEncryptor._required_encryption_context + assert k in test_decryptor._required_encryption_context + # If a key is NOT in required_encryption_context_keys, then ... + else: + # ... its EC is NOT in the StreamEncryptor._required_encryption_context + assert k not in test_decryptor._required_encryption_context diff --git a/test/unit/test_streaming_client_stream_encryptor.py b/test/unit/test_streaming_client_stream_encryptor.py index e43752689..2d18f67ce 100644 --- a/test/unit/test_streaming_client_stream_encryptor.py +++ b/test/unit/test_streaming_client_stream_encryptor.py @@ -451,6 +451,113 @@ def test_GIVEN_has_mpl_AND_has_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_sig encoding=serialization.Encoding.PEM ) + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_message_THEN_paritions_stored_and_required_EC(self): + # Create explicit values to explicitly test logic in smaller cases + required_encryption_context_keys_values = [ + # Case of empty encryption context list is not allowed; + # if a list is provided, it must be non-empty. + # The MPL enforces this behavior on construction. + ["one_key"], + ["one_key", "two_key"], + ["one_key", "two_key", "red_key"], + ["one_key", "two_key", "red_key", "blue_key"], + ] + + encryption_context_values = [ + {}, + {"one_key": "some_value"}, + { + "one_key": "some_value", + "two_key": "some_other_value", + }, + { + "one_key": "some_value", + "two_key": "some_other_value", + "red_key": "some_red_value", + }, + { + "one_key": "some_value", + "two_key": "some_other_value", + "red_key": "some_red_value", + "blue_key": "some_blue_value", + } + ] + + self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 + + for required_encryption_context_keys in required_encryption_context_keys_values: + + # Given: encryption context has required_encryption_context_keys + self.mock_encryption_materials.required_encryption_context_keys = \ + required_encryption_context_keys + + for encryption_context in encryption_context_values: + self.mock_encryption_materials.encryption_context = encryption_context + + test_encryptor = StreamEncryptor( + source=VALUES["data_128"], + materials_manager=self.mock_mpl_materials_manager, + frame_length=self.mock_frame_length, + algorithm=Algorithm.AES_128_GCM_IV12_TAG16, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.content_type = ContentType.FRAMED_DATA + # When: prep_message + test_encryptor._prep_message() + + # Then: Assert correctness of partitioned EC + for k in encryption_context: + # If a key is in required_encryption_context_keys, then + if k in required_encryption_context_keys: + # 1) Its EC is in the StreamEncryptor._required_encryption_context + assert k in test_encryptor._required_encryption_context + # 2) Its EC is NOT in the StreamEncryptor._stored_encryption_context + assert k not in test_encryptor._stored_encryption_context + # If a key is NOT in required_encryption_context_keys, then + else: + # 1) Its EC is NOT in the StreamEncryptor._required_encryption_context + assert k not in test_encryptor._required_encryption_context + # 2) Its EC is in the StreamEncryptor._stored_encryption_context + assert k in test_encryptor._stored_encryption_context + + # Assert size(stored_EC) + size(required_EC) == size(EC) + # (i.e. every EC was sorted into one or the other) + assert len(test_encryptor._required_encryption_context) \ + + len(test_encryptor._stored_encryption_context) \ + == len(encryption_context) + + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_mpl_AND_encryption_materials_does_not_have_required_EC_keys_WHEN_prep_message_THEN_stored_EC_is_EC(self): + + self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 + + mock_encryption_context = MagicMock(__class__=dict) + self.mock_encryption_materials.encryption_context = mock_encryption_context + # Given: encryption materials does not have required encryption context keys + # (MagicMock default is to "make up" "Some" value here; this deletes that value) + del self.mock_encryption_materials.required_encryption_context_keys + + test_encryptor = StreamEncryptor( + source=VALUES["data_128"], + materials_manager=self.mock_mpl_materials_manager, + frame_length=self.mock_frame_length, + algorithm=Algorithm.AES_128_GCM_IV12_TAG16, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.content_type = ContentType.FRAMED_DATA + # When: prep_message + test_encryptor._prep_message() + + # Then: _stored_encryption_context is the provided encryption_context + assert test_encryptor._stored_encryption_context == mock_encryption_context + # Then: _required_encryption_context is None + assert test_encryptor._required_encryption_context is None + def test_prep_message_no_signer(self): self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 test_encryptor = StreamEncryptor( @@ -575,6 +682,53 @@ def test_write_header(self): ) assert test_encryptor.output_buffer == b"1234567890" + @patch("aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context") + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_mpl_AND_has_required_EC_WHEN_write_header_THEN_adds_serialized_required_ec_to_header_auth( + self, + serialize_encryption_context + ): + self.mock_serialize_header.return_value = b"12345" + self.mock_serialize_header_auth.return_value = b"67890" + pt_stream = io.BytesIO(self.plaintext) + test_encryptor = StreamEncryptor( + source=pt_stream, + materials_manager=self.mock_materials_manager, + algorithm=aws_encryption_sdk.internal.defaults.ALGORITHM, + frame_length=self.mock_frame_length, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.signer = sentinel.signer + test_encryptor.content_type = sentinel.content_type + test_encryptor._header = sentinel.header + sentinel.header.version = SerializationVersion.V1 + test_encryptor.output_buffer = b"" + test_encryptor._encryption_materials = self.mock_encryption_materials + test_encryptor._derived_data_key = sentinel.derived_data_key + + # Given: StreamEncryptor has _required_encryption_context + mock_required_ec = MagicMock(__class__=dict) + test_encryptor._required_encryption_context = mock_required_ec + mock_serialized_required_ec = MagicMock(__class__=bytes) + serialize_encryption_context.return_value = mock_serialized_required_ec + + # When: _write_header() + test_encryptor._write_header() + + self.mock_serialize_header.assert_called_once_with(header=test_encryptor._header, signer=sentinel.signer) + self.mock_serialize_header_auth.assert_called_once_with( + version=sentinel.header.version, + algorithm=self.mock_encryption_materials.algorithm, + header=b"12345", + data_encryption_key=sentinel.derived_data_key, + signer=sentinel.signer, + # Then: Pass serialized required EC to serialize_header_auth + required_ec_bytes=mock_serialized_required_ec, + ) + assert test_encryptor.output_buffer == b"1234567890" + @patch("aws_encryption_sdk.streaming_client.non_framed_body_iv") def test_prep_non_framed(self, mock_non_framed_iv): self.mock_serialize_non_framed_open.return_value = b"1234567890" From f9d60a8eeb54a916ca09fb660defc90e77606f03 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 26 Mar 2024 16:19:46 -0700 Subject: [PATCH 157/172] lint --- src/aws_encryption_sdk/streaming_client.py | 4 ++-- .../test_material_managers_mpl_materials.py | 6 +++--- test/unit/test_serialize.py | 10 ++++++++-- .../test_streaming_client_stream_decryptor.py | 10 ++++++---- .../test_streaming_client_stream_encryptor.py | 19 +++++++++++++------ 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 4d45918e3..98b54b3df 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -976,7 +976,7 @@ def _create_decrypt_materials_request(self, header): encryption_context=header.encryption_context, commitment_policy=self.config.commitment_policy, ) - + def _validate_parsed_header( self, header, @@ -1096,7 +1096,7 @@ def _read_header(self): "Key commitment validation failed. Key identity does not match the identity asserted in the " "message. Halting processing of this message." ) - + return self._validate_parsed_header( header=header, header_auth=header_auth, diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py index 94136f806..8d9052c0a 100644 --- a/test/mpl/unit/test_material_managers_mpl_materials.py +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -160,7 +160,7 @@ def test_GIVEN_valid_signing_key_WHEN_EncryptionMaterials_get_signing_key_THEN_r assert output == mock_signing_key -def test_GIVEN_valid_required_encryption_context_keys_WHEN_EncryptionMaterials_get_required_encryption_context_keys_THEN_returns_required_encryption_context_keys(): +def test_GIVEN_valid_required_encryption_context_keys_WHEN_EncryptionMaterials_get_required_encryption_context_keys_THEN_returns_required_encryption_context_keys(): # noqa pylint: disable=line-too-long # Given: valid required encryption context keys mock_required_encryption_context_keys = MagicMock(__class__=bytes) mock_mpl_encryption_materials.required_encryption_context_keys = mock_required_encryption_context_keys @@ -202,7 +202,7 @@ def test_GIVEN_valid_verification_key_WHEN_DecryptionMaterials_get_verification_ assert output == mock_verification_key -def test_GIVEN_valid_encryption_context_WHEN_DecryptionMaterials_get_encryption_context_THEN_returns_encryption_context(): +def test_GIVEN_valid_encryption_context_WHEN_DecryptionMaterials_get_encryption_context_THEN_returns_encryption_context(): # noqa pylint: disable=line-too-long # Given: valid encryption context mock_encryption_context = MagicMock(__class__=Dict[str, str]) mock_mpl_decrypt_materials.encryption_context = mock_encryption_context @@ -215,7 +215,7 @@ def test_GIVEN_valid_encryption_context_WHEN_DecryptionMaterials_get_encryption_ assert output == mock_encryption_context -def test_GIVEN_valid_required_encryption_context_keys_WHEN_DecryptionMaterials_get_required_encryption_context_keys_THEN_returns_required_encryption_context_keys(): +def test_GIVEN_valid_required_encryption_context_keys_WHEN_DecryptionMaterials_get_required_encryption_context_keys_THEN_returns_required_encryption_context_keys(): # noqa pylint: disable=line-too-long # Given: valid required encryption context keys mock_required_encryption_context_keys = MagicMock(__class__=bytes) mock_mpl_decrypt_materials.required_encryption_context_keys = mock_required_encryption_context_keys diff --git a/test/unit/test_serialize.py b/test/unit/test_serialize.py index 5e294bf51..06ac6126b 100644 --- a/test/unit/test_serialize.py +++ b/test/unit/test_serialize.py @@ -169,7 +169,10 @@ def test_serialize_header_auth_v1_no_signer(self): ) @patch("aws_encryption_sdk.internal.formatting.serialize.header_auth_iv") - def test_GIVEN_required_ec_bytes_WHEN_serialize_header_auth_v1_THEN_aad_has_required_ec_bytes(self, mock_header_auth_iv): + def test_GIVEN_required_ec_bytes_WHEN_serialize_header_auth_v1_THEN_aad_has_required_ec_bytes( + self, + mock_header_auth_iv, + ): """Validate that the _create_header_auth function behaves as expected for SerializationVersion.V1 when required_ec_bytes are provided. @@ -230,7 +233,10 @@ def test_serialize_header_auth_v2_no_signer(self): ) @patch("aws_encryption_sdk.internal.formatting.serialize.header_auth_iv") - def test_GIVEN_required_ec_bytes_WHEN_serialize_header_auth_v2_THEN_aad_has_required_ec_bytes(self, mock_header_auth_iv): + def test_GIVEN_required_ec_bytes_WHEN_serialize_header_auth_v2_THEN_aad_has_required_ec_bytes( + self, + mock_header_auth_iv, + ): """Validate that the _create_header_auth function behaves as expected for SerializationVersion.V2. """ diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index 74d01e5bd..e29f8d16d 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -11,6 +11,7 @@ # 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.streaming_client.StreamDecryptor""" +# noqa pylint: disable=too-many-lines import io import pytest @@ -1011,7 +1012,7 @@ def test_GIVEN_config_has_EC_WHEN_create_decrypt_materials_request_THEN_provide_ assert hasattr(output, "reproduced_encryption_context") assert output.reproduced_encryption_context == mock_reproduced_encryption_context - def test_GIVEN_config_does_not_have_EC_WHEN_create_decrypt_materials_request_THEN_request_does_not_have_reproduced_EC( + def test_GIVEN_config_does_not_have_EC_WHEN_create_decrypt_materials_request_THEN_request_does_not_have_reproduced_EC( # noqa pylint: disable=line-too-long self, ): self.mock_header.content_type = ContentType.FRAMED_DATA @@ -1066,7 +1067,7 @@ def test_GIVEN_materials_has_no_required_encryption_context_keys_attr_WHEN_read_ @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") @patch("aws_encryption_sdk.streaming_client.Verifier") - def test_GIVEN_materials_has_required_encryption_context_keys_attr_WHEN_read_header_THEN_creates_correct_required_EC( + def test_GIVEN_materials_has_required_encryption_context_keys_attr_WHEN_read_header_THEN_creates_correct_required_EC( # noqa pylint: disable=line-too-long self, mock_verifier, *_ @@ -1106,9 +1107,9 @@ def test_GIVEN_materials_has_required_encryption_context_keys_attr_WHEN_read_hea # Given: decryption_materials has required_encryption_context_keys self.mock_decrypt_materials.required_encryption_context_keys = \ required_encryption_context_keys - + for encryption_context in encryption_context_values: - + self.mock_decrypt_materials.encryption_context = encryption_context mock_verifier_instance = MagicMock() @@ -1130,6 +1131,7 @@ def test_GIVEN_materials_has_required_encryption_context_keys_attr_WHEN_read_hea if k in required_encryption_context_keys: # ... its EC is in the StreamEncryptor._required_encryption_context assert k in test_decryptor._required_encryption_context + assert test_decryptor._required_encryption_context[k] == encryption_context[k] # If a key is NOT in required_encryption_context_keys, then ... else: # ... its EC is NOT in the StreamEncryptor._required_encryption_context diff --git a/test/unit/test_streaming_client_stream_encryptor.py b/test/unit/test_streaming_client_stream_encryptor.py index 2d18f67ce..7d97d1b25 100644 --- a/test/unit/test_streaming_client_stream_encryptor.py +++ b/test/unit/test_streaming_client_stream_encryptor.py @@ -11,6 +11,7 @@ # 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.streaming_client.StreamEncryptor""" +# noqa pylint: disable=too-many-lines import io import pytest @@ -453,7 +454,9 @@ def test_GIVEN_has_mpl_AND_has_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_sig # Given: has MPL @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") - def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_message_THEN_paritions_stored_and_required_EC(self): + def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_message_THEN_paritions_stored_and_required_EC( # noqa pylint: disable=line-too-long + self + ): # Create explicit values to explicitly test logic in smaller cases required_encryption_context_keys_values = [ # Case of empty encryption context list is not allowed; @@ -492,7 +495,7 @@ def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_m # Given: encryption context has required_encryption_context_keys self.mock_encryption_materials.required_encryption_context_keys = \ required_encryption_context_keys - + for encryption_context in encryption_context_values: self.mock_encryption_materials.encryption_context = encryption_context @@ -514,6 +517,7 @@ def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_m if k in required_encryption_context_keys: # 1) Its EC is in the StreamEncryptor._required_encryption_context assert k in test_encryptor._required_encryption_context + assert test_encryptor._required_encryption_context[k] == encryption_context[k] # 2) Its EC is NOT in the StreamEncryptor._stored_encryption_context assert k not in test_encryptor._stored_encryption_context # If a key is NOT in required_encryption_context_keys, then @@ -522,16 +526,19 @@ def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_m assert k not in test_encryptor._required_encryption_context # 2) Its EC is in the StreamEncryptor._stored_encryption_context assert k in test_encryptor._stored_encryption_context - + assert test_encryptor._stored_encryption_context[k] == encryption_context[k] + # Assert size(stored_EC) + size(required_EC) == size(EC) # (i.e. every EC was sorted into one or the other) assert len(test_encryptor._required_encryption_context) \ + len(test_encryptor._stored_encryption_context) \ == len(encryption_context) - + # Given: has MPL @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") - def test_GIVEN_has_mpl_AND_encryption_materials_does_not_have_required_EC_keys_WHEN_prep_message_THEN_stored_EC_is_EC(self): + def test_GIVEN_has_mpl_AND_encryption_materials_does_not_have_required_EC_keys_WHEN_prep_message_THEN_stored_EC_is_EC( # noqa pylint: disable=line-too-long + self + ): self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 @@ -557,7 +564,7 @@ def test_GIVEN_has_mpl_AND_encryption_materials_does_not_have_required_EC_keys_W assert test_encryptor._stored_encryption_context == mock_encryption_context # Then: _required_encryption_context is None assert test_encryptor._required_encryption_context is None - + def test_prep_message_no_signer(self): self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 test_encryptor = StreamEncryptor( From 16725f86b103258123b231c5c09d9e30b4e2725c Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 26 Mar 2024 16:24:43 -0700 Subject: [PATCH 158/172] lint --- test/unit/test_streaming_client_configs.py | 5 +---- test/unit/test_streaming_client_stream_decryptor.py | 4 ++-- test/unit/test_streaming_client_stream_encryptor.py | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 6ddba3f60..ba69da704 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -33,10 +33,7 @@ # Ideally, this logic would be based on mocking imports and testing logic, # but doing that introduces errors that cause other tests to fail. try: - from aws_cryptographic_materialproviders.mpl.references import ( - ICryptographicMaterialsManager, - IKeyring, - ) + from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager, IKeyring HAS_MPL = True from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index e29f8d16d..cb003d868 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -1071,7 +1071,7 @@ def test_GIVEN_materials_has_required_encryption_context_keys_attr_WHEN_read_hea self, mock_verifier, *_ - ): + ): required_encryption_context_keys_values = [ # Case of empty encryption context list is not allowed; # if a list is provided, it must be non-empty. @@ -1126,7 +1126,7 @@ def test_GIVEN_materials_has_required_encryption_context_keys_attr_WHEN_read_hea test_decryptor._read_header() # Then: Assert correctness of partitioned EC - for k in encryption_context: + for k in encryption_context.keys(): # If a key is in required_encryption_context_keys, then ... if k in required_encryption_context_keys: # ... its EC is in the StreamEncryptor._required_encryption_context diff --git a/test/unit/test_streaming_client_stream_encryptor.py b/test/unit/test_streaming_client_stream_encryptor.py index 7d97d1b25..e456dfd21 100644 --- a/test/unit/test_streaming_client_stream_encryptor.py +++ b/test/unit/test_streaming_client_stream_encryptor.py @@ -512,7 +512,7 @@ def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_m test_encryptor._prep_message() # Then: Assert correctness of partitioned EC - for k in encryption_context: + for k in encryption_context.keys(): # If a key is in required_encryption_context_keys, then if k in required_encryption_context_keys: # 1) Its EC is in the StreamEncryptor._required_encryption_context From 1706db21586f81cb319a8096c7fc7f9a6dbd5164 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 26 Mar 2024 16:27:12 -0700 Subject: [PATCH 159/172] lint --- test/unit/test_streaming_client_stream_decryptor.py | 2 +- test/unit/test_streaming_client_stream_encryptor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index cb003d868..7b4e36995 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -1126,7 +1126,7 @@ def test_GIVEN_materials_has_required_encryption_context_keys_attr_WHEN_read_hea test_decryptor._read_header() # Then: Assert correctness of partitioned EC - for k in encryption_context.keys(): + for k, _ in encryption_context.items(): # If a key is in required_encryption_context_keys, then ... if k in required_encryption_context_keys: # ... its EC is in the StreamEncryptor._required_encryption_context diff --git a/test/unit/test_streaming_client_stream_encryptor.py b/test/unit/test_streaming_client_stream_encryptor.py index e456dfd21..5030acf16 100644 --- a/test/unit/test_streaming_client_stream_encryptor.py +++ b/test/unit/test_streaming_client_stream_encryptor.py @@ -512,7 +512,7 @@ def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_m test_encryptor._prep_message() # Then: Assert correctness of partitioned EC - for k in encryption_context.keys(): + for k, _ in encryption_context.items(): # If a key is in required_encryption_context_keys, then if k in required_encryption_context_keys: # 1) Its EC is in the StreamEncryptor._required_encryption_context From 06e08429c6204200442faa4165514ded0d3e0201 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 26 Mar 2024 16:33:39 -0700 Subject: [PATCH 160/172] lint --- test/unit/test_streaming_client_stream_decryptor.py | 4 ++-- test/unit/test_streaming_client_stream_encryptor.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index 7b4e36995..f38f413f4 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -1126,12 +1126,12 @@ def test_GIVEN_materials_has_required_encryption_context_keys_attr_WHEN_read_hea test_decryptor._read_header() # Then: Assert correctness of partitioned EC - for k, _ in encryption_context.items(): + for k, v in encryption_context.items(): # If a key is in required_encryption_context_keys, then ... if k in required_encryption_context_keys: # ... its EC is in the StreamEncryptor._required_encryption_context assert k in test_decryptor._required_encryption_context - assert test_decryptor._required_encryption_context[k] == encryption_context[k] + assert test_decryptor._required_encryption_context[k] == v # If a key is NOT in required_encryption_context_keys, then ... else: # ... its EC is NOT in the StreamEncryptor._required_encryption_context diff --git a/test/unit/test_streaming_client_stream_encryptor.py b/test/unit/test_streaming_client_stream_encryptor.py index 5030acf16..4df79e146 100644 --- a/test/unit/test_streaming_client_stream_encryptor.py +++ b/test/unit/test_streaming_client_stream_encryptor.py @@ -512,12 +512,12 @@ def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_m test_encryptor._prep_message() # Then: Assert correctness of partitioned EC - for k, _ in encryption_context.items(): + for k, v in encryption_context.items(): # If a key is in required_encryption_context_keys, then if k in required_encryption_context_keys: # 1) Its EC is in the StreamEncryptor._required_encryption_context assert k in test_encryptor._required_encryption_context - assert test_encryptor._required_encryption_context[k] == encryption_context[k] + assert test_encryptor._required_encryption_context[k] == v # 2) Its EC is NOT in the StreamEncryptor._stored_encryption_context assert k not in test_encryptor._stored_encryption_context # If a key is NOT in required_encryption_context_keys, then @@ -526,7 +526,7 @@ def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_m assert k not in test_encryptor._required_encryption_context # 2) Its EC is in the StreamEncryptor._stored_encryption_context assert k in test_encryptor._stored_encryption_context - assert test_encryptor._stored_encryption_context[k] == encryption_context[k] + assert test_encryptor._stored_encryption_context[k] == v # Assert size(stored_EC) + size(required_EC) == size(EC) # (i.e. every EC was sorted into one or the other) From 5ad8e3a9318bab554ce75df73ee6c29552e49874 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Mon, 15 Apr 2024 14:09:40 -0700 Subject: [PATCH 161/172] Update examples/src/keyrings/hierarchical_keyring.py --- examples/src/keyrings/hierarchical_keyring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index b75421359..9c77379cf 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -55,7 +55,7 @@ from .example_branch_key_id_supplier import ExampleBranchKeyIdSupplier -# TODO-MPL: Remove this as part of removing PYTHONPATH hacks +# TODO-MPL: Remove this as part of removing PYTHONPATH hacks. module_root_dir = '/'.join(__file__.split("/")[:-1]) sys.path.append(module_root_dir) From 280e038d0423528e824b1080c48383da5b5aecf1 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Tue, 16 Apr 2024 10:06:10 -0700 Subject: [PATCH 162/172] Update examples/src/keyrings/hierarchical_keyring.py --- examples/src/keyrings/hierarchical_keyring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index 9c77379cf..32a6cbf8b 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -26,7 +26,7 @@ This example requires access to the DDB Table where you are storing the Branch Keys. This table must be configured with the following primary key configuration: - Partition key is named -"partition_key" with type (S) - Sort key is named "sort_key" with type (S) +"partition_key" with type (S) - Sort key is named "sort_key" with type (S). This example also requires using a KMS Key. You need the following access on this key: - GenerateDataKeyWithoutPlaintext - Decrypt From 0dfeb5d87b96a50883d3bd0fe61eda9a858ab255 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Thu, 18 Apr 2024 15:29:58 -0700 Subject: [PATCH 163/172] Adding test for KMS keyring --- .../src/keyrings/aws_kms_keyring_example.py | 115 ++++++++++++++++++ .../test_i_aws_kms_keyring_example.py | 13 ++ 2 files changed, 128 insertions(+) create mode 100644 examples/src/keyrings/aws_kms_keyring_example.py create mode 100644 examples/test/keyrings/test_i_aws_kms_keyring_example.py diff --git a/examples/src/keyrings/aws_kms_keyring_example.py b/examples/src/keyrings/aws_kms_keyring_example.py new file mode 100644 index 000000000..20d656b75 --- /dev/null +++ b/examples/src/keyrings/aws_kms_keyring_example.py @@ -0,0 +1,115 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the KMS Keyring +""" +import sys + +import boto3 + +from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig +from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsKeyringInput +from aws_cryptographic_materialproviders.mpl.references import IKeyring +from typing import Dict + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + + +# TODO-MPL: Remove this as part of removing PYTHONPATH hacks. +module_root_dir = '/'.join(__file__.split("/")[:-1]) + +sys.path.append(module_root_dir) + +EXAMPLE_DATA: bytes = b"Hello World" + +def encrypt_and_decrypt_with_keyring( + kms_key_id: str +): + """Demonstrate an encrypt/decrypt cycle using an AWS KMS keyring.""" + + """ + 1. Instantiate the encryption SDK client. + This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + which enforces that this client only encrypts using committing algorithm suites and enforces + that this client will only decrypt encrypted messages that were created with a committing + algorithm suite. + This is the default commitment policy if you were to build the client as + `client = aws_encryption_sdk.EncryptionSDKClient()`. + """ + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + """ + 2. Create boto3 clients for KMS. + """ + kms_client = boto3.client('kms', region_name="us-west-2") + + """ + 3. Instantiate the Material Providers + """ + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + """ + 4. Create encryption context + Remember that your encryption context is NOT SECRET. + https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + """ + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + """ + 5. Create the KMS keyring + """ + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=keyring_input + ) + + """ + 6. Encrypt the data for the encryptionContext + """ + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=kms_keyring, + encryption_context=encryption_context + ) + + """ + 7. Demonstrate that the ciphertext and plaintext are different. + """ + assert ciphertext != EXAMPLE_DATA, "Ciphertext and plaintext data are the same. Invalid encryption" + + """ + 8. Decrypt your encrypted data using the same keyring you used on encrypt. + You do not need to specify the encryption context on decrypt + because the header of the encrypted message includes the encryption context. + """ + plaintext_bytes, dec_header = client.decrypt( + source=ciphertext, + keyring=kms_keyring + ) + + """ + 9. Demonstrate that the encryption context is correct in the decrypted message header + """ + for k, v in encryption_context.items(): + assert v == dec_header.encryption_context[k], "Encryption context does not match expected values" + + """ + 10. Demonstrate that the decrypted plaintext is identical to the original plaintext. + """ + assert plaintext_bytes == EXAMPLE_DATA \ No newline at end of file diff --git a/examples/test/keyrings/test_i_aws_kms_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_keyring_example.py new file mode 100644 index 000000000..ca1378db0 --- /dev/null +++ b/examples/test/keyrings/test_i_aws_kms_keyring_example.py @@ -0,0 +1,13 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS keyring example.""" +import pytest + +from ...src.keyrings.aws_kms_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + key_arn = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + encrypt_and_decrypt_with_keyring(key_arn) From 18328ad8de7177c2eb6be698985331464afb9946 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 19 Apr 2024 11:25:47 -0700 Subject: [PATCH 164/172] chore: KMS keyring example --- .../src/keyrings/aws_kms_keyring_example.py | 108 +++++++++--------- .../test_i_aws_kms_keyring_example.py | 6 +- .../keyrings/test_i_hierarchical_keyring.py | 5 +- 3 files changed, 63 insertions(+), 56 deletions(-) diff --git a/examples/src/keyrings/aws_kms_keyring_example.py b/examples/src/keyrings/aws_kms_keyring_example.py index 20d656b75..416521d75 100644 --- a/examples/src/keyrings/aws_kms_keyring_example.py +++ b/examples/src/keyrings/aws_kms_keyring_example.py @@ -2,63 +2,73 @@ # SPDX-License-Identifier: Apache-2.0 """ This example sets up the KMS Keyring + +The AWS KMS keyring uses symmetric encryption KMS keys to generate, encrypt and +decrypt data keys. This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also demonstrates some sanity checks for demonstration. +1. Ciphertext and plaintext data are not the same +2. Encryption context is correct in the decrypted message header +3. Decrypted plaintext value matches EXAMPLE_DATA + +AWS KMS keyrings can be used independently or in a multi-keyring with other keyrings +of the same or a different type. + +For more info on how to use KMS keyring, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html """ import sys - +from typing import Dict import boto3 from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsKeyringInput from aws_cryptographic_materialproviders.mpl.references import IKeyring -from typing import Dict import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy # TODO-MPL: Remove this as part of removing PYTHONPATH hacks. -module_root_dir = '/'.join(__file__.split("/")[:-1]) +MODULE_ROOT_DIR = '/'.join(__file__.split("/")[:-1]) -sys.path.append(module_root_dir) +sys.path.append(MODULE_ROOT_DIR) EXAMPLE_DATA: bytes = b"Hello World" + def encrypt_and_decrypt_with_keyring( kms_key_id: str ): - """Demonstrate an encrypt/decrypt cycle using an AWS KMS keyring.""" + """Demonstrate an encrypt/decrypt cycle using an AWS KMS keyring. + Usage: encrypt_and_decrypt_with_keyring(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use for encryption and + decryption of your data keys. + :type kms_key_id: string + + For more info on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id """ - 1. Instantiate the encryption SDK client. - This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, - which enforces that this client only encrypts using committing algorithm suites and enforces - that this client will only decrypt encrypted messages that were created with a committing - algorithm suite. - This is the default commitment policy if you were to build the client as - `client = aws_encryption_sdk.EncryptionSDKClient()`. - """ + + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. client = aws_encryption_sdk.EncryptionSDKClient( commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT ) - """ - 2. Create boto3 clients for KMS. - """ + # 2. Create a boto3 client for KMS. kms_client = boto3.client('kms', region_name="us-west-2") - """ - 3. Instantiate the Material Providers - """ - mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( - config=MaterialProvidersConfig() - ) - - """ - 4. Create encryption context - Remember that your encryption context is NOT SECRET. - https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - """ + # 3. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context encryption_context: Dict[str, str] = { "encryption": "context", "is not": "secret", @@ -67,9 +77,11 @@ def encrypt_and_decrypt_with_keyring( "the data you are handling": "is what you think it is", } - """ - 5. Create the KMS keyring - """ + # 4. Create a KMS keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( kms_key_id=kms_key_id, kms_client=kms_client @@ -79,37 +91,29 @@ def encrypt_and_decrypt_with_keyring( input=keyring_input ) - """ - 6. Encrypt the data for the encryptionContext - """ + # 5. Encrypt the data for the encryptionContext ciphertext, _ = client.encrypt( source=EXAMPLE_DATA, keyring=kms_keyring, encryption_context=encryption_context ) - """ - 7. Demonstrate that the ciphertext and plaintext are different. - """ - assert ciphertext != EXAMPLE_DATA, "Ciphertext and plaintext data are the same. Invalid encryption" - - """ - 8. Decrypt your encrypted data using the same keyring you used on encrypt. - You do not need to specify the encryption context on decrypt - because the header of the encrypted message includes the encryption context. - """ + # 6. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 7. Decrypt your encrypted data using the same keyring you used on encrypt. plaintext_bytes, dec_header = client.decrypt( source=ciphertext, keyring=kms_keyring ) - """ - 9. Demonstrate that the encryption context is correct in the decrypted message header - """ + # 8. Demonstrate that the encryption context is correct in the decrypted message header + # (This is an example for demonstration; you do not need to do this in your own code.) for k, v in encryption_context.items(): - assert v == dec_header.encryption_context[k], "Encryption context does not match expected values" + assert v == dec_header.encryption_context[k], \ + "Encryption context does not match expected values" - """ - 10. Demonstrate that the decrypted plaintext is identical to the original plaintext. - """ - assert plaintext_bytes == EXAMPLE_DATA \ No newline at end of file + # 9. Demonstrate that the decrypted plaintext is identical to the original plaintext. + assert plaintext_bytes == EXAMPLE_DATA diff --git a/examples/test/keyrings/test_i_aws_kms_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_keyring_example.py index ca1378db0..cd4b7086f 100644 --- a/examples/test/keyrings/test_i_aws_kms_keyring_example.py +++ b/examples/test/keyrings/test_i_aws_kms_keyring_example.py @@ -9,5 +9,7 @@ def test_encrypt_and_decrypt_with_keyring(): - key_arn = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" - encrypt_and_decrypt_with_keyring(key_arn) + """Test function for encryption and decryption using the AWS KMS Keyring example""" + + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + encrypt_and_decrypt_with_keyring(kms_key_id) diff --git a/examples/test/keyrings/test_i_hierarchical_keyring.py b/examples/test/keyrings/test_i_hierarchical_keyring.py index c4583534a..768cf994a 100644 --- a/examples/test/keyrings/test_i_hierarchical_keyring.py +++ b/examples/test/keyrings/test_i_hierarchical_keyring.py @@ -9,6 +9,7 @@ def test_encrypt_and_decrypt_with_keyring(): + """Test function for encryption and decryption using the AWS KMS Hierarchical Keyring example""" key_store_table_name = "KeyStoreDdbTable" - key_arn = "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" - encrypt_and_decrypt_with_keyring(key_store_table_name, key_store_table_name, key_arn) + kms_key_id = "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" + encrypt_and_decrypt_with_keyring(key_store_table_name, key_store_table_name, kms_key_id) From a7feaaf3fa238b3c34346724c448f628015ea3a4 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 19 Apr 2024 12:02:33 -0700 Subject: [PATCH 165/172] chore: verified flake8 and isort checks for aws_kms_keyring_example.py --- examples/src/keyrings/aws_kms_keyring_example.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/src/keyrings/aws_kms_keyring_example.py b/examples/src/keyrings/aws_kms_keyring_example.py index 416521d75..c3d76b6cb 100644 --- a/examples/src/keyrings/aws_kms_keyring_example.py +++ b/examples/src/keyrings/aws_kms_keyring_example.py @@ -10,25 +10,24 @@ 2. Encryption context is correct in the decrypted message header 3. Decrypted plaintext value matches EXAMPLE_DATA -AWS KMS keyrings can be used independently or in a multi-keyring with other keyrings +AWS KMS keyrings can be used independently or in a multi-keyring with other keyrings of the same or a different type. For more info on how to use KMS keyring, see https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html """ import sys -from typing import Dict -import boto3 +import boto3 from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsKeyringInput from aws_cryptographic_materialproviders.mpl.references import IKeyring +from typing import Dict import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy - # TODO-MPL: Remove this as part of removing PYTHONPATH hacks. MODULE_ROOT_DIR = '/'.join(__file__.split("/")[:-1]) @@ -50,7 +49,6 @@ def encrypt_and_decrypt_with_keyring( For more info on KMS Key identifiers, see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id """ - # 1. Instantiate the encryption SDK client. # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, # which enforces that this client only encrypts using committing algorithm suites and enforces From cad5a8872060a1522f5efcceeaa0798af47fded0 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 19 Apr 2024 12:11:45 -0700 Subject: [PATCH 166/172] chore: valid isort and flake8 for all files --- examples/test/keyrings/test_i_aws_kms_keyring_example.py | 3 +-- examples/test/keyrings/test_i_hierarchical_keyring.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/test/keyrings/test_i_aws_kms_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_keyring_example.py index cd4b7086f..83a784a9e 100644 --- a/examples/test/keyrings/test_i_aws_kms_keyring_example.py +++ b/examples/test/keyrings/test_i_aws_kms_keyring_example.py @@ -9,7 +9,6 @@ def test_encrypt_and_decrypt_with_keyring(): - """Test function for encryption and decryption using the AWS KMS Keyring example""" - + """Test function for encrypt and decrypt using the AWS KMS Keyring example.""" kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" encrypt_and_decrypt_with_keyring(kms_key_id) diff --git a/examples/test/keyrings/test_i_hierarchical_keyring.py b/examples/test/keyrings/test_i_hierarchical_keyring.py index 768cf994a..5a3adaa1c 100644 --- a/examples/test/keyrings/test_i_hierarchical_keyring.py +++ b/examples/test/keyrings/test_i_hierarchical_keyring.py @@ -9,7 +9,7 @@ def test_encrypt_and_decrypt_with_keyring(): - """Test function for encryption and decryption using the AWS KMS Hierarchical Keyring example""" + """Test function for encrypt and decrypt using the AWS KMS Hierarchical Keyring example.""" key_store_table_name = "KeyStoreDdbTable" kms_key_id = "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" encrypt_and_decrypt_with_keyring(key_store_table_name, key_store_table_name, kms_key_id) From 1c43dd9b83963b4a6287bf0586499f05662e23a9 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 19 Apr 2024 15:00:15 -0700 Subject: [PATCH 167/172] chore: minor comment fixes --- examples/src/keyrings/aws_kms_keyring_example.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/src/keyrings/aws_kms_keyring_example.py b/examples/src/keyrings/aws_kms_keyring_example.py index c3d76b6cb..dddfa5adc 100644 --- a/examples/src/keyrings/aws_kms_keyring_example.py +++ b/examples/src/keyrings/aws_kms_keyring_example.py @@ -5,15 +5,16 @@ The AWS KMS keyring uses symmetric encryption KMS keys to generate, encrypt and decrypt data keys. This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA -with an encryption context. This example also demonstrates some sanity checks for demonstration. +with an encryption context. This example also includes some sanity checks for demonstration: 1. Ciphertext and plaintext data are not the same 2. Encryption context is correct in the decrypted message header 3. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. AWS KMS keyrings can be used independently or in a multi-keyring with other keyrings of the same or a different type. -For more info on how to use KMS keyring, see +For more information on how to use KMS keyrings, see https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html """ import sys @@ -46,7 +47,7 @@ def encrypt_and_decrypt_with_keyring( decryption of your data keys. :type kms_key_id: string - For more info on KMS Key identifiers, see + For more information on KMS Key identifiers, see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id """ # 1. Instantiate the encryption SDK client. @@ -114,4 +115,5 @@ def encrypt_and_decrypt_with_keyring( "Encryption context does not match expected values" # 9. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) assert plaintext_bytes == EXAMPLE_DATA From ef141b9b8e207d600b182363e79b584493fd341d Mon Sep 17 00:00:00 2001 From: Ritvik Kapila <61410899+RitvikKapila@users.noreply.github.com> Date: Fri, 26 Apr 2024 15:25:03 -0700 Subject: [PATCH 168/172] Update test_streaming_client_stream_decryptor.py --- .../test_streaming_client_stream_decryptor.py | 138 +----------------- 1 file changed, 1 insertion(+), 137 deletions(-) diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index be9304006..aeb17ad65 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -365,143 +365,7 @@ def test_GIVEN_verification_key_AND_has_mpl_AND_has_MPLCMM_WHEN_read_header_THEN algorithm=self.mock_header.algorithm, encoded_point=mock_b64encoding() ) - @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") - @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") - @patch("aws_encryption_sdk.streaming_client.Verifier") - # Given: no MPL - @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") - def test_GIVEN_decrypt_config_has_ec_WHEN_read_header_THEN_calls_decrypt_materials_with_reproduced_ec( - self, - mock_verifier, - mock_decrypt_materials_request, - *_, - ): - - mock_verifier_instance = MagicMock() - mock_verifier.from_key_bytes.return_value = mock_verifier_instance - ct_stream = io.BytesIO(VALUES["data_128"]) - mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) - test_decryptor = StreamDecryptor( - materials_manager=self.mock_materials_manager, - source=ct_stream, - commitment_policy=mock_commitment_policy, - ) - test_decryptor.source_stream = ct_stream - test_decryptor._stream_length = len(VALUES["data_128"]) - # Given: self.config has "encryption_context" - any_reproduced_ec = {"some": "ec"} - test_decryptor.config.encryption_context = any_reproduced_ec - - # When: read header - test_decryptor._read_header() - - # Then: calls decrypt_materials with reproduced_encryption_context - mock_decrypt_materials_request.assert_called_once_with( - encrypted_data_keys=self.mock_header.encrypted_data_keys, - algorithm=self.mock_header.algorithm, - encryption_context=sentinel.encryption_context, - commitment_policy=mock_commitment_policy, - reproduced_encryption_context=any_reproduced_ec, - ) - - @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") - @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") - @patch("aws_encryption_sdk.streaming_client.Verifier") - # Given: no MPL - @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") - def test_GIVEN_verification_key_AND_no_mpl_WHEN_read_header_THEN_calls_from_key_bytes( - self, - mock_verifier, - *_, - ): - # Given: verification key - mock_verifier_instance = MagicMock() - mock_verifier.from_key_bytes.return_value = mock_verifier_instance - ct_stream = io.BytesIO(VALUES["data_128"]) - mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) - test_decryptor = StreamDecryptor( - materials_manager=self.mock_materials_manager, - source=ct_stream, - commitment_policy=mock_commitment_policy, - ) - test_decryptor.source_stream = ct_stream - test_decryptor._stream_length = len(VALUES["data_128"]) - - # When: read header - test_decryptor._read_header() - - # Then: calls from_key_bytes - mock_verifier.from_key_bytes.assert_called_once_with( - algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key - ) - - @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") - @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") - @patch("aws_encryption_sdk.streaming_client.Verifier") - # Given: has MPL - @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") - def test_GIVEN_verification_key_AND_has_mpl_AND_not_MPLCMM_WHEN_read_header_THEN_calls_from_key_bytes( - self, - mock_verifier, - *_, - ): - # Given: verification key - mock_verifier_instance = MagicMock() - mock_verifier.from_key_bytes.return_value = mock_verifier_instance - ct_stream = io.BytesIO(VALUES["data_128"]) - mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) - test_decryptor = StreamDecryptor( - # Given: native CMM - materials_manager=self.mock_materials_manager, - source=ct_stream, - commitment_policy=mock_commitment_policy, - ) - test_decryptor.source_stream = ct_stream - test_decryptor._stream_length = len(VALUES["data_128"]) - - # When: read_header - test_decryptor._read_header() - - # Then: calls from_key_bytess - mock_verifier.from_key_bytes.assert_called_once_with( - algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key - ) - - @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") - @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") - @patch("aws_encryption_sdk.streaming_client.Verifier") - @patch("base64.b64encode") - # Given: has MPL - @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") - def test_GIVEN_verification_key_AND_has_mpl_AND_has_MPLCMM_WHEN_read_header_THEN_calls_from_encoded_point( - self, - mock_b64encoding, - mock_verifier, - *_, - ): - # Given: Verification key - mock_verifier_instance = MagicMock() - mock_verifier.from_key_bytes.return_value = mock_verifier_instance - ct_stream = io.BytesIO(VALUES["data_128"]) - mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) - test_decryptor = StreamDecryptor( - # Given: MPL CMM - materials_manager=self.mock_mpl_materials_manager, - source=ct_stream, - commitment_policy=mock_commitment_policy, - ) - test_decryptor.source_stream = ct_stream - test_decryptor._stream_length = len(VALUES["data_128"]) - - # When: read header - test_decryptor._read_header() - - # Then: calls from_encoded_point - mock_verifier.from_encoded_point.assert_called_once_with( - algorithm=self.mock_header.algorithm, encoded_point=mock_b64encoding() - ) - - @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") def test_read_header_frame_too_large(self, mock_derive_datakey): self.mock_header.content_type = ContentType.FRAMED_DATA self.mock_header.frame_length = 1024 From 4d3d3b53b5f4e5d63fea3330a62f9f2e0622c0b8 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila <61410899+RitvikKapila@users.noreply.github.com> Date: Fri, 26 Apr 2024 15:25:29 -0700 Subject: [PATCH 169/172] Update test_streaming_client_stream_decryptor.py --- test/unit/test_streaming_client_stream_decryptor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index aeb17ad65..ce3d6ee3c 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -365,7 +365,7 @@ def test_GIVEN_verification_key_AND_has_mpl_AND_has_MPLCMM_WHEN_read_header_THEN algorithm=self.mock_header.algorithm, encoded_point=mock_b64encoding() ) - @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") def test_read_header_frame_too_large(self, mock_derive_datakey): self.mock_header.content_type = ContentType.FRAMED_DATA self.mock_header.frame_length = 1024 From 67afc5034ed392527d472868fc450438e23965a3 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Tue, 30 Apr 2024 15:45:05 -0700 Subject: [PATCH 170/172] fix --- examples/src/keyrings/aws_kms_keyring_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/keyrings/aws_kms_keyring_example.py b/examples/src/keyrings/aws_kms_keyring_example.py index dddfa5adc..46923478f 100644 --- a/examples/src/keyrings/aws_kms_keyring_example.py +++ b/examples/src/keyrings/aws_kms_keyring_example.py @@ -90,7 +90,7 @@ def encrypt_and_decrypt_with_keyring( input=keyring_input ) - # 5. Encrypt the data for the encryptionContext + # 5. Encrypt the data for the encryptionContext. ciphertext, _ = client.encrypt( source=EXAMPLE_DATA, keyring=kms_keyring, From 4258d59437329ba5218875940c84c51983d2eee6 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Tue, 30 Apr 2024 15:51:20 -0700 Subject: [PATCH 171/172] fix: ci, delete appveyor.yml, updated documentation --- .github/workflows/ci_test-vector-handler.yaml | 4 +- .github/workflows/ci_tests.yaml | 4 +- appveyor.yml | 113 ------------------ src/aws_encryption_sdk/__init__.py | 2 +- 4 files changed, 5 insertions(+), 118 deletions(-) delete mode 100644 appveyor.yml diff --git a/.github/workflows/ci_test-vector-handler.yaml b/.github/workflows/ci_test-vector-handler.yaml index 2fcd22fc9..8a142096d 100644 --- a/.github/workflows/ci_test-vector-handler.yaml +++ b/.github/workflows/ci_test-vector-handler.yaml @@ -19,7 +19,7 @@ jobs: os: - ubuntu-latest - windows-latest - - macos-latest + - macos-12 python: - 3.8 - 3.x @@ -34,7 +34,7 @@ jobs: # x86 builds are only meaningful for Windows - os: ubuntu-latest architecture: x86 - - os: macos-latest + - os: macos-12 architecture: x86 steps: - uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 3187b8db0..b03c47163 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -30,7 +30,7 @@ jobs: # (i.e. doo files, per-package module names) # Disable for now # - windows-latest - - macos-latest + - macos-12 python: - 3.7 - 3.8 @@ -59,7 +59,7 @@ jobs: # x86 builds are only meaningful for Windows - os: ubuntu-latest architecture: x86 - - os: macos-latest + - os: macos-12 architecture: x86 # MPL is not supported on <3.11 - python: 3.7 diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index cfb4bdcdb..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,113 +0,0 @@ -# https://packaging.python.org/guides/supporting-windows-using-appveyor/ - -environment: - - matrix: - # The only test we perform on Windows are our actual code tests. All linting, static - # analysis, etc are only run on Linux (via Travis CI). - - # Python 2.7 - - PYTHON: "C:\\Python27" - TOXENV: "py27-local" - - PYTHON: "C:\\Python27" - TOXENV: "py27-integ" - - PYTHON: "C:\\Python27" - TOXENV: "py27-accept" - - PYTHON: "C:\\Python27" - TOXENV: "py27-examples" - - PYTHON: "C:\\Python27-x64" - TOXENV: "py27-local" - - PYTHON: "C:\\Python27-x64" - TOXENV: "py27-integ" - - PYTHON: "C:\\Python27-x64" - TOXENV: "py27-accept" - - PYTHON: "C:\\Python27-x64" - TOXENV: "py27-examples" - - # Python 3.4 - - PYTHON: "C:\\Python34" - TOXENV: "py34-local" - - PYTHON: "C:\\Python34" - TOXENV: "py34-integ" - - PYTHON: "C:\\Python34" - TOXENV: "py34-accept" - - PYTHON: "C:\\Python34" - TOXENV: "py34-examples" - - PYTHON: "C:\\Python34-x64" - DISTUTILS_USE_SDK: "1" - TOXENV: "py34-local" - - PYTHON: "C:\\Python34-x64" - DISTUTILS_USE_SDK: "1" - TOXENV: "py34-integ" - - PYTHON: "C:\\Python34-x64" - DISTUTILS_USE_SDK: "1" - TOXENV: "py34-accept" - - PYTHON: "C:\\Python34-x64" - DISTUTILS_USE_SDK: "1" - TOXENV: "py34-examples" - - # Python 3.5 - - PYTHON: "C:\\Python35" - TOXENV: "py35-local" - - PYTHON: "C:\\Python35" - TOXENV: "py35-integ" - - PYTHON: "C:\\Python35" - TOXENV: "py35-accept" - - PYTHON: "C:\\Python35" - TOXENV: "py35-examples" - - PYTHON: "C:\\Python35-x64" - TOXENV: "py35-local" - - PYTHON: "C:\\Python35-x64" - TOXENV: "py35-integ" - - PYTHON: "C:\\Python35-x64" - TOXENV: "py35-accept" - - PYTHON: "C:\\Python35-x64" - TOXENV: "py35-examples" - - # Python 3.6 - - PYTHON: "C:\\Python36" - TOXENV: "py36-local" - - PYTHON: "C:\\Python36" - TOXENV: "py36-integ" - - PYTHON: "C:\\Python36" - TOXENV: "py36-accept" - - PYTHON: "C:\\Python36" - TOXENV: "py36-examples" - - PYTHON: "C:\\Python36-x64" - TOXENV: "py36-local" - - PYTHON: "C:\\Python36-x64" - TOXENV: "py36-integ" - - PYTHON: "C:\\Python36-x64" - TOXENV: "py36-accept" - - PYTHON: "C:\\Python36-x64" - TOXENV: "py36-examples" - - # Python 3.7 - - PYTHON: "C:\\Python37" - TOXENV: "py37-local" - - PYTHON: "C:\\Python37" - TOXENV: "py37-integ" - - PYTHON: "C:\\Python37" - TOXENV: "py37-accept" - - PYTHON: "C:\\Python37" - TOXENV: "py37-examples" - - PYTHON: "C:\\Python37-x64" - TOXENV: "py37-local" - - PYTHON: "C:\\Python37-x64" - TOXENV: "py37-integ" - - PYTHON: "C:\\Python37-x64" - TOXENV: "py37-accept" - - PYTHON: "C:\\Python37-x64" - TOXENV: "py37-examples" - -install: - # Prepend newly installed Python to the PATH of this build - - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - # Check the Python version to verify the correct version was installed - - "python --version" - - "python -m pip install wheel tox" - -build: off - -test_script: - - "tox" diff --git a/src/aws_encryption_sdk/__init__.py b/src/aws_encryption_sdk/__init__.py index 74732bfc5..66b779fa5 100644 --- a/src/aws_encryption_sdk/__init__.py +++ b/src/aws_encryption_sdk/__init__.py @@ -164,7 +164,7 @@ def decrypt(self, **kwargs): ... 'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', ... 'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333' ... ]) - >>> my_ciphertext, encryptor_header = client.decrypt( + >>> my_plaintext, decryptor_header = client.decrypt( ... source=my_ciphertext, ... key_provider=kms_key_provider ... ) From 84f09d72e4c9a7ea0a83b16d18be304a7bea3988 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila <61410899+RitvikKapila@users.noreply.github.com> Date: Thu, 2 May 2024 10:30:15 -0700 Subject: [PATCH 172/172] Update examples/src/keyrings/aws_kms_keyring_example.py Co-authored-by: Lucas McDonald --- examples/src/keyrings/aws_kms_keyring_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/keyrings/aws_kms_keyring_example.py b/examples/src/keyrings/aws_kms_keyring_example.py index 46923478f..fa7ffd12f 100644 --- a/examples/src/keyrings/aws_kms_keyring_example.py +++ b/examples/src/keyrings/aws_kms_keyring_example.py @@ -90,7 +90,7 @@ def encrypt_and_decrypt_with_keyring( input=keyring_input ) - # 5. Encrypt the data for the encryptionContext. + # 5. Encrypt the data with the encryptionContext. ciphertext, _ = client.encrypt( source=EXAMPLE_DATA, keyring=kms_keyring,