def test_init_with_discovery_fails(self, mock_add_keys): # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.6 # //= type=test # //# A discovery filter MUST NOT be configured in strict mode. key_ids = ( "arn:aws:kms:us-east-1:222222222222:key/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb", "arn:aws:kms:us-east-1:333333333333:key/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb", ) discovery_filter = DiscoveryFilter(account_ids=["1234"], partition="aws") with pytest.raises(ConfigMismatchError) as excinfo: StrictAwsKmsMasterKeyProvider(key_ids=key_ids, discovery_filter=discovery_filter) excinfo.match( "To enable discovery mode, use a DiscoveryAwsKmsMasterKeyProvider") mock_add_keys.assert_not_called()
def test_new_master_key_with_discovery_filter_success(self, mock_client): # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.7 # //= type=test # //# In discovery mode if a discovery filter is configured the requested AWS # //# KMS key ARN's "partition" MUST match the discovery filter's # //# "partition" and the AWS KMS key ARN's "account" MUST exist in the # //# discovery filter's account id set. mock_client.return_value = self.mock_boto3_client_instance key_info = b"arn:aws:kms:us-east-1:222222222222:key/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb" test = UnitTestBaseKMSMasterKeyProvider() test.config.discovery_filter = DiscoveryFilter( partition="aws", account_ids=["222222222222"]) key = test._new_master_key(key_info) assert key.key_id == key_info mock_client.assert_called_with(to_str(key_info))
def aws_kms_master_key_provider(discovery=True, **kwargs): # type: (bool, **List[Union[Text, str]]) -> Union[DiscoveryAwsKmsMasterKeyProvider, StrictAwsKmsMasterKeyProvider] """Apply post-processing to transform ``KMSMasterKeyProvider``-specific values from CLI arguments to valid ``KMSMasterKeyProvider`` parameters, then call ``KMSMasterKeyprovider`` with those parameters. :param bool discovery: Return a DiscoveryAwsKmsMasterKeyProvider :param dict kwargs: Named parameters collected from CLI arguments as prepared in aws_encryption_sdk_cli.internal.master_key_parsing._parse_master_key_providers_from_args :rtype: aws_encryption_sdk.key_providers.kms.BaseKMSMasterKeyProvider """ kwargs = copy.deepcopy(kwargs) try: profile_names = kwargs.pop("profile") if len(profile_names) != 1: raise BadUserArgumentError( "Only one profile may be specified per master key provider configuration. {} provided." .format(len(profile_names))) profile_name = profile_names[0] # type: Optional[Text] except KeyError: profile_name = None botocore_session = botocore.session.Session(profile=profile_name) botocore_session.user_agent_extra = USER_AGENT_SUFFIX kwargs["botocore_session"] = botocore_session try: region_name = kwargs.pop("region") if len(region_name) != 1: raise BadUserArgumentError( "Only one region may be specified per master key provider configuration. {} provided." .format(len(region_name))) kwargs["region_names"] = region_name except KeyError: pass if discovery: accounts = kwargs.pop("discovery-account", None) partition = kwargs.pop("discovery-partition", None) if accounts and partition: discovery_filter = DiscoveryFilter(account_ids=accounts, partition=partition) kwargs["discovery_filter"] = discovery_filter # type: ignore return DiscoveryAwsKmsMasterKeyProvider(**kwargs) return StrictAwsKmsMasterKeyProvider(**kwargs)
def encrypt_decrypt(key_arn, source_plaintext, botocore_session=None): """Encrypts a string under one KMS customer master key (CMK), then decrypts it using discovery mode. :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK :param bytes source_plaintext: Data to encrypt :param botocore_session: existing botocore session instance :type botocore_session: botocore.session.Session """ encrypt_kwargs = dict(key_ids=[key_arn]) if botocore_session is not None: encrypt_kwargs["botocore_session"] = botocore_session # 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) # Create strict master key provider that is only allowed to encrypt and decrypt using the ARN of the provided key. strict_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**encrypt_kwargs) # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header ciphertext, encrypted_message_header = client.encrypt(source=source_plaintext, key_provider=strict_key_provider) # Create a second master key provider in discovery mode that does not explicitly list the key used to encrypt. # Note: The discovery_filter argument is optional; if you omit this, the AWS Encryption SDK attempts to # decrypt any ciphertext it receives. arn = arn_from_str(key_arn) decrypt_kwargs = dict(discovery_filter=DiscoveryFilter(account_ids=[arn.account_id], partition=arn.partition)) if botocore_session is not None: encrypt_kwargs["botocore_session"] = botocore_session discovery_key_provider = aws_encryption_sdk.DiscoveryAwsKmsMasterKeyProvider(**decrypt_kwargs) # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header. plaintext, decrypted_message_header = client.decrypt(source=ciphertext, key_provider=discovery_key_provider) # Verify that the original message and the decrypted message are the same assert source_plaintext == plaintext # Verify that the encryption context of the encrypted message and decrypted message match assert all( pair in encrypted_message_header.encryption_context.items() for pair in decrypted_message_header.encryption_context.items() )
def _discovery_filter_from_spec(cls, spec): if spec: return DiscoveryFilter(partition=str(spec["partition"]), account_ids=spec["account-ids"]) return None
{ "a": "a thing", "botocore_session": sentinel.botocore_session }, ), ( # with discovery filter { "discovery": True, "discovery-account": ["123"], "discovery-partition": "aws" }, { "botocore_session": sentinel.botocore_session, "discovery_filter": DiscoveryFilter(account_ids=["123"], partition="aws"), }, ), ), ) def test_discovery_master_key_provider_post_processing( patch_botocore_session, patch_discovery_master_key_provider, source, expected): test = key_providers.aws_kms_master_key_provider(**source) patch_discovery_master_key_provider.assert_called_once_with(**expected) assert test is patch_discovery_master_key_provider.return_value @pytest.mark.parametrize( "source, expected",
def test_init_failure_discovery_filter_empty_partition(self): with pytest.raises(ConfigMismatchError) as excinfo: DiscoveryAwsKmsMasterKeyProvider(discovery_filter=DiscoveryFilter( account_ids=["123"], partition="")) excinfo.match("you must include both account ids and partition")
def test_init_failure_discovery_filter_empty_account_id_string(self): with pytest.raises(ConfigMismatchError) as excinfo: DiscoveryAwsKmsMasterKeyProvider(discovery_filter=DiscoveryFilter( account_ids=["123456789012", ""], partition="aws")) excinfo.match("account ids must be non-empty strings")
def test_init_failure_discovery_filter_missing_account_ids(self): with pytest.raises(ConfigMismatchError) as excinfo: DiscoveryAwsKmsMasterKeyProvider(discovery_filter=DiscoveryFilter( partition="aws")) excinfo.match("you must include both account ids and partition")
def encrypt_decrypt(mrk_arn, mrk_arn_second_region, source_plaintext): """Illustrates usage of KMS Multi-Region Keys. :param str mrk_arn: Amazon Resource Name (ARN) of the first KMS MRK :param str mrk_arn_second_region: Amazon Resource Name (ARN) of a related KMS MRK in a different region :param bytes source_plaintext: Data to encrypt """ # Encrypt in the first region # 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) # For this example, set mrk_arn to be a Multi-Region key. # Multi-Region keys have a distinctive key ID that begins with 'mrk'. # For example: "arn:aws:kms:us-east-1:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab". # Create a Strict Multi-Region Key Aware Master Key Provider which targets the Multi-Region key ARN. kwargs = dict(key_ids=[mrk_arn]) strict_key_provider = MRKAwareStrictAwsKmsMasterKeyProvider(**kwargs) # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header ciphertext, _ = client.encrypt(source=source_plaintext, key_provider=strict_key_provider) # Decrypt in a second region # For this example, set mrk_arn_second_region to be Multi-Region key related to key_arn. # Related multi-Region keys have the same key ID. Their key ARNs differs only in the Region field. # For example: "arn:aws:kms:us-west-2:111122223333:key/mrk-1234abcd12ab34cd56ef1234567890ab" # Create a Strict Multi-Region Key Aware Master Key Provider which targets the Multi-Region key in the second region kwargs = dict(key_ids=[mrk_arn_second_region]) strict_key_provider_region_2 = MRKAwareStrictAwsKmsMasterKeyProvider( **kwargs) # Decrypt your ciphertext plaintext, _ = client.decrypt(source=ciphertext, key_provider=strict_key_provider_region_2) # Verify that the original message and the decrypted message are the same assert source_plaintext == plaintext # Decrypt in discovery mode in a second region # First determine what region you want to perform discovery in, as well as what # accounts and partition you want to allow if using a Discovery Filter. # In this example, we just want to use whatever region, account, and partition # our second key is in, in order to ensure we can discover it. # Note that the ARN itself is never used in the configuration. arn = arn_from_str(mrk_arn_second_region) discovery_region = arn.region filter_accounts = [arn.account_id] filter_partition = arn.partition # Configure a Discovery Region and optional Discovery Filter decrypt_kwargs = dict( discovery_filter=DiscoveryFilter(account_ids=filter_accounts, partition=filter_partition), discovery_region=discovery_region, ) # Create an MRK-aware master key provider in discovery mode that targets the second region. # This will cause the provider to try to decrypt using this region whenever it encounters an MRK. discovery_key_provider = MRKAwareDiscoveryAwsKmsMasterKeyProvider( **decrypt_kwargs) # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header. plaintext, _ = client.decrypt(source=ciphertext, key_provider=discovery_key_provider) # Verify that the original message and the decrypted message are the same assert source_plaintext == plaintext