def test_add_master_keys_mrk_sdk_default(self): """Check that an MRK-aware provider without an explicit discovery_region uses its default region when creating new keys if the requested keys are MRKs.""" grant_tokens = (sentinel.grant_token2, sentinel.grant_token2) original_arn = arn_from_str( "arn:aws:kms:eu-west-2:222222222222:key/mrk-aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb" ) with patch.object(self.mock_botocore_session, "get_config_variable", return_value="us-west-2") as mock_get_config: provider = MRKAwareDiscoveryAwsKmsMasterKeyProvider( botocore_session=self.mock_botocore_session, grant_tokens=grant_tokens) mock_get_config.assert_called_once_with("region") master_key = provider._new_master_key(original_arn.to_string()) # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.7 # //= type=test # //# Otherwise if the mode is discovery then # //# the AWS Region MUST be the discovery MRK region. assert master_key.__class__ == MRKAwareKMSMasterKey self.mock_boto3_session.assert_called_with(botocore_session=ANY) self.mock_boto3_session_instance.client.assert_called_with( "kms", region_name=provider.default_region, config=provider._user_agent_adding_config, ) assert provider.default_region in master_key._key_id assert original_arn.region not in master_key._key_id assert master_key.config.grant_tokens is grant_tokens
def test_add_master_keys_mrk_with_discovery_region(self): """Check that an MRK-aware provider with an explicit discovery_region uses its configured region when creating new keys if the requested keys are MRKs.""" grant_tokens = (sentinel.grant_token2, sentinel.grant_token2) original_arn = arn_from_str( "arn:aws:kms:eu-west-2:222222222222:key/mrk-aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb" ) configured_region = "us-east-1" provider = MRKAwareDiscoveryAwsKmsMasterKeyProvider( discovery_region=configured_region, grant_tokens=grant_tokens) master_key = provider._new_master_key(original_arn.to_string()) # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.7 # //= type=test # //# In discovery 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 self.mock_boto3_session.assert_called_with(botocore_session=ANY) self.mock_boto3_session_instance.client.assert_called_with( "kms", region_name=configured_region, config=provider._user_agent_adding_config, ) assert configured_region in master_key._key_id assert original_arn.region not in master_key._key_id assert master_key.config.grant_tokens is grant_tokens
def test_add_master_keys_invalid_arn(self, non_arn): """Check that the provider throws an error when creating a new key with an invalid arn.""" # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.7 # //= type=test # //# In discovery mode, the requested # //# AWS KMS key identifier MUST be a well formed AWS KMS ARN. provider = MRKAwareDiscoveryAwsKmsMasterKeyProvider( discovery_region="us-east-1") with pytest.raises(MalformedArnError): provider._new_master_key(non_arn)
def test_init_sdk_default_not_found(self): """Check that an MRK-aware provider without an explicit discovery_region fails to initialize if it cannot find a default region for the AWS SDK.""" # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.6 # //= type=test # //# If an AWS SDK Default Region can not be obtained # //# initialization MUST fail. with pytest.raises(ConfigMismatchError) as excinfo: MRKAwareDiscoveryAwsKmsMasterKeyProvider( botocore_session=self.botocore_no_region_session) excinfo.match("Failed to determine default discovery region")
def test_add_master_keys_srk(self): """Check that the MRK-aware provider uses the original key region when creating new keys if the requested keys are SRKs.""" original_arn = "arn:aws:kms:eu-west-2:222222222222:key/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb" provider = MRKAwareDiscoveryAwsKmsMasterKeyProvider( discovery_region="us-east-1") master_key = provider._new_master_key(original_arn) # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.7 # //= type=test # //# Otherwise if the requested AWS KMS key # //# identifier is identified as a multi-Region key (aws-kms-key- # //# arn.md#identifying-an-aws-kms-multi-region-key), then AWS Region MUST # //# be the region from the AWS KMS key ARN stored in the provider info # //# from the encrypted data key. assert master_key.__class__ == MRKAwareKMSMasterKey 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._key_id == original_arn
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 _kms_mrk_aware_discovery_master_key_from_spec(self, _keys): # type: (KeysManifest) -> KMSMasterKey """Build an AWS KMS master key using this specification. :param KeySpec key_spec: Key specification to use with this master key :return: AWS KMS master key based on this specification :rtype: KMSMasterKey :raises TypeError: if this is not an AWS KMS master key specification """ if not self.type_name == "aws-kms-mrk-aware-discovery": raise TypeError( "This is not an AWS KMS MRK-aware discovery master key") return MRKAwareDiscoveryAwsKmsMasterKeyProvider( discovery_region=self.default_mrk_region, discovery_filter=self.discovery_filter)
def test_init_implicit_discovery_region(self): """Check that an MRK-aware provider without an explicit discovery_region uses the default SDK region.""" with patch.object( self.mock_botocore_session, "get_config_variable", return_value=sentinel.default_region) as mock_get_config: test = MRKAwareDiscoveryAwsKmsMasterKeyProvider( botocore_session=self.mock_botocore_session) mock_get_config.assert_called_once_with("region") # //= compliance/framework/aws-kms/aws-kms-mrk-aware-master-key-provider.txt#2.6 # //= type=test # //# In discovery mode # //# if a default MRK Region is not configured the AWS SDK Default Region # //# MUST be used. assert test.default_region is sentinel.default_region assert test.config.discovery_region is sentinel.default_region
"""Helper utilities for interacting with AWS KMS.""" try: from aws_encryption_sdk.identifiers import AlgorithmSuite except ImportError: from aws_encryption_sdk.identifiers import Algorithm as AlgorithmSuite 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") def arn_from_key_id(key_id): # type: (str) -> str """Determine the KMS CMK Arn for the identified key ID. To avoid needing additional KMS permissions, we just call ``generate_data_key`` using a master key identified by ``key_id``. :param str key_id: Original key ID :returns: Full Arn for KMS CMK that key ID identifies :rtype: str """ provider = StrictAwsKmsMasterKeyProvider(key_ids=[key_id]) encrypted_data_key = provider.master_key(
def test_init_explicit_discovery_region(self): provider = MRKAwareDiscoveryAwsKmsMasterKeyProvider( discovery_region="us-east-1") assert provider.config.discovery_region == "us-east-1"
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