|
| 1 | +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. |
| 2 | +# SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +"""Example showing use of AWS KMS CMP with a DynamoDB Global table and an AWS Multi-Region Key.""" |
| 5 | + |
| 6 | +import time |
| 7 | + |
| 8 | +import boto3 |
| 9 | + |
| 10 | +from dynamodb_encryption_sdk.encrypted.client import EncryptedClient |
| 11 | +from dynamodb_encryption_sdk.identifiers import CryptoAction |
| 12 | +from dynamodb_encryption_sdk.material_providers.aws_kms import AwsKmsCryptographicMaterialsProvider |
| 13 | +from dynamodb_encryption_sdk.structures import AttributeActions |
| 14 | + |
| 15 | +SECOND_REGION = "eu-west-1" |
| 16 | + |
| 17 | + |
| 18 | +def encrypt_item(table_name, cmk_mrk_arn_first_region, cmk_mrk_arn_second_region): |
| 19 | + """Demonstrate use of Multi-Region Keys with DynamoDB Encryption Client. |
| 20 | +
|
| 21 | + This example encrypts an item with a Multi-Region Key in one region and decrypts it in another region. It |
| 22 | + assumes that you have a Dynamo DB Global table in two regions, as well as a KMS |
| 23 | + Multi-Region Key replicated to these regions. |
| 24 | + """ |
| 25 | + index_key = {"partition_attribute": {"S": "is this"}, "sort_attribute": {"N": "55"}} |
| 26 | + plaintext_item = { |
| 27 | + "example": {"S": "data"}, |
| 28 | + "some numbers": {"N": "99"}, |
| 29 | + "and some binary": {"B": b"\x00\x01\x02"}, |
| 30 | + "leave me": {"S": "alone"}, # We want to ignore this attribute |
| 31 | + } |
| 32 | + # Collect all of the attributes that will be encrypted (used later). |
| 33 | + encrypted_attributes = set(plaintext_item.keys()) |
| 34 | + encrypted_attributes.remove("leave me") |
| 35 | + # Collect all of the attributes that will not be encrypted (used later). |
| 36 | + unencrypted_attributes = set(index_key.keys()) |
| 37 | + unencrypted_attributes.add("leave me") |
| 38 | + # Add the index pairs to the item. |
| 39 | + plaintext_item.update(index_key) |
| 40 | + |
| 41 | + # Create attribute actions that tells the encrypted client to encrypt all attributes except one. |
| 42 | + actions = AttributeActions( |
| 43 | + default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={"leave me": CryptoAction.DO_NOTHING} |
| 44 | + ) |
| 45 | + |
| 46 | + # Create a DDB client and KMS crypto materials provider in the first region using the specified AWS KMS key. |
| 47 | + split_arn = cmk_mrk_arn_first_region.split(":") |
| 48 | + encryption_region = split_arn[3] |
| 49 | + ddb_client = boto3.client("dynamodb", region_name=encryption_region) |
| 50 | + encryption_cmp = AwsKmsCryptographicMaterialsProvider(key_id=cmk_mrk_arn_first_region) |
| 51 | + # Use these objects to create an encrypted client. |
| 52 | + encryption_client = EncryptedClient(client=ddb_client, materials_provider=encryption_cmp, attribute_actions=actions) |
| 53 | + |
| 54 | + # Put the item to the table, using the encrypted client to transparently encrypt it. |
| 55 | + encryption_client.put_item(TableName=table_name, Item=plaintext_item) |
| 56 | + |
| 57 | + # Create a DDB client and KMS crypto materials provider in the second region |
| 58 | + split_arn = cmk_mrk_arn_second_region.split(":") |
| 59 | + decryption_region = split_arn[3] |
| 60 | + decryption_cmp = AwsKmsCryptographicMaterialsProvider(key_id=cmk_mrk_arn_second_region) |
| 61 | + ddb_client = boto3.client("dynamodb", region_name=decryption_region) |
| 62 | + # Use these objects to create an encrypted client. |
| 63 | + decryption_client = EncryptedClient(client=ddb_client, materials_provider=decryption_cmp, attribute_actions=actions) |
| 64 | + |
| 65 | + # DDB Global Table replication takes some time. Sleep for a moment to give the item a chance to replicate to the |
| 66 | + # second region |
| 67 | + time.sleep(1) |
| 68 | + |
| 69 | + # Get the item from the second region, transparently decrypting it. This allows you to avoid a cross-region KMS |
| 70 | + # call to the first region if your application is running in the second region |
| 71 | + decrypted_item = decryption_client.get_item(TableName=table_name, Key=index_key)["Item"] |
| 72 | + |
| 73 | + # Verify that the decryption successfully retrieved the original plaintext |
| 74 | + for name in encrypted_attributes: |
| 75 | + assert plaintext_item[name] == decrypted_item[name] |
| 76 | + |
| 77 | + # Clean up the item |
| 78 | + encryption_client.delete_item(TableName=table_name, Key=index_key) |
0 commit comments