def test_add_master_keys_invalid_arn(self, non_arn):
        """
        Check that the Strict MRK-aware provider uses the default region when creating a new key with an invalid arn.
        """
        with patch.object(self.mock_botocore_session,
                          "get_config_variable",
                          return_value="us-west-2") as mock_get_config:
            provider = MRKAwareStrictAwsKmsMasterKeyProvider(
                botocore_session=self.mock_botocore_session, key_ids=[non_arn])
            master_key = provider._new_master_key(non_arn)

            mock_get_config.assert_called_with("region")
            assert master_key.__class__ == MRKAwareKMSMasterKey
            assert master_key._key_id == non_arn
            self.mock_boto3_session.assert_called_with(botocore_session=ANY)
            self.mock_boto3_session_instance.client.assert_called_with(
                "kms",
                region_name="us-west-2",
                config=provider._user_agent_adding_config,
            )
    def test_add_master_keys_class(self):
        """Check that the MRK-aware provider creates MRKAwareKMSMasterKeys"""
        grant_tokens = (sentinel.grant_token2, sentinel.grant_token2)
        key_id = "arn:aws:kms:eu-west-2:222222222222:key/mrk-aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb"
        provider = MRKAwareStrictAwsKmsMasterKeyProvider(
            key_ids=[key_id], grant_tokens=grant_tokens)

        master_key = provider._new_master_key(key_id)

        # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.7
        # //= type=test
        # //# In strict mode a AWS KMS MRK Aware Master Key (aws-kms-mrk-aware-
        # //# master-key.md) MUST be returned configured with
        assert master_key.__class__ == MRKAwareKMSMasterKey
        assert "eu-west-2" in master_key._key_id
        self.mock_boto3_session.assert_called_with(botocore_session=ANY)
        self.mock_boto3_session_instance.client.assert_called_with(
            "kms",
            region_name="eu-west-2",
            config=provider._user_agent_adding_config,
        )
        assert master_key.config.grant_tokens is grant_tokens
def aws_kms_master_key_provider(
        discovery=True,  # type: bool
        **kwargs  # type: List[Union[Text, str]]
):
    # type: (...) -> Union[MRKAwareDiscoveryAwsKmsMasterKeyProvider, MRKAwareStrictAwsKmsMasterKeyProvider]
    """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 MRKAwareDiscoveryAwsKmsMasterKeyProvider
    :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 MRKAwareDiscoveryAwsKmsMasterKeyProvider(**kwargs)
    return MRKAwareStrictAwsKmsMasterKeyProvider(**kwargs)
 def test_init_requires_unique_mrks(self):
     # //= compliance/framework/aws-kms/aws-kms-mrk-are-unique.txt#2.5
     # //= type=test
     # //# If any duplicate multi-region resource ids exist, this function MUST
     # //# yield an error that includes all identifiers with duplicate resource
     # //# ids not only the first duplicate found.
     key_ids = (
         "arn:aws:kms:eu-west-2:222222222222:key/mrk-aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
         "arn:aws:kms:us-east-1:222222222222:key/mrk-aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
         "mrk-aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
         "arn:aws:kms:eu-west-2:222222222222:key/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
     )
     expected_error_string = "Configured key ids must be unique. Found related MRKs: .*, .*, .*"
     with patch.object(self.mock_botocore_session,
                       "get_config_variable",
                       return_value="us-west-2"):
         with pytest.raises(ConfigMismatchError) as excinfo:
             MRKAwareStrictAwsKmsMasterKeyProvider(
                 botocore_session=self.mock_botocore_session,
                 key_ids=key_ids)
         excinfo.match(expected_error_string)
 def test_init_with_duplicate_non_mrk_key_ids(self):
     # //= compliance/framework/aws-kms/aws-kms-mrk-are-unique.txt#2.5
     # //= type=test
     # //# If the list does not contain any multi-Region keys (aws-kms-key-
     # //# arn.md#identifying-an-aws-kms-multi-region-key) this function MUST
     # //# exit successfully.
     key_ids = (
         "arn:aws:kms:eu-west-2:222222222222:key/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
         "arn:aws:kms:eu-west-2:222222222222:key/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
         "alias/myAlias",
         "alias/myAlias",
     )
     with patch.object(self.mock_botocore_session,
                       "get_config_variable",
                       return_value="us-west-2"):
         provider = MRKAwareStrictAwsKmsMasterKeyProvider(
             botocore_session=self.mock_botocore_session, key_ids=key_ids)
         assert len(provider.config.key_ids) == 4
         assert key_ids[0] in provider.config.key_ids
         assert key_ids[1] in provider.config.key_ids
         assert key_ids[2] in provider.config.key_ids
         assert key_ids[3] in provider.config.key_ids
    def test_init_with_key_ids(self):
        # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.6
        # //= type=test
        # //# All AWS KMS
        # //# key identifiers are be passed to Assert AWS KMS MRK are unique (aws-
        # //# kms-mrk-are-unique.md#Implementation) and the function MUST return
        # //# success.

        # //= compliance/framework/aws-kms/aws-kms-mrk-are-unique.txt#2.5
        # //= type=test
        # //# The caller MUST provide:

        # //= compliance/framework/aws-kms/aws-kms-mrk-are-unique.txt#2.5
        # //= type=test
        # //# If there are zero duplicate resource ids between the multi-region
        # //# keys, this function MUST exit successfully
        key_ids = (
            "arn:aws:kms:eu-west-2:222222222222:key/mrk-aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
            "arn:aws:kms:us-east-1:222222222222:key/mrk-bbbbbbbb-1111-2222-3333-bbbbbbbbbbbb",
        )
        provider = MRKAwareStrictAwsKmsMasterKeyProvider(key_ids=key_ids)
        assert len(provider.config.key_ids) == 2
        assert key_ids[0] in provider.config.key_ids
        assert key_ids[1] in provider.config.key_ids
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