From 7360edd46964ae0a0d7127a1cf51bd180973af33 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 31 Jan 2024 15:18:21 -0800 Subject: [PATCH 001/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] . --- 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] . --- 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/180] . --- 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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/180] 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 437b81d7aef17f77e86cb1d38cd72614e32ed72f Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 26 Apr 2024 14:56:35 -0700 Subject: [PATCH 163/180] chore: kms rsa keyring example --- .../keyrings/aws_kms_rsa_keyring_example.py | 120 ++++++++++++++++++ .../test_i_aws_kms_rsa_keyring_example.py | 14 ++ 2 files changed, 134 insertions(+) create mode 100644 examples/src/keyrings/aws_kms_rsa_keyring_example.py create mode 100644 examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py new file mode 100644 index 000000000..eec081014 --- /dev/null +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -0,0 +1,120 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS RSA Keyring + +The AWS KMS RSA keyring uses asymmetric encryption KMS RSA keys to generate, encrypt and +decrypt data keys. This example creates a KMS RSA Keyring and then encrypts a custom input +EXAMPLE_DATA 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 RSA keyrings can be used independently or in a multi-keyring with other keyrings +of the same or a different type. + +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 + +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_rsa_key_id: str +): + """Demonstrate an encrypt/decrypt cycle using an AWS KMS keyring. + + Usage: encrypt_and_decrypt_with_keyring(kms_rsa_key_id) + :param kms_rsa_key_id: KMS RSA Key identifier for the KMS key you want to use for encryption and + decryption of your data keys. + :type kms_rsa_key_id: string + + 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. + # 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 a boto3 client for KMS. + kms_client = boto3.client('kms', region_name="us-west-2") + + # 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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Create a KMS keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_rsa_key_id=kms_rsa_key_id, + kms_client=kms_client + ) + + kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=keyring_input + ) + + # 5. Encrypt the data for the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=kms_keyring, + encryption_context=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 + ) + + # 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" + + # 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 \ No newline at end of file diff --git a/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py new file mode 100644 index 000000000..86d3bf6b1 --- /dev/null +++ b/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS RSA keyring example.""" +import pytest + +from ...src.keyrings.aws_kms_rsa_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS RSA Keyring example.""" + kms_rsa_key_id = "arn:aws:kms:us-west-2:370957321024:key/mrk-63d386cb70614ea59b32ad65c9315297" + encrypt_and_decrypt_with_keyring(kms_rsa_key_id) From 5bf8d2b0438dbd07d9feb1a5504f251ace93924f Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 29 Apr 2024 10:10:01 -0700 Subject: [PATCH 164/180] adding more keyrings --- .../aws_kms_discovery_keyring_example.py | 196 +++++++++++++++++ .../keyrings/aws_kms_rsa_keyring_example.py | 47 ++-- .../src/keyrings/multi_keyring_example.py | 207 ++++++++++++++++++ ...est_i_aws_kms_discovery_keyring_example.py | 14 ++ .../keyrings/test_i_multi_keyring_example.py | 14 ++ 5 files changed, 461 insertions(+), 17 deletions(-) create mode 100644 examples/src/keyrings/aws_kms_discovery_keyring_example.py create mode 100644 examples/src/keyrings/multi_keyring_example.py create mode 100644 examples/test/keyrings/test_i_aws_kms_discovery_keyring_example.py create mode 100644 examples/test/keyrings/test_i_multi_keyring_example.py diff --git a/examples/src/keyrings/aws_kms_discovery_keyring_example.py b/examples/src/keyrings/aws_kms_discovery_keyring_example.py new file mode 100644 index 000000000..578935647 --- /dev/null +++ b/examples/src/keyrings/aws_kms_discovery_keyring_example.py @@ -0,0 +1,196 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS Discovery Keyring + +AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. + +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. For information about using multi-Region keys with the +AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks + +Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. +If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt +operation fails. The exception is the AWS Encryption SDK for C, where the encrypt operation +ignores a standard discovery keyring, but fails if you specify a multi-Region discovery +keyring, alone or in a multi-keyring. + +When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS key. + +This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This encrypted ciphertext is then decrypted using the Discovery keyring. +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 +4. Decryption is only possible if the Discovery Keyring contains the correct AWS Account ID's to +which the KMS key used for encryption belongs +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +""" +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, CreateAwsKmsDiscoveryKeyringInput, DiscoveryFilter +from aws_cryptographic_materialproviders.mpl.references import IKeyring +from typing import Dict + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError + +# 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 Discovery 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 creating + the kms_keyring used for encryption + :type kms_key_id: string + + 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. + # 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 a boto3 client for KMS. + kms_client = boto3.client('kms', region_name="us-west-2") + + # 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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Create the keyring that determines how your data keys are protected. + # Although this example highlights Discovery keyrings, Discovery keyrings cannot + # be used to encrypt, so for encryption we create a KMS keyring without discovery mode. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + encrypt_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=kms_keyring_input + ) + + # 5. Encrypt the data for the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=encrypt_kms_keyring, + encryption_context=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. Now create a Discovery keyring to use for decryption. We'll add a discovery filter + # so that we limit the set of ciphertexts we are willing to decrypt to only ones + # created by KMS keys in our account and partition. + + discovery_keyring_input: CreateAwsKmsDiscoveryKeyringInput = CreateAwsKmsDiscoveryKeyringInput( + kms_client=kms_client, + discovery_filter=DiscoveryFilter( + account_ids=["658956600833"], + partition="aws" + ) + ) + + discovery_keyring: IKeyring = mat_prov.create_aws_kms_discovery_keyring( + input=discovery_keyring_input + ) + + # 8. Decrypt your encrypted data using the discovery keyring. + # On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + # The header contains the Encrypted Data Keys (EDKs), which, if the EDK + # was encrypted by a KMS Keyring, includes the KMS Key ARN. + # The Discovery Keyring filters these EDKs for + # EDKs encrypted by Single Region OR Multi Region KMS Keys. + # If a Discovery Filter is present, these KMS Keys must belong + # to an AWS Account ID in the discovery filter's AccountIds and + # must be from the discovery filter's partition. + # Finally, KMS is called to decrypt each filtered EDK until an EDK is + # successfully decrypted. The resulting data key is used to decrypt the + # ciphertext's message. + # If all calls to KMS fail, the decryption fails. + + plaintext_bytes, dec_header = client.decrypt( + source=ciphertext, + keyring=discovery_keyring + ) + + # 9. 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" + + # 10. 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 + + # 11. Demonstrate that if the Discovery keyring (Bob's) doesn't have the correct account id's, + # the decrypt will fail with an error message + discovery_keyring_input_bob: CreateAwsKmsDiscoveryKeyringInput = \ + CreateAwsKmsDiscoveryKeyringInput( + kms_client=kms_client, + discovery_filter=DiscoveryFilter( + account_ids=["658956600834"], + partition="aws" + ) + ) + + discovery_keyring_bob: IKeyring = mat_prov.create_aws_kms_discovery_keyring( + input=discovery_keyring_input_bob + ) + + # Decrypt the ciphertext using Bob's discovery keyring which doesn't contain the required + # Account ID's for the KMS keyring used for encryption + try: + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=discovery_keyring_bob + ) + + raise AssertionError("Decrypt using discovery keyring with wrong AWS Account ID should" + + "raise AWSEncryptionSDKClientError") + except AWSEncryptionSDKClientError: + pass diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py index eec081014..1860d1c46 100644 --- a/examples/src/keyrings/aws_kms_rsa_keyring_example.py +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -11,19 +11,19 @@ 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 RSA keyrings can be used independently or in a multi-keyring with other keyrings -of the same or a different type. - -For more information on how to use KMS keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html """ +# +# AWS KMS RSA keyrings can be used independently or in a multi-keyring with other keyrings +# of the same or a different type. + +# 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 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.models import CreateAwsKmsRsaKeyringInput from aws_cryptographic_materialproviders.mpl.references import IKeyring from typing import Dict @@ -41,11 +41,11 @@ def encrypt_and_decrypt_with_keyring( kms_rsa_key_id: str ): - """Demonstrate an encrypt/decrypt cycle using an AWS KMS keyring. + """Demonstrate an encrypt/decrypt cycle using an AWS KMS RSA keyring. Usage: encrypt_and_decrypt_with_keyring(kms_rsa_key_id) - :param kms_rsa_key_id: KMS RSA Key identifier for the KMS key you want to use for encryption and - decryption of your data keys. + :param kms_rsa_key_id: KMS RSA Key identifier for the KMS RSA key you want to use for + encryption and decryption of your data keys. :type kms_rsa_key_id: string For more information on KMS Key identifiers, see @@ -77,24 +77,37 @@ def encrypt_and_decrypt_with_keyring( "the data you are handling": "is what you think it is", } - # 4. Create a KMS keyring + # 4. Create a KMS RSA keyring + kms_rsa_public_key = '''-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ +oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN +/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK +fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2 +r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9 +yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou +twIDAQAB +-----END PUBLIC KEY----- +''' + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( config=MaterialProvidersConfig() ) - keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( - kms_rsa_key_id=kms_rsa_key_id, + keyring_input: CreateAwsKmsRsaKeyringInput = CreateAwsKmsRsaKeyringInput( + public_key=kms_rsa_public_key, + kms_key_id=kms_rsa_key_id, + encryption_algorithm="RSAES_OAEP_SHA_256", kms_client=kms_client ) - kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + kms_rsa_keyring: IKeyring = mat_prov.create_aws_kms_rsa_keyring( input=keyring_input ) # 5. Encrypt the data for the encryptionContext ciphertext, _ = client.encrypt( source=EXAMPLE_DATA, - keyring=kms_keyring, + keyring=kms_rsa_keyring, encryption_context=encryption_context ) @@ -106,7 +119,7 @@ def encrypt_and_decrypt_with_keyring( # 7. Decrypt your encrypted data using the same keyring you used on encrypt. plaintext_bytes, dec_header = client.decrypt( source=ciphertext, - keyring=kms_keyring + keyring=kms_rsa_keyring ) # 8. Demonstrate that the encryption context is correct in the decrypted message header @@ -117,4 +130,4 @@ def encrypt_and_decrypt_with_keyring( # 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 \ No newline at end of file + assert plaintext_bytes == EXAMPLE_DATA diff --git a/examples/src/keyrings/multi_keyring_example.py b/examples/src/keyrings/multi_keyring_example.py new file mode 100644 index 000000000..91a08592a --- /dev/null +++ b/examples/src/keyrings/multi_keyring_example.py @@ -0,0 +1,207 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the Multi Keyring + +A multi-keyring is a keyring that consists of one or more individual keyrings of the +same or a different type. The effect is like using several keyrings in a series. +When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its +keyrings can decrypt that data. + +When you create a multi-keyring to encrypt data, you designate one of the keyrings as +the generator keyring. All other keyrings are known as child keyrings. The generator keyring +generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the +child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext +key and one encrypted data key for each wrapping key in the multi-keyring. If you create a +multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. +If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates +and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, +and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. + +When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted +data keys. The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. + +This example creates a Multi Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decryption of ciphertext is possible using the multi_keyring, +and every one of the keyrings from the multi_keyring separately +3. All decrypted plaintext value match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +This example creates a multi_keyring using a KMS keyring as generator keyring and a raw AES keyring +as a child keyring. You can use different combinations of keyrings in the multi_keyring. + +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html +""" +import secrets +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 ( + AesWrappingAlg, + CreateAwsKmsKeyringInput, + CreateMultiKeyringInput, + CreateRawAesKeyringInput, +) +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 a Multi keyring. + The multi_keyring is created using a KMS keyring as generator keyring and a raw AES keyring + as a child keyring. Therefore we take a kms_key_id as input + + 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 in the kms_keyring that is in-turn used in the multi_keyring + :type kms_key_id: string + + 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. + # 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 a boto3 client for KMS. + kms_client = boto3.client('kms', region_name="us-west-2") + + # 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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Initialize the material providers library + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # 5. Create a KMS keyring + kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=kms_keyring_input + ) + + # 6. Create a raw AES keyring to additionally encrypt under as child_keyring + + # The key namespace and key name are defined by you. + # and are used by the Raw AES keyring to determine + # whether it should attempt to decrypt an encrypted data key. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html + key_name_space = "Some managed raw keys" + key_name = "My 256-bit AES wrapping key" + + # Generate a 256-bit AES key to use with your raw AES keyring. + # Here, the input to secrets.token_bytes() = 32 bytes = 256 bits + static_key = secrets.token_bytes(32) + + raw_aes_keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + wrapping_key=static_key, + wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16 + ) + + raw_aes_keyring: IKeyring = mat_prov.create_raw_aes_keyring( + input=raw_aes_keyring_input + ) + + # 7. Create a multi_keyring that consists of the previously created keyrings. + # When using this multi_keyring to encrypt data, either `kms_keyring` or + # `raw_aes_keyring` (or a multi_keyring containing either) may be used to decrypt the data. + multi_keyring_input: CreateMultiKeyringInput = CreateMultiKeyringInput( + generator=kms_keyring, + child_keyrings=[raw_aes_keyring] + ) + + multi_keyring: IKeyring = mat_prov.create_multi_keyring( + input=multi_keyring_input + ) + + # 8. Encrypt the data for the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=multi_keyring, + encryption_context=encryption_context + ) + + # 9. 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" + + # 10a. Decrypt your encrypted data using the same multi_keyring you used on encrypt. + plaintext_bytes_multi_keyring, _ = client.decrypt( + source=ciphertext, + keyring=multi_keyring + ) + + # 10b. 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_multi_keyring == EXAMPLE_DATA + + # Because you used a multi_keyring on Encrypt, you can use either the + # `kms_keyring` or `raw_aes_keyring` individually to decrypt the data. + + # 11. Demonstrate that you can successfully decrypt data using just the `kms_keyring` + # directly. + + # 11a. Decrypt your encrypted data using the kms_keyring. + plaintext_bytes_kms_keyring, _ = client.decrypt( + source=ciphertext, + keyring=kms_keyring + ) + + # 11b. 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_kms_keyring == EXAMPLE_DATA + + # 12. Demonstrate that you can also successfully decrypt data using the `raw_aes_keyring` + # directly. + + # 12a. Decrypt your encrypted data using the raw_aes_keyring. + plaintext_bytes_raw_aes_keyring, _ = client.decrypt( + source=ciphertext, + keyring=raw_aes_keyring + ) + + # 12b. 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_raw_aes_keyring == EXAMPLE_DATA diff --git a/examples/test/keyrings/test_i_aws_kms_discovery_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_discovery_keyring_example.py new file mode 100644 index 000000000..b2455858e --- /dev/null +++ b/examples/test/keyrings/test_i_aws_kms_discovery_keyring_example.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS Discovery keyring example.""" +import pytest + +from ...src.keyrings.aws_kms_discovery_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS Discovery 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_multi_keyring_example.py b/examples/test/keyrings/test_i_multi_keyring_example.py new file mode 100644 index 000000000..9948c2b67 --- /dev/null +++ b/examples/test/keyrings/test_i_multi_keyring_example.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the multi keyring example.""" +import pytest + +from ...src.keyrings.multi_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the Multi Keyring example.""" + kms_rsa_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + encrypt_and_decrypt_with_keyring(kms_rsa_key_id) From 41f901b45bc8c67d3f6c37539c878dd8a736c883 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 29 Apr 2024 15:32:17 -0700 Subject: [PATCH 165/180] chore: updated multi, discovery and discovery multi keyrings --- .../aws_kms_discovery_keyring_example.py | 19 +- ...aws_kms_discovery_multi_keyring_example.py | 169 ++++++++++++++++++ .../src/keyrings/multi_keyring_example.py | 6 +- ...aws_kms_discovery_multi_keyring_example.py | 14 ++ 4 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py create mode 100644 examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py diff --git a/examples/src/keyrings/aws_kms_discovery_keyring_example.py b/examples/src/keyrings/aws_kms_discovery_keyring_example.py index 578935647..7afd1d627 100644 --- a/examples/src/keyrings/aws_kms_discovery_keyring_example.py +++ b/examples/src/keyrings/aws_kms_discovery_keyring_example.py @@ -31,7 +31,7 @@ which the KMS key used for encryption belongs These sanity checks are for demonstration in the example only. You do not need these in your code. -For more information on how to use KMS keyrings, see +For more information on how to use KMS Discovery keyrings, see https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery """ import sys @@ -39,7 +39,11 @@ 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, CreateAwsKmsDiscoveryKeyringInput, DiscoveryFilter +from aws_cryptographic_materialproviders.mpl.models import ( + CreateAwsKmsDiscoveryKeyringInput, + CreateAwsKmsKeyringInput, + DiscoveryFilter, +) from aws_cryptographic_materialproviders.mpl.references import IKeyring from typing import Dict @@ -125,7 +129,6 @@ def encrypt_and_decrypt_with_keyring( # 7. Now create a Discovery keyring to use for decryption. We'll add a discovery filter # so that we limit the set of ciphertexts we are willing to decrypt to only ones # created by KMS keys in our account and partition. - discovery_keyring_input: CreateAwsKmsDiscoveryKeyringInput = CreateAwsKmsDiscoveryKeyringInput( kms_client=kms_client, discovery_filter=DiscoveryFilter( @@ -151,7 +154,6 @@ def encrypt_and_decrypt_with_keyring( # successfully decrypted. The resulting data key is used to decrypt the # ciphertext's message. # If all calls to KMS fail, the decryption fails. - plaintext_bytes, dec_header = client.decrypt( source=ciphertext, keyring=discovery_keyring @@ -167,13 +169,15 @@ def encrypt_and_decrypt_with_keyring( # (This is an example for demonstration; you do not need to do this in your own code.) assert plaintext_bytes == EXAMPLE_DATA - # 11. Demonstrate that if the Discovery keyring (Bob's) doesn't have the correct account id's, + # 11. Demonstrate that if a discovery keyring (Bob's) doesn't have the correct AWS Account ID's, # the decrypt will fail with an error message + # Note that the Account ID '888888888888' used here is different than the one used + # during encryption '658956600833' discovery_keyring_input_bob: CreateAwsKmsDiscoveryKeyringInput = \ CreateAwsKmsDiscoveryKeyringInput( kms_client=kms_client, discovery_filter=DiscoveryFilter( - account_ids=["658956600834"], + account_ids=["888888888888"], partition="aws" ) ) @@ -183,7 +187,8 @@ def encrypt_and_decrypt_with_keyring( ) # Decrypt the ciphertext using Bob's discovery keyring which doesn't contain the required - # Account ID's for the KMS keyring used for encryption + # Account ID's for the KMS keyring used for encryption. + # This should throw an AWSEncryptionSDKClientError exception try: plaintext_bytes, _ = client.decrypt( source=ciphertext, diff --git a/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py b/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py new file mode 100644 index 000000000..7a1cc5be1 --- /dev/null +++ b/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py @@ -0,0 +1,169 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS Discovery Multi Keyring and demonstrates decryption +using a Multi-Keyring containing multiple AWS KMS Discovery Keyrings. + +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. For information about using multi-Region keys with the +AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks + +Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. +If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt +operation fails. The exception is the AWS Encryption SDK for C, where the encrypt operation +ignores a standard discovery keyring, but fails if you specify a multi-Region discovery +keyring, alone or in a multi-keyring. + +When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS key. + +This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This encrypted ciphertext is then decrypted using the Discovery Multi +keyring. 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. + +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +""" +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 ( + CreateAwsKmsDiscoveryMultiKeyringInput, + CreateAwsKmsKeyringInput, + DiscoveryFilter, +) +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 Discovery Multi 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 creating + the kms_keyring used for encryption + :type kms_key_id: string + + 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. + # 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 a boto3 client for KMS. + kms_client = boto3.client('kms', region_name="us-west-2") + + # 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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Create the keyring that determines how your data keys are protected. + # Although this example highlights Discovery keyrings, Discovery keyrings cannot + # be used to encrypt, so for encryption we create a KMS keyring without discovery mode. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + encrypt_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=kms_keyring_input + ) + + # 5. Encrypt the data for the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=encrypt_kms_keyring, + encryption_context=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. Now create a Discovery Multi keyring to use for decryption. We'll add a discovery filter + # so that we limit the set of ciphertexts we are willing to decrypt to only ones + # created by KMS keys in our account and partition. + discovery_multi_keyring_input: CreateAwsKmsDiscoveryMultiKeyringInput = \ + CreateAwsKmsDiscoveryMultiKeyringInput( + regions=["us-west-2", "us-east-1"], + discovery_filter=DiscoveryFilter( + account_ids=["658956600833"], + partition="aws" + ) + ) + + # This is a Multi Keyring composed of Discovery Keyrings. + # There is a keyring for every region in `regions`. + # All the keyrings have the same Discovery Filter. + # Each keyring has its own KMS Client, which is created for the keyring's region. + discovery_multi_keyring: IKeyring = mat_prov.create_aws_kms_discovery_multi_keyring( + input=discovery_multi_keyring_input + ) + + # 8. On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + # The header contains the Encrypted Data Keys (EDKs), which, if the EDK + # was encrypted by a KMS Keyring, includes the KMS Key ARN. + # For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption + # is successful. + # Since every member of the Multi Keyring is a Discovery Keyring: + # Each Keyring will filter the EDKs by the Discovery Filter + # For the filtered EDKs, the keyring will try to decrypt it with the keyring's client. + # All of this is done serially, until a success occurs or all keyrings have + # failed all (filtered) EDKs. + # KMS Discovery Keyrings will attempt to decrypt Multi Region Keys (MRKs) and regular KMS Keys. + plaintext_bytes, dec_header = client.decrypt( + source=ciphertext, + keyring=discovery_multi_keyring + ) + + # 9. 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" + + # 10. 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 diff --git a/examples/src/keyrings/multi_keyring_example.py b/examples/src/keyrings/multi_keyring_example.py index 91a08592a..0a6588a8e 100644 --- a/examples/src/keyrings/multi_keyring_example.py +++ b/examples/src/keyrings/multi_keyring_example.py @@ -33,7 +33,7 @@ This example creates a multi_keyring using a KMS keyring as generator keyring and a raw AES keyring as a child keyring. You can use different combinations of keyrings in the multi_keyring. -For more information on how to use KMS keyrings, see +For more information on how to use Multi keyrings, see https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html """ import secrets @@ -67,11 +67,11 @@ def encrypt_and_decrypt_with_keyring( ): """Demonstrate an encrypt/decrypt cycle using a Multi keyring. The multi_keyring is created using a KMS keyring as generator keyring and a raw AES keyring - as a child keyring. Therefore we take a kms_key_id as input + as a child keyring. Therefore, we take a kms_key_id as input 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 in the kms_keyring that is in-turn used in the multi_keyring + decryption of your data keys in the kms_keyring, that is in-turn used in the multi_keyring :type kms_key_id: string For more information on KMS Key identifiers, see diff --git a/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py new file mode 100644 index 000000000..833698f6b --- /dev/null +++ b/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS Discovery Multi keyring example.""" +import pytest + +from ...src.keyrings.aws_kms_discovery_multi_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS Discovery Multi 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) From 731c69861f4354260fed54b8555d26f74d7dc8f7 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 29 Apr 2024 16:29:21 -0700 Subject: [PATCH 166/180] chore: fixed naming in test_i_multi_keyring_example.py, added aws_kms_multi_keyring_example.py --- .../keyrings/aws_kms_multi_keyring_example.py | 197 ++++++++++++++++++ .../test_i_aws_kms_multi_keyring_example.py | 17 ++ .../keyrings/test_i_multi_keyring_example.py | 4 +- 3 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 examples/src/keyrings/aws_kms_multi_keyring_example.py create mode 100644 examples/test/keyrings/test_i_aws_kms_multi_keyring_example.py diff --git a/examples/src/keyrings/aws_kms_multi_keyring_example.py b/examples/src/keyrings/aws_kms_multi_keyring_example.py new file mode 100644 index 000000000..46fd0365c --- /dev/null +++ b/examples/src/keyrings/aws_kms_multi_keyring_example.py @@ -0,0 +1,197 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS Multi Keyring made up of multiple AWS KMS Keyrings. + +A multi-keyring is a keyring that consists of one or more individual keyrings of the +same or a different type. The effect is like using several keyrings in a series. +When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its +keyrings can decrypt that data. + +When you create a multi-keyring to encrypt data, you designate one of the keyrings as +the generator keyring. All other keyrings are known as child keyrings. The generator keyring +generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the +child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext +key and one encrypted data key for each wrapping key in the multi-keyring. If you create a +multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. +If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates +and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, +and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. + +When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted +data keys. The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. + +This example creates a Multi Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decryption of ciphertext is possible using the multi_keyring, +and every one of the keyrings from the multi_keyring separately +3. All decrypted plaintext value match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +This example creates a multi_keyring using a KMS keyring as generator keyring and +another KMS keyring as a child keyring. + +For more information on how to use Multi keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html +""" +import secrets +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, + CreateAwsKmsMultiKeyringInput +) +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( + default_region_kms_key_id: str, + second_region_kms_key_id: str +): + """Demonstrate an encrypt/decrypt cycle using an AWS KMS Multi keyring. + The multi_keyring is created using a KMS keyring as generator keyring and another KMS keyring + as a child keyring. For this example, `default_region_kms_key_id` is the generator key id + for a KMS key located in your default region, and `second_region_kms_key_id` is the KMS key id + for a KMS Key located in some second Region. + + Usage: encrypt_and_decrypt_with_keyring(default_region_kms_key_id, second_region_kms_key_id) + :param default_region_kms_key_id: KMS Key identifier for the default region KMS key you want to + use as a generator keyring + :type default_region_kms_key_id: string + :param second_region_kms_key_id: KMS Key identifier for the second region KMS key you want to + use as a child keyring + :type second_region_kms_key_id: string + + 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. + # 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 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", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 3. Create an AwsKmsMultiKeyring that protects your data under two different KMS Keys. + # Either KMS Key individually is capable of decrypting data encrypted under this Multi Keyring. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + kms_multi_keyring_input: CreateAwsKmsMultiKeyringInput = CreateAwsKmsMultiKeyringInput( + generator=default_region_kms_key_id, + kms_key_ids=[second_region_kms_key_id] + ) + + kms_multi_keyring: IKeyring = mat_prov.create_aws_kms_multi_keyring( + input=kms_multi_keyring_input + ) + + # 4. Encrypt the data for the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=kms_multi_keyring, + encryption_context=encryption_context + ) + + # 5. 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" + + # 6a. Decrypt your encrypted data using the same multi_keyring you used on encrypt. + plaintext_bytes_multi_keyring, _ = client.decrypt( + source=ciphertext, + keyring=kms_multi_keyring + ) + + # 6b. 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_multi_keyring == EXAMPLE_DATA + + # Because you used a multi_keyring on Encrypt, you can use either of the two + # kms keyrings individually to decrypt the data. + + # 7. Demonstrate that you can successfully decrypt data using a KMS keyring with just the + # `default_region_kms_key_id` directly. + + # 7a. Create a boto3 client for KMS for the default region. + default_region_kms_client = boto3.client('kms', region_name="us-west-2") + + # 7b. Create KMS keyring + default_region_kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=default_region_kms_key_id, + kms_client=default_region_kms_client + ) + + default_region_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=default_region_kms_keyring_input + ) + + # 7c. Decrypt your encrypted data using the default_region_kms_keyring. + plaintext_bytes_default_region_kms_keyring, _ = client.decrypt( + source=ciphertext, + keyring=default_region_kms_keyring + ) + + # 7d. 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_default_region_kms_keyring == EXAMPLE_DATA + + # 8. Demonstrate that you can also successfully decrypt data using a KMS keyring with just the + # `second_region_kms_key_id` directly. + + # 8a. Create a boto3 client for KMS for the second region. + second_region_kms_client = boto3.client('kms', region_name="eu-central-1") + + # 8b. Create KMS keyring + second_region_kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=second_region_kms_key_id, + kms_client=second_region_kms_client + ) + + second_region_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=second_region_kms_keyring_input + ) + + # 8c. Decrypt your encrypted data using the second_region_kms_keyring. + plaintext_bytes_second_region_kms_keyring, _ = client.decrypt( + source=ciphertext, + keyring=second_region_kms_keyring + ) + + # 8d. 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_second_region_kms_keyring == EXAMPLE_DATA diff --git a/examples/test/keyrings/test_i_aws_kms_multi_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_multi_keyring_example.py new file mode 100644 index 000000000..e5bc90c30 --- /dev/null +++ b/examples/test/keyrings/test_i_aws_kms_multi_keyring_example.py @@ -0,0 +1,17 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS multi keyring example.""" +import pytest + +from ...src.keyrings.aws_kms_multi_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS Multi Keyring example.""" + default_region_kms_key_id = \ + "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + second_region_kms_key_id = \ + "arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2" + encrypt_and_decrypt_with_keyring(default_region_kms_key_id, second_region_kms_key_id) diff --git a/examples/test/keyrings/test_i_multi_keyring_example.py b/examples/test/keyrings/test_i_multi_keyring_example.py index 9948c2b67..ad12c0c9c 100644 --- a/examples/test/keyrings/test_i_multi_keyring_example.py +++ b/examples/test/keyrings/test_i_multi_keyring_example.py @@ -10,5 +10,5 @@ def test_encrypt_and_decrypt_with_keyring(): """Test function for encrypt and decrypt using the Multi Keyring example.""" - kms_rsa_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" - encrypt_and_decrypt_with_keyring(kms_rsa_key_id) + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + encrypt_and_decrypt_with_keyring(kms_key_id) From 8bd9383567cb612ada9d440665ac2f5bb0c7a167 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Tue, 30 Apr 2024 12:44:46 -0700 Subject: [PATCH 167/180] fix: updated aws_kms_rsa_keyring --- .../keyrings/aws_kms_multi_keyring_example.py | 6 +--- .../keyrings/aws_kms_rsa_keyring_example.py | 33 +++++++------------ .../test_i_aws_kms_rsa_keyring_example.py | 13 +++++++- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/examples/src/keyrings/aws_kms_multi_keyring_example.py b/examples/src/keyrings/aws_kms_multi_keyring_example.py index 46fd0365c..f0f6a7bf6 100644 --- a/examples/src/keyrings/aws_kms_multi_keyring_example.py +++ b/examples/src/keyrings/aws_kms_multi_keyring_example.py @@ -36,16 +36,12 @@ For more information on how to use Multi keyrings, see https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html """ -import secrets 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, - CreateAwsKmsMultiKeyringInput -) +from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsKeyringInput, CreateAwsKmsMultiKeyringInput from aws_cryptographic_materialproviders.mpl.references import IKeyring from typing import Dict diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py index 1860d1c46..a3c65a4b0 100644 --- a/examples/src/keyrings/aws_kms_rsa_keyring_example.py +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -3,21 +3,17 @@ """ This example sets up the AWS KMS RSA Keyring -The AWS KMS RSA keyring uses asymmetric encryption KMS RSA keys to generate, encrypt and -decrypt data keys. This example creates a KMS RSA Keyring and then encrypts a custom input +This example creates a KMS RSA Keyring and then encrypts a custom input EXAMPLE_DATA 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 RSA keyrings can be used independently or in a multi-keyring with other keyrings -# of the same or a different type. # 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 import boto3 @@ -29,6 +25,7 @@ import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.identifiers import AlgorithmSuite # TODO-MPL: Remove this as part of removing PYTHONPATH hacks. MODULE_ROOT_DIR = '/'.join(__file__.split("/")[:-1]) @@ -39,14 +36,16 @@ def encrypt_and_decrypt_with_keyring( - kms_rsa_key_id: str + kms_rsa_key_id: str, + kms_rsa_public_key: str ): """Demonstrate an encrypt/decrypt cycle using an AWS KMS RSA keyring. - Usage: encrypt_and_decrypt_with_keyring(kms_rsa_key_id) - :param kms_rsa_key_id: KMS RSA Key identifier for the KMS RSA key you want to use for - encryption and decryption of your data keys. + Usage: encrypt_and_decrypt_with_keyring(kms_rsa_key_id, kms_rsa_public_key) + :param kms_rsa_key_id: KMS RSA Key identifier for the KMS RSA key you want to use :type kms_rsa_key_id: string + :param kms_rsa_public_key: KMS RSA public key you want to use + :type kms_rsa_public_key: string For more information on KMS Key identifiers, see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id @@ -78,17 +77,6 @@ def encrypt_and_decrypt_with_keyring( } # 4. Create a KMS RSA keyring - kms_rsa_public_key = '''-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ -oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN -/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK -fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2 -r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9 -yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou -twIDAQAB ------END PUBLIC KEY----- -''' - mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( config=MaterialProvidersConfig() ) @@ -108,7 +96,8 @@ def encrypt_and_decrypt_with_keyring( ciphertext, _ = client.encrypt( source=EXAMPLE_DATA, keyring=kms_rsa_keyring, - encryption_context=encryption_context + encryption_context=encryption_context, + algorithm=AlgorithmSuite.AES_256_GCM_HKDF_SHA512_COMMIT_KEY ) # 6. Demonstrate that the ciphertext and plaintext are different. diff --git a/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py index 86d3bf6b1..1f1f34adb 100644 --- a/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py +++ b/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py @@ -11,4 +11,15 @@ def test_encrypt_and_decrypt_with_keyring(): """Test function for encrypt and decrypt using the AWS KMS RSA Keyring example.""" kms_rsa_key_id = "arn:aws:kms:us-west-2:370957321024:key/mrk-63d386cb70614ea59b32ad65c9315297" - encrypt_and_decrypt_with_keyring(kms_rsa_key_id) + + # THIS IS A PUBLIC RESOURCE AND SHOULD NOT BE USED IN A PRODUCTION ENVIRONMENT + kms_rsa_public_key = bytes("-----BEGIN PUBLIC KEY-----\n" + + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ" + + "oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN" + + "/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK" + + "fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2" + + "r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9" + + "yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou" + + "twIDAQAB" + + "\n-----END PUBLIC KEY-----", 'utf-8') + encrypt_and_decrypt_with_keyring(kms_rsa_key_id, kms_rsa_public_key) From f7a0a7a9a363fb31e19637c1b933d55e53294e4f Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Tue, 30 Apr 2024 13:09:43 -0700 Subject: [PATCH 168/180] fix --- examples/src/keyrings/aws_kms_rsa_keyring_example.py | 10 +++++----- .../keyrings/test_i_aws_kms_rsa_keyring_example.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py index a3c65a4b0..665eae2ca 100644 --- a/examples/src/keyrings/aws_kms_rsa_keyring_example.py +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -37,15 +37,15 @@ def encrypt_and_decrypt_with_keyring( kms_rsa_key_id: str, - kms_rsa_public_key: str + public_key: str ): """Demonstrate an encrypt/decrypt cycle using an AWS KMS RSA keyring. - Usage: encrypt_and_decrypt_with_keyring(kms_rsa_key_id, kms_rsa_public_key) + Usage: encrypt_and_decrypt_with_keyring(kms_rsa_key_id, public_key) :param kms_rsa_key_id: KMS RSA Key identifier for the KMS RSA key you want to use :type kms_rsa_key_id: string - :param kms_rsa_public_key: KMS RSA public key you want to use - :type kms_rsa_public_key: string + :param public_key: public key you want to use + :type public_key: string For more information on KMS Key identifiers, see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id @@ -82,7 +82,7 @@ def encrypt_and_decrypt_with_keyring( ) keyring_input: CreateAwsKmsRsaKeyringInput = CreateAwsKmsRsaKeyringInput( - public_key=kms_rsa_public_key, + public_key=public_key, kms_key_id=kms_rsa_key_id, encryption_algorithm="RSAES_OAEP_SHA_256", kms_client=kms_client diff --git a/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py index 1f1f34adb..e2065cea8 100644 --- a/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py +++ b/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py @@ -13,7 +13,7 @@ def test_encrypt_and_decrypt_with_keyring(): kms_rsa_key_id = "arn:aws:kms:us-west-2:370957321024:key/mrk-63d386cb70614ea59b32ad65c9315297" # THIS IS A PUBLIC RESOURCE AND SHOULD NOT BE USED IN A PRODUCTION ENVIRONMENT - kms_rsa_public_key = bytes("-----BEGIN PUBLIC KEY-----\n" + public_key = bytes("-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ" + "oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN" + "/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK" @@ -22,4 +22,4 @@ def test_encrypt_and_decrypt_with_keyring(): + "yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou" + "twIDAQAB" + "\n-----END PUBLIC KEY-----", 'utf-8') - encrypt_and_decrypt_with_keyring(kms_rsa_key_id, kms_rsa_public_key) + encrypt_and_decrypt_with_keyring(kms_rsa_key_id, public_key) From 86845ea5045a7e3393e59bbac1ffa474f9269084 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Tue, 30 Apr 2024 17:40:02 -0700 Subject: [PATCH 169/180] fix --- .../test_streaming_client_stream_decryptor.py | 136 ------------------ 1 file changed, 136 deletions(-) diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index be9304006..ce3d6ee3c 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -365,142 +365,6 @@ 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") def test_read_header_frame_too_large(self, mock_derive_datakey): self.mock_header.content_type = ContentType.FRAMED_DATA From ca8943b256399248cef711896887d6a1a1e88dae Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Wed, 1 May 2024 10:39:28 -0700 Subject: [PATCH 170/180] fix --- .../test_i_aws_kms_rsa_keyring_example.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py index e2065cea8..4d953947a 100644 --- a/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py +++ b/examples/test/keyrings/test_i_aws_kms_rsa_keyring_example.py @@ -14,12 +14,12 @@ def test_encrypt_and_decrypt_with_keyring(): # THIS IS A PUBLIC RESOURCE AND SHOULD NOT BE USED IN A PRODUCTION ENVIRONMENT public_key = bytes("-----BEGIN PUBLIC KEY-----\n" - + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ" - + "oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN" - + "/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK" - + "fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2" - + "r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9" - + "yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou" - + "twIDAQAB" - + "\n-----END PUBLIC KEY-----", 'utf-8') + + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ" + + "oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN" + + "/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK" + + "fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2" + + "r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9" + + "yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou" + + "twIDAQAB" + + "\n-----END PUBLIC KEY-----", 'utf-8') encrypt_and_decrypt_with_keyring(kms_rsa_key_id, public_key) From c56e924f776cf9b5f3fe6b255a2cb916004e0eae Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Wed, 1 May 2024 10:46:36 -0700 Subject: [PATCH 171/180] fix(-) --- examples/src/keyrings/aws_kms_rsa_keyring_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py index 665eae2ca..d8d50b7e3 100644 --- a/examples/src/keyrings/aws_kms_rsa_keyring_example.py +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -97,7 +97,7 @@ def encrypt_and_decrypt_with_keyring( source=EXAMPLE_DATA, keyring=kms_rsa_keyring, encryption_context=encryption_context, - algorithm=AlgorithmSuite.AES_256_GCM_HKDF_SHA512_COMMIT_KEY + algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA256 ) # 6. Demonstrate that the ciphertext and plaintext are different. From 3c7292b6b1ec6393964bc0e6394a02c97b658b05 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Wed, 1 May 2024 13:17:36 -0700 Subject: [PATCH 172/180] fix --- examples/src/keyrings/aws_kms_rsa_keyring_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py index d8d50b7e3..ccdb681b3 100644 --- a/examples/src/keyrings/aws_kms_rsa_keyring_example.py +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -58,7 +58,7 @@ def encrypt_and_decrypt_with_keyring( # 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 + commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT ) # 2. Create a boto3 client for KMS. From 7cb586458b05a255bb2ffc8a5ff8c2401bac77bd Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Wed, 1 May 2024 14:07:49 -0700 Subject: [PATCH 173/180] update --- examples/src/keyrings/aws_kms_rsa_keyring_example.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py index ccdb681b3..bced401f2 100644 --- a/examples/src/keyrings/aws_kms_rsa_keyring_example.py +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -58,7 +58,7 @@ def encrypt_and_decrypt_with_keyring( # 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.FORBID_ENCRYPT_ALLOW_DECRYPT + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT ) # 2. Create a boto3 client for KMS. @@ -97,7 +97,7 @@ def encrypt_and_decrypt_with_keyring( source=EXAMPLE_DATA, keyring=kms_rsa_keyring, encryption_context=encryption_context, - algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA256 + algorithm=AlgorithmSuite.AES_256_GCM_HKDF_SHA512_COMMIT_KEY ) # 6. Demonstrate that the ciphertext and plaintext are different. From d2b79c9c0802eadd21e493b155cdd3ce43509020 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 1 May 2024 14:56:06 -0700 Subject: [PATCH 174/180] fix cb --- .../materials_managers/mpl/cmm.py | 9 ++++-- .../unit/test_material_managers_mpl_cmm.py | 32 +++++++++---------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index 1f2102757..30ba3c5a9 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -78,18 +78,21 @@ def get_encryption_materials( def _native_to_mpl_get_encryption_materials( request: EncryptionMaterialsRequest ) -> 'MPL_GetEncryptionMaterialsInput': - commitment_policy = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy( + commitment_policy = CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy( request.commitment_policy ) output: MPL_GetEncryptionMaterialsInput = MPL_GetEncryptionMaterialsInput( encryption_context=request.encryption_context, commitment_policy=commitment_policy, + algorithm_suite_id=CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( + request.algorithm.algorithm_id + ), max_plaintext_length=request.plaintext_length, ) return output @staticmethod - def _native_to_mpl_commmitment_policy( + def _native_to_mpl_commitment_policy( native_commitment_policy: CommitmentPolicy ) -> 'MPL_CommitmentPolicyESDK': if native_commitment_policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT: @@ -138,7 +141,7 @@ def _create_mpl_decrypt_materials_input_from_request( algorithm_suite_id=CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( request.algorithm.algorithm_id ), - commitment_policy=CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy( + commitment_policy=CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy( request.commitment_policy ), encrypted_data_keys=list_edks, diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index 0551e8f30..bb2538b89 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -97,7 +97,7 @@ def test_GIVEN_valid_request_WHEN_get_encryption_materials_THEN_return_Encryptio @patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" - "._native_to_mpl_commmitment_policy") + "._native_to_mpl_commitment_policy") def test_GIVEN_mpl_cmm_raises_MPLException_WHEN_get_encryption_materials_THEN_raise_ESDKException( _ ): @@ -112,7 +112,7 @@ def test_GIVEN_mpl_cmm_raises_MPLException_WHEN_get_encryption_materials_THEN_ra @patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" - "._native_to_mpl_commmitment_policy") + "._native_to_mpl_commitment_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 ): @@ -132,50 +132,50 @@ 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(): # noqa: E501 +def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commitment_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 - # When: _native_to_mpl_commmitment_policy - output = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) + # When: _native_to_mpl_commitment_policy + output = CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy(native_commitment_policy) # Then: Returns MPL FORBID_ENCRYPT_ALLOW_DECRYPT 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(): # noqa: E501 +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commitment_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 - # When: _native_to_mpl_commmitment_policy - output = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) + # When: _native_to_mpl_commitment_policy + output = CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy(native_commitment_policy) # Then: Returns MPL REQUIRE_ENCRYPT_ALLOW_DECRYPT 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(): # noqa: E501 +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_native_to_mpl_commitment_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 - # When: _native_to_mpl_commmitment_policy - output = CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) + # When: _native_to_mpl_commitment_policy + output = CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy(native_commitment_policy) # Then: Returns MPL REQUIRE_ENCRYPT_REQUIRE_DECRYPT 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(): +def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_native_to_mpl_commitment_policy_THEN_raise_ValueError(): # Given: invalid native commitment policy native_commitment_policy = "not a commitment policy" # Then: Raises ValueError with pytest.raises(ValueError): - # When: _native_to_mpl_commmitment_policy - CryptoMaterialsManagerFromMPL._native_to_mpl_commmitment_policy(native_commitment_policy) + # When: _native_to_mpl_commitment_policy + CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy(native_commitment_policy) @patch.object(mock_mpl_cmm, "decrypt_materials") @@ -239,7 +239,7 @@ def test_GIVEN_valid_native_algorithm_id_WHEN_native_algorithm_id_to_mpl_algorit @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") + "._native_to_mpl_commitment_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, @@ -248,7 +248,7 @@ def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_reques 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 + # Given: _native_to_mpl_commitment_policy returns some MPL commitment policy mock_commitment_policy = MagicMock(__class__=MPL_CommitmentPolicyESDK) mock_mpl_commitment_policy.return_value = mock_commitment_policy From 324f6838b584628631ea64657e7342447252b714 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 1 May 2024 15:05:42 -0700 Subject: [PATCH 175/180] fix --- .../materials_managers/mpl/cmm.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index 30ba3c5a9..0e5dc958e 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -78,18 +78,20 @@ def get_encryption_materials( def _native_to_mpl_get_encryption_materials( request: EncryptionMaterialsRequest ) -> 'MPL_GetEncryptionMaterialsInput': - commitment_policy = CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy( - request.commitment_policy - ) - output: MPL_GetEncryptionMaterialsInput = MPL_GetEncryptionMaterialsInput( - encryption_context=request.encryption_context, - commitment_policy=commitment_policy, - algorithm_suite_id=CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( + output_kwargs = { + "encryption_context": request.encryption_context, + "max_plaintext_length": request.plaintext_length, + "commitment_policy": CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy( + request.commitment_policy + ) + } + + if request.algorithm is not None: + output_kwargs["algorithm_suite_id"] = CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( request.algorithm.algorithm_id - ), - max_plaintext_length=request.plaintext_length, - ) - return output + ) + + return MPL_GetEncryptionMaterialsInput(**output_kwargs) @staticmethod def _native_to_mpl_commitment_policy( From 8c9d4b16ec73dbea49749f024611e3e4df2e1cc0 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Wed, 1 May 2024 17:12:16 -0700 Subject: [PATCH 176/180] fix: unit tests for _native_algorithm_id_to_mpl_algorithm_id --- .../materials_managers/mpl/cmm.py | 5 +++-- .../mpl/unit/test_material_managers_mpl_cmm.py | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py index 0e5dc958e..86ff37dd2 100644 --- a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -87,8 +87,9 @@ def _native_to_mpl_get_encryption_materials( } if request.algorithm is not None: - output_kwargs["algorithm_suite_id"] = CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( - request.algorithm.algorithm_id + output_kwargs["algorithm_suite_id"] = \ + CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( + request.algorithm.algorithm_id ) return MPL_GetEncryptionMaterialsInput(**output_kwargs) diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py index bb2538b89..603446550 100644 --- a/test/mpl/unit/test_material_managers_mpl_cmm.py +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -96,11 +96,18 @@ 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_algorithm_id_to_mpl_algorithm_id") @patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" "._native_to_mpl_commitment_policy") def test_GIVEN_mpl_cmm_raises_MPLException_WHEN_get_encryption_materials_THEN_raise_ESDKException( - _ + _, + 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 + # Then: Raises AWSEncryptionSDKClientError with pytest.raises(AWSEncryptionSDKClientError): # Given: mpl_cmm.get_encryption_materials raises MPL exception @@ -111,11 +118,18 @@ 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_algorithm_id_to_mpl_algorithm_id") @patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" "._native_to_mpl_commitment_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 + 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: commitment policy is some MPL ESDK commitment policy mock_commitment_policy = MagicMock(__class__=MPL_CommitmentPolicyESDK) mock_mpl_commitment_policy.return_value = mock_commitment_policy From 3c41753bb70bea8f4edaa0900c706ebbeb3f8089 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Thu, 2 May 2024 13:13:48 -0700 Subject: [PATCH 177/180] fixed comments --- .../aws_kms_discovery_keyring_example.py | 50 +++++++++++++++---- ...aws_kms_discovery_multi_keyring_example.py | 49 +++++++++++++++--- .../keyrings/aws_kms_multi_keyring_example.py | 4 +- .../keyrings/aws_kms_rsa_keyring_example.py | 2 +- examples/src/keyrings/hierarchical_keyring.py | 2 +- .../src/keyrings/multi_keyring_example.py | 2 +- 6 files changed, 87 insertions(+), 22 deletions(-) diff --git a/examples/src/keyrings/aws_kms_discovery_keyring_example.py b/examples/src/keyrings/aws_kms_discovery_keyring_example.py index 7afd1d627..b9c70d41b 100644 --- a/examples/src/keyrings/aws_kms_discovery_keyring_example.py +++ b/examples/src/keyrings/aws_kms_discovery_keyring_example.py @@ -12,9 +12,7 @@ Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt -operation fails. The exception is the AWS Encryption SDK for C, where the encrypt operation -ignores a standard discovery keyring, but fails if you specify a multi-Region discovery -keyring, alone or in a multi-keyring. +operation fails. When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or @@ -28,7 +26,7 @@ 2. Encryption context is correct in the decrypted message header 3. Decrypted plaintext value matches EXAMPLE_DATA 4. Decryption is only possible if the Discovery Keyring contains the correct AWS Account ID's to -which the KMS key used for encryption belongs + which the KMS key used for encryption belongs These sanity checks are for demonstration in the example only. You do not need these in your code. For more information on how to use KMS Discovery keyrings, see @@ -59,6 +57,32 @@ EXAMPLE_DATA: bytes = b"Hello World" +def get_account_id_from_kms_key_id(kms_key_id: str) -> str: + """ + Get the AWS Account ID from the KMS Key ID. + + Usage: get_account_id_from_kms_key_id(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use + :type kms_key_id: string + :return: AWS Account ID + :rtype: string + """ + return kms_key_id.split(":")[4] + + +def get_aws_region_from_kms_key_id(kms_key_id: str) -> str: + """ + Get the AWS Region from the KMS Key ID. + + Usage: get_aws_region_from_kms_key_id(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use + :type kms_key_id: string + :return: AWS Region + :rtype: string + """ + return kms_key_id.split(":")[3] + + def encrypt_and_decrypt_with_keyring( kms_key_id: str ): @@ -84,7 +108,11 @@ def encrypt_and_decrypt_with_keyring( ) # 2. Create a boto3 client for KMS. - kms_client = boto3.client('kms', region_name="us-west-2") + + # Get the AWS Region from the KMS Key ID. + region_name = get_aws_region_from_kms_key_id(kms_key_id) + + kms_client = boto3.client('kms', region_name=region_name) # 3. Create encryption context. # Remember that your encryption context is NOT SECRET. @@ -114,7 +142,7 @@ def encrypt_and_decrypt_with_keyring( input=kms_keyring_input ) - # 5. Encrypt the data for the encryptionContext + # 5. Encrypt the data with the encryptionContext ciphertext, _ = client.encrypt( source=EXAMPLE_DATA, keyring=encrypt_kms_keyring, @@ -129,10 +157,14 @@ def encrypt_and_decrypt_with_keyring( # 7. Now create a Discovery keyring to use for decryption. We'll add a discovery filter # so that we limit the set of ciphertexts we are willing to decrypt to only ones # created by KMS keys in our account and partition. + + # Get the AWS Account ID from the KMS Key ID to use in the discovery filter + aws_account_id: str = get_account_id_from_kms_key_id(kms_key_id=kms_key_id) + discovery_keyring_input: CreateAwsKmsDiscoveryKeyringInput = CreateAwsKmsDiscoveryKeyringInput( kms_client=kms_client, discovery_filter=DiscoveryFilter( - account_ids=["658956600833"], + account_ids=[aws_account_id], partition="aws" ) ) @@ -171,8 +203,8 @@ def encrypt_and_decrypt_with_keyring( # 11. Demonstrate that if a discovery keyring (Bob's) doesn't have the correct AWS Account ID's, # the decrypt will fail with an error message - # Note that the Account ID '888888888888' used here is different than the one used - # during encryption '658956600833' + # Note that this assumes Account ID used here ('888888888888') is different than the one used + # during encryption discovery_keyring_input_bob: CreateAwsKmsDiscoveryKeyringInput = \ CreateAwsKmsDiscoveryKeyringInput( kms_client=kms_client, diff --git a/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py b/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py index 7a1cc5be1..c080cc947 100644 --- a/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py +++ b/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py @@ -11,9 +11,7 @@ Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt -operation fails. The exception is the AWS Encryption SDK for C, where the encrypt operation -ignores a standard discovery keyring, but fails if you specify a multi-Region discovery -keyring, alone or in a multi-keyring. +operation fails. When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or @@ -55,8 +53,35 @@ EXAMPLE_DATA: bytes = b"Hello World" +def get_account_id_from_kms_key_id(kms_key_id: str) -> str: + """ + Get the AWS Account ID from the KMS Key ID. + + Usage: get_account_id_from_kms_key_id(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use + :type kms_key_id: string + :return: AWS Account ID + :rtype: string + """ + return kms_key_id.split(":")[4] + + +def get_aws_region_from_kms_key_id(kms_key_id: str) -> str: + """ + Get the AWS Region from the KMS Key ID. + + Usage: get_aws_region_from_kms_key_id(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use + :type kms_key_id: string + :return: AWS Region + :rtype: string + """ + return kms_key_id.split(":")[3] + + def encrypt_and_decrypt_with_keyring( - kms_key_id: str + kms_key_id: str, + aws_regions: list(str) ): """Demonstrate an encrypt/decrypt cycle using an AWS KMS Discovery Multi Keyring. @@ -80,7 +105,11 @@ def encrypt_and_decrypt_with_keyring( ) # 2. Create a boto3 client for KMS. - kms_client = boto3.client('kms', region_name="us-west-2") + + # Get the AWS Region from the KMS Key ID. + region_name = get_aws_region_from_kms_key_id(kms_key_id) + + kms_client = boto3.client('kms', region_name=region_name) # 3. Create encryption context. # Remember that your encryption context is NOT SECRET. @@ -110,7 +139,7 @@ def encrypt_and_decrypt_with_keyring( input=kms_keyring_input ) - # 5. Encrypt the data for the encryptionContext + # 5. Encrypt the data with the encryptionContext ciphertext, _ = client.encrypt( source=EXAMPLE_DATA, keyring=encrypt_kms_keyring, @@ -125,11 +154,15 @@ def encrypt_and_decrypt_with_keyring( # 7. Now create a Discovery Multi keyring to use for decryption. We'll add a discovery filter # so that we limit the set of ciphertexts we are willing to decrypt to only ones # created by KMS keys in our account and partition. + + # Get the AWS Account ID from the KMS Key ID to use in the discovery filter + aws_account_id: str = get_account_id_from_kms_key_id(kms_key_id=kms_key_id) + discovery_multi_keyring_input: CreateAwsKmsDiscoveryMultiKeyringInput = \ CreateAwsKmsDiscoveryMultiKeyringInput( - regions=["us-west-2", "us-east-1"], + regions=aws_regions, discovery_filter=DiscoveryFilter( - account_ids=["658956600833"], + account_ids=[aws_account_id], partition="aws" ) ) diff --git a/examples/src/keyrings/aws_kms_multi_keyring_example.py b/examples/src/keyrings/aws_kms_multi_keyring_example.py index f0f6a7bf6..655a3b87f 100644 --- a/examples/src/keyrings/aws_kms_multi_keyring_example.py +++ b/examples/src/keyrings/aws_kms_multi_keyring_example.py @@ -26,7 +26,7 @@ with an encryption context. This example also includes some sanity checks for demonstration: 1. Ciphertext and plaintext data are not the same 2. Decryption of ciphertext is possible using the multi_keyring, -and every one of the keyrings from the multi_keyring separately + and every one of the keyrings from the multi_keyring separately 3. All decrypted plaintext value match EXAMPLE_DATA These sanity checks are for demonstration in the example only. You do not need these in your code. @@ -115,7 +115,7 @@ def encrypt_and_decrypt_with_keyring( input=kms_multi_keyring_input ) - # 4. Encrypt the data for the encryptionContext + # 4. Encrypt the data with the encryptionContext ciphertext, _ = client.encrypt( source=EXAMPLE_DATA, keyring=kms_multi_keyring, diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py index bced401f2..49324d86d 100644 --- a/examples/src/keyrings/aws_kms_rsa_keyring_example.py +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -92,7 +92,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_rsa_keyring, diff --git a/examples/src/keyrings/hierarchical_keyring.py b/examples/src/keyrings/hierarchical_keyring.py index 32a6cbf8b..5642a0b71 100644 --- a/examples/src/keyrings/hierarchical_keyring.py +++ b/examples/src/keyrings/hierarchical_keyring.py @@ -154,7 +154,7 @@ def encrypt_and_decrypt_with_keyring( "the data you are handling": "is what you think it is", } - # 8. Encrypt the data for encryptionContextA & encryptionContextB + # 8. Encrypt the data with encryptionContextA & encryptionContextB ciphertext_A, _ = client.encrypt( source=EXAMPLE_DATA, keyring=hierarchical_keyring, diff --git a/examples/src/keyrings/multi_keyring_example.py b/examples/src/keyrings/multi_keyring_example.py index 0a6588a8e..6fb5800a8 100644 --- a/examples/src/keyrings/multi_keyring_example.py +++ b/examples/src/keyrings/multi_keyring_example.py @@ -155,7 +155,7 @@ def encrypt_and_decrypt_with_keyring( input=multi_keyring_input ) - # 8. Encrypt the data for the encryptionContext + # 8. Encrypt the data with the encryptionContext ciphertext, _ = client.encrypt( source=EXAMPLE_DATA, keyring=multi_keyring, From 036662f4c31c64ac0f7b7097f43f6225a8977d66 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Thu, 2 May 2024 14:11:08 -0700 Subject: [PATCH 178/180] fix --- ...aws_kms_discovery_multi_keyring_example.py | 6 ++++-- .../keyrings/aws_kms_multi_keyring_example.py | 19 +++++++++++++++++-- .../keyrings/aws_kms_rsa_keyring_example.py | 3 +++ ...aws_kms_discovery_multi_keyring_example.py | 3 ++- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py b/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py index c080cc947..ef0965b38 100644 --- a/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py +++ b/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py @@ -81,14 +81,16 @@ def get_aws_region_from_kms_key_id(kms_key_id: str) -> str: def encrypt_and_decrypt_with_keyring( kms_key_id: str, - aws_regions: list(str) + aws_regions: list[str] ): """Demonstrate an encrypt/decrypt cycle using an AWS KMS Discovery Multi Keyring. - Usage: encrypt_and_decrypt_with_keyring(kms_key_id) + Usage: encrypt_and_decrypt_with_keyring(kms_key_id, aws_regions) :param kms_key_id: KMS Key identifier for the KMS key you want to use for creating the kms_keyring used for encryption :type kms_key_id: string + :param aws_regions: List of AWS Regions to use for creating the discovery multi keyring + :type aws_regions: list[string] For more information on KMS Key identifiers, see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id diff --git a/examples/src/keyrings/aws_kms_multi_keyring_example.py b/examples/src/keyrings/aws_kms_multi_keyring_example.py index 655a3b87f..ca8072e90 100644 --- a/examples/src/keyrings/aws_kms_multi_keyring_example.py +++ b/examples/src/keyrings/aws_kms_multi_keyring_example.py @@ -56,6 +56,19 @@ EXAMPLE_DATA: bytes = b"Hello World" +def get_aws_region_from_kms_key_id(kms_key_id: str) -> str: + """ + Get the AWS Region from the KMS Key ID. + + Usage: get_aws_region_from_kms_key_id(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use + :type kms_key_id: string + :return: AWS Region + :rtype: string + """ + return kms_key_id.split(":")[3] + + def encrypt_and_decrypt_with_keyring( default_region_kms_key_id: str, second_region_kms_key_id: str @@ -144,7 +157,8 @@ def encrypt_and_decrypt_with_keyring( # `default_region_kms_key_id` directly. # 7a. Create a boto3 client for KMS for the default region. - default_region_kms_client = boto3.client('kms', region_name="us-west-2") + default_region = get_aws_region_from_kms_key_id(default_region_kms_key_id) + default_region_kms_client = boto3.client('kms', region_name=default_region) # 7b. Create KMS keyring default_region_kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( @@ -170,7 +184,8 @@ def encrypt_and_decrypt_with_keyring( # `second_region_kms_key_id` directly. # 8a. Create a boto3 client for KMS for the second region. - second_region_kms_client = boto3.client('kms', region_name="eu-central-1") + second_region = get_aws_region_from_kms_key_id(second_region_kms_key_id) + second_region_kms_client = boto3.client('kms', region_name=second_region) # 8b. Create KMS keyring second_region_kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py index 49324d86d..7cb7e26a7 100644 --- a/examples/src/keyrings/aws_kms_rsa_keyring_example.py +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -81,6 +81,9 @@ def encrypt_and_decrypt_with_keyring( config=MaterialProvidersConfig() ) + # # Create the AWS KMS RSA keyring input + # For more information on the allowed encryption algorithms, please see + # https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/framework/aws-kms/aws-kms-rsa-keyring.md#supported-padding-schemes keyring_input: CreateAwsKmsRsaKeyringInput = CreateAwsKmsRsaKeyringInput( public_key=public_key, kms_key_id=kms_rsa_key_id, diff --git a/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py index 833698f6b..f9a78218b 100644 --- a/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py +++ b/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py @@ -11,4 +11,5 @@ def test_encrypt_and_decrypt_with_keyring(): """Test function for encrypt and decrypt using the AWS KMS Discovery Multi 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) + aws_regions = ["us-east-1", "us-west-2"] + encrypt_and_decrypt_with_keyring(kms_key_id, aws_regions) From cbf72ff77b82d134193e7f3588ce2c9e85397477 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila <61410899+RitvikKapila@users.noreply.github.com> Date: Thu, 2 May 2024 15:15:50 -0700 Subject: [PATCH 179/180] Update aws_kms_rsa_keyring_example.py --- examples/src/keyrings/aws_kms_rsa_keyring_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py index 7cb7e26a7..a06e70b40 100644 --- a/examples/src/keyrings/aws_kms_rsa_keyring_example.py +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -83,7 +83,7 @@ def encrypt_and_decrypt_with_keyring( # # Create the AWS KMS RSA keyring input # For more information on the allowed encryption algorithms, please see - # https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/framework/aws-kms/aws-kms-rsa-keyring.md#supported-padding-schemes + # https://docs.aws.amazon.com/kms/latest/developerguide/asymmetric-key-specs.html#key-spec-rsa keyring_input: CreateAwsKmsRsaKeyringInput = CreateAwsKmsRsaKeyringInput( public_key=public_key, kms_key_id=kms_rsa_key_id, From 41e40f3f2cc11bea87701ffe40223951c5e4e2ed Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Thu, 2 May 2024 16:25:23 -0700 Subject: [PATCH 180/180] fix --- .../aws_kms_discovery_keyring_example.py | 45 ++++--------------- ...aws_kms_discovery_multi_keyring_example.py | 41 +++-------------- .../keyrings/aws_kms_multi_keyring_example.py | 34 +++++++------- .../keyrings/aws_kms_rsa_keyring_example.py | 2 +- .../src/keyrings/multi_keyring_example.py | 2 + ...est_i_aws_kms_discovery_keyring_example.py | 4 +- ...aws_kms_discovery_multi_keyring_example.py | 3 +- .../test_i_aws_kms_multi_keyring_example.py | 7 ++- 8 files changed, 43 insertions(+), 95 deletions(-) diff --git a/examples/src/keyrings/aws_kms_discovery_keyring_example.py b/examples/src/keyrings/aws_kms_discovery_keyring_example.py index b9c70d41b..24dc111f1 100644 --- a/examples/src/keyrings/aws_kms_discovery_keyring_example.py +++ b/examples/src/keyrings/aws_kms_discovery_keyring_example.py @@ -57,41 +57,21 @@ EXAMPLE_DATA: bytes = b"Hello World" -def get_account_id_from_kms_key_id(kms_key_id: str) -> str: - """ - Get the AWS Account ID from the KMS Key ID. - - Usage: get_account_id_from_kms_key_id(kms_key_id) - :param kms_key_id: KMS Key identifier for the KMS key you want to use - :type kms_key_id: string - :return: AWS Account ID - :rtype: string - """ - return kms_key_id.split(":")[4] - - -def get_aws_region_from_kms_key_id(kms_key_id: str) -> str: - """ - Get the AWS Region from the KMS Key ID. - - Usage: get_aws_region_from_kms_key_id(kms_key_id) - :param kms_key_id: KMS Key identifier for the KMS key you want to use - :type kms_key_id: string - :return: AWS Region - :rtype: string - """ - return kms_key_id.split(":")[3] - - def encrypt_and_decrypt_with_keyring( - kms_key_id: str + kms_key_id: str, + aws_account_id: str, + aws_region: str ): """Demonstrate an encrypt/decrypt cycle using an AWS KMS Discovery Keyring. - Usage: encrypt_and_decrypt_with_keyring(kms_key_id) + Usage: encrypt_and_decrypt_with_keyring(kms_key_id, aws_account_id) :param kms_key_id: KMS Key identifier for the KMS key you want to use for creating the kms_keyring used for encryption :type kms_key_id: string + :param aws_account_id: AWS Account ID to use in the discovery filter + :type aws_account_id: string + :param aws_region: AWS Region to use for the kms client + :type aws_region: string For more information on KMS Key identifiers, see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id @@ -108,11 +88,7 @@ def encrypt_and_decrypt_with_keyring( ) # 2. Create a boto3 client for KMS. - - # Get the AWS Region from the KMS Key ID. - region_name = get_aws_region_from_kms_key_id(kms_key_id) - - kms_client = boto3.client('kms', region_name=region_name) + kms_client = boto3.client('kms', region_name=aws_region) # 3. Create encryption context. # Remember that your encryption context is NOT SECRET. @@ -158,9 +134,6 @@ def encrypt_and_decrypt_with_keyring( # so that we limit the set of ciphertexts we are willing to decrypt to only ones # created by KMS keys in our account and partition. - # Get the AWS Account ID from the KMS Key ID to use in the discovery filter - aws_account_id: str = get_account_id_from_kms_key_id(kms_key_id=kms_key_id) - discovery_keyring_input: CreateAwsKmsDiscoveryKeyringInput = CreateAwsKmsDiscoveryKeyringInput( kms_client=kms_client, discovery_filter=DiscoveryFilter( diff --git a/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py b/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py index ef0965b38..0830ecb58 100644 --- a/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py +++ b/examples/src/keyrings/aws_kms_discovery_multi_keyring_example.py @@ -53,42 +53,19 @@ EXAMPLE_DATA: bytes = b"Hello World" -def get_account_id_from_kms_key_id(kms_key_id: str) -> str: - """ - Get the AWS Account ID from the KMS Key ID. - - Usage: get_account_id_from_kms_key_id(kms_key_id) - :param kms_key_id: KMS Key identifier for the KMS key you want to use - :type kms_key_id: string - :return: AWS Account ID - :rtype: string - """ - return kms_key_id.split(":")[4] - - -def get_aws_region_from_kms_key_id(kms_key_id: str) -> str: - """ - Get the AWS Region from the KMS Key ID. - - Usage: get_aws_region_from_kms_key_id(kms_key_id) - :param kms_key_id: KMS Key identifier for the KMS key you want to use - :type kms_key_id: string - :return: AWS Region - :rtype: string - """ - return kms_key_id.split(":")[3] - - def encrypt_and_decrypt_with_keyring( kms_key_id: str, + aws_account_id: str, aws_regions: list[str] ): """Demonstrate an encrypt/decrypt cycle using an AWS KMS Discovery Multi Keyring. - Usage: encrypt_and_decrypt_with_keyring(kms_key_id, aws_regions) + Usage: encrypt_and_decrypt_with_keyring(kms_key_id, aws_account_id, aws_regions) :param kms_key_id: KMS Key identifier for the KMS key you want to use for creating the kms_keyring used for encryption :type kms_key_id: string + :param aws_account_id: AWS Account ID to use in the discovery filter + :type aws_account_id: string :param aws_regions: List of AWS Regions to use for creating the discovery multi keyring :type aws_regions: list[string] @@ -107,11 +84,7 @@ def encrypt_and_decrypt_with_keyring( ) # 2. Create a boto3 client for KMS. - - # Get the AWS Region from the KMS Key ID. - region_name = get_aws_region_from_kms_key_id(kms_key_id) - - kms_client = boto3.client('kms', region_name=region_name) + kms_client = boto3.client('kms', region_name="us-west-2") # 3. Create encryption context. # Remember that your encryption context is NOT SECRET. @@ -156,10 +129,6 @@ def encrypt_and_decrypt_with_keyring( # 7. Now create a Discovery Multi keyring to use for decryption. We'll add a discovery filter # so that we limit the set of ciphertexts we are willing to decrypt to only ones # created by KMS keys in our account and partition. - - # Get the AWS Account ID from the KMS Key ID to use in the discovery filter - aws_account_id: str = get_account_id_from_kms_key_id(kms_key_id=kms_key_id) - discovery_multi_keyring_input: CreateAwsKmsDiscoveryMultiKeyringInput = \ CreateAwsKmsDiscoveryMultiKeyringInput( regions=aws_regions, diff --git a/examples/src/keyrings/aws_kms_multi_keyring_example.py b/examples/src/keyrings/aws_kms_multi_keyring_example.py index ca8072e90..0e556f5e3 100644 --- a/examples/src/keyrings/aws_kms_multi_keyring_example.py +++ b/examples/src/keyrings/aws_kms_multi_keyring_example.py @@ -26,7 +26,7 @@ with an encryption context. This example also includes some sanity checks for demonstration: 1. Ciphertext and plaintext data are not the same 2. Decryption of ciphertext is possible using the multi_keyring, - and every one of the keyrings from the multi_keyring separately + and every one of the keyrings from the multi_keyring separately 3. All decrypted plaintext value match EXAMPLE_DATA These sanity checks are for demonstration in the example only. You do not need these in your code. @@ -56,36 +56,32 @@ EXAMPLE_DATA: bytes = b"Hello World" -def get_aws_region_from_kms_key_id(kms_key_id: str) -> str: - """ - Get the AWS Region from the KMS Key ID. - - Usage: get_aws_region_from_kms_key_id(kms_key_id) - :param kms_key_id: KMS Key identifier for the KMS key you want to use - :type kms_key_id: string - :return: AWS Region - :rtype: string - """ - return kms_key_id.split(":")[3] - - def encrypt_and_decrypt_with_keyring( default_region_kms_key_id: str, - second_region_kms_key_id: str + second_region_kms_key_id: str, + default_region: str, + second_region: str ): """Demonstrate an encrypt/decrypt cycle using an AWS KMS Multi keyring. The multi_keyring is created using a KMS keyring as generator keyring and another KMS keyring as a child keyring. For this example, `default_region_kms_key_id` is the generator key id for a KMS key located in your default region, and `second_region_kms_key_id` is the KMS key id - for a KMS Key located in some second Region. + for a KMS Key located in some second region. - Usage: encrypt_and_decrypt_with_keyring(default_region_kms_key_id, second_region_kms_key_id) + Usage: encrypt_and_decrypt_with_keyring(default_region_kms_key_id, + second_region_kms_key_id, + default_region, + second_region) :param default_region_kms_key_id: KMS Key identifier for the default region KMS key you want to use as a generator keyring :type default_region_kms_key_id: string :param second_region_kms_key_id: KMS Key identifier for the second region KMS key you want to use as a child keyring :type second_region_kms_key_id: string + :param default_region: AWS Region for the default region KMS key + :type default_region: string + :param second_region: AWS Region for the second region KMS key + :type second_region: string For more information on KMS Key identifiers, see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id @@ -155,9 +151,9 @@ def encrypt_and_decrypt_with_keyring( # 7. Demonstrate that you can successfully decrypt data using a KMS keyring with just the # `default_region_kms_key_id` directly. + # (This is an example for demonstration; you do not need to do this in your own code.) # 7a. Create a boto3 client for KMS for the default region. - default_region = get_aws_region_from_kms_key_id(default_region_kms_key_id) default_region_kms_client = boto3.client('kms', region_name=default_region) # 7b. Create KMS keyring @@ -182,9 +178,9 @@ def encrypt_and_decrypt_with_keyring( # 8. Demonstrate that you can also successfully decrypt data using a KMS keyring with just the # `second_region_kms_key_id` directly. + # (This is an example for demonstration; you do not need to do this in your own code.) # 8a. Create a boto3 client for KMS for the second region. - second_region = get_aws_region_from_kms_key_id(second_region_kms_key_id) second_region_kms_client = boto3.client('kms', region_name=second_region) # 8b. Create KMS keyring diff --git a/examples/src/keyrings/aws_kms_rsa_keyring_example.py b/examples/src/keyrings/aws_kms_rsa_keyring_example.py index a06e70b40..5c0bbe736 100644 --- a/examples/src/keyrings/aws_kms_rsa_keyring_example.py +++ b/examples/src/keyrings/aws_kms_rsa_keyring_example.py @@ -81,7 +81,7 @@ def encrypt_and_decrypt_with_keyring( config=MaterialProvidersConfig() ) - # # Create the AWS KMS RSA keyring input + # Create the AWS KMS RSA keyring input # For more information on the allowed encryption algorithms, please see # https://docs.aws.amazon.com/kms/latest/developerguide/asymmetric-key-specs.html#key-spec-rsa keyring_input: CreateAwsKmsRsaKeyringInput = CreateAwsKmsRsaKeyringInput( diff --git a/examples/src/keyrings/multi_keyring_example.py b/examples/src/keyrings/multi_keyring_example.py index 6fb5800a8..58e4839ac 100644 --- a/examples/src/keyrings/multi_keyring_example.py +++ b/examples/src/keyrings/multi_keyring_example.py @@ -182,6 +182,7 @@ def encrypt_and_decrypt_with_keyring( # 11. Demonstrate that you can successfully decrypt data using just the `kms_keyring` # directly. + # (This is an example for demonstration; you do not need to do this in your own code.) # 11a. Decrypt your encrypted data using the kms_keyring. plaintext_bytes_kms_keyring, _ = client.decrypt( @@ -195,6 +196,7 @@ def encrypt_and_decrypt_with_keyring( # 12. Demonstrate that you can also successfully decrypt data using the `raw_aes_keyring` # directly. + # (This is an example for demonstration; you do not need to do this in your own code.) # 12a. Decrypt your encrypted data using the raw_aes_keyring. plaintext_bytes_raw_aes_keyring, _ = client.decrypt( diff --git a/examples/test/keyrings/test_i_aws_kms_discovery_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_discovery_keyring_example.py index b2455858e..af3c2a3a3 100644 --- a/examples/test/keyrings/test_i_aws_kms_discovery_keyring_example.py +++ b/examples/test/keyrings/test_i_aws_kms_discovery_keyring_example.py @@ -11,4 +11,6 @@ def test_encrypt_and_decrypt_with_keyring(): """Test function for encrypt and decrypt using the AWS KMS Discovery 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) + aws_account_id = "658956600833" + aws_region = "us-west-2" + encrypt_and_decrypt_with_keyring(kms_key_id, aws_account_id, aws_region) diff --git a/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py index f9a78218b..eff2a1a3b 100644 --- a/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py +++ b/examples/test/keyrings/test_i_aws_kms_discovery_multi_keyring_example.py @@ -11,5 +11,6 @@ def test_encrypt_and_decrypt_with_keyring(): """Test function for encrypt and decrypt using the AWS KMS Discovery Multi Keyring example.""" kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + aws_account_id = "658956600833" aws_regions = ["us-east-1", "us-west-2"] - encrypt_and_decrypt_with_keyring(kms_key_id, aws_regions) + encrypt_and_decrypt_with_keyring(kms_key_id, aws_account_id, aws_regions) diff --git a/examples/test/keyrings/test_i_aws_kms_multi_keyring_example.py b/examples/test/keyrings/test_i_aws_kms_multi_keyring_example.py index e5bc90c30..10f991754 100644 --- a/examples/test/keyrings/test_i_aws_kms_multi_keyring_example.py +++ b/examples/test/keyrings/test_i_aws_kms_multi_keyring_example.py @@ -14,4 +14,9 @@ def test_encrypt_and_decrypt_with_keyring(): "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" second_region_kms_key_id = \ "arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2" - encrypt_and_decrypt_with_keyring(default_region_kms_key_id, second_region_kms_key_id) + default_region = "us-west-2" + second_region = "eu-central-1" + encrypt_and_decrypt_with_keyring(default_region_kms_key_id, + second_region_kms_key_id, + default_region, + second_region)