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)
Esempio n. 4
0
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
Esempio n. 6
0
            {
                "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