Skip to content

Commit 3641f19

Browse files
author
Lucas McDonald
committed
m
1 parent 64869a8 commit 3641f19

File tree

4 files changed

+193
-27
lines changed

4 files changed

+193
-27
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
Example for using the EncryptedPaginator provided by EncryptedClient.
5+
6+
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/paginator/Query.html
7+
8+
Running this example requires access to the DDB Table whose name
9+
is provided in the function arguments.
10+
This table must be configured with the following primary key configuration:
11+
- Partition key is named "partition_key" with type (S)
12+
- Sort key is named "sort_key" with type (N)
13+
"""
14+
15+
import boto3
16+
from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders
17+
from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig
18+
from aws_cryptographic_material_providers.mpl.models import (
19+
CreateAwsKmsMrkMultiKeyringInput,
20+
DBEAlgorithmSuiteId,
21+
)
22+
from aws_cryptographic_material_providers.mpl.references import IKeyring
23+
from aws_dbesdk_dynamodb.encrypted.client import EncryptedClient
24+
from aws_dbesdk_dynamodb.structures.dynamodb import (
25+
DynamoDbTableEncryptionConfig,
26+
DynamoDbTablesEncryptionConfig,
27+
)
28+
from aws_dbesdk_dynamodb.structures.structured_encryption import (
29+
CryptoAction,
30+
)
31+
32+
33+
def encrypted_paginator_example(
34+
kms_key_id: str,
35+
dynamodb_table_name: str,
36+
):
37+
"""Use an EncryptedPaginator to paginate through items in a table."""
38+
# 1. Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data.
39+
# For this example, we will create a AWS KMS Keyring with the AWS KMS Key we want to use.
40+
# We will use the `CreateMrkMultiKeyring` method to create this keyring,
41+
# as it will correctly handle both single region and Multi-Region KMS Keys.
42+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(config=MaterialProvidersConfig())
43+
kms_mrk_multi_keyring_input: CreateAwsKmsMrkMultiKeyringInput = CreateAwsKmsMrkMultiKeyringInput(
44+
generator=kms_key_id,
45+
)
46+
kms_mrk_multi_keyring: IKeyring = mat_prov.create_aws_kms_mrk_multi_keyring(input=kms_mrk_multi_keyring_input)
47+
48+
# 2. Configure which attributes are encrypted and/or signed when writing new items.
49+
# For each attribute that may exist on the items we plan to write to our DynamoDbTable,
50+
# we must explicitly configure how they should be treated during item encryption:
51+
# - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
52+
# - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
53+
# - DO_NOTHING: The attribute is not encrypted and not included in the signature
54+
attribute_actions_on_encrypt = {
55+
"partition_key": CryptoAction.SIGN_ONLY,
56+
"sort_key": CryptoAction.SIGN_ONLY,
57+
"attribute1": CryptoAction.ENCRYPT_AND_SIGN,
58+
"attribute2": CryptoAction.SIGN_ONLY,
59+
":attribute3": CryptoAction.DO_NOTHING,
60+
}
61+
62+
# 3. Configure which attributes we expect to be included in the signature
63+
# when reading items. There are two options for configuring this:
64+
#
65+
# - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
66+
# When defining your DynamoDb schema and deciding on attribute names,
67+
# choose a distinguishing prefix (such as ":") for all attributes that
68+
# you do not want to include in the signature.
69+
# This has two main benefits:
70+
# - It is easier to reason about the security and authenticity of data within your item
71+
# when all unauthenticated data is easily distinguishable by their attribute name.
72+
# - If you need to add new unauthenticated attributes in the future,
73+
# you can easily make the corresponding update to your `attributeActionsOnEncrypt`
74+
# and immediately start writing to that new attribute, without
75+
# any other configuration update needed.
76+
# Once you configure this field, it is not safe to update it.
77+
#
78+
# - Configure `allowedUnsignedAttributes`: You may also explicitly list
79+
# a set of attributes that should be considered unauthenticated when encountered
80+
# on read. Be careful if you use this configuration. Do not remove an attribute
81+
# name from this configuration, even if you are no longer writing with that attribute,
82+
# as old items may still include this attribute, and our configuration needs to know
83+
# to continue to exclude this attribute from the signature scope.
84+
# If you add new attribute names to this field, you must first deploy the update to this
85+
# field to all readers in your host fleet before deploying the update to start writing
86+
# with that new attribute.
87+
#
88+
# For this example, we have designed our DynamoDb table such that any attribute name with
89+
# the ":" prefix should be considered unauthenticated.
90+
unsignAttrPrefix: str = ":"
91+
92+
# 4. Create the DynamoDb Encryption configuration for the tables we will be writing to.
93+
# For each table, we create a DynamoDbTableEncryptionConfig and add it to a dictionary.
94+
# This dictionary is then added to a DynamoDbTablesEncryptionConfig, which is used to create the
95+
# EncryptedResource.
96+
table_configs = {}
97+
table_config = DynamoDbTableEncryptionConfig(
98+
logical_table_name=dynamodb_table_name,
99+
partition_key_name="partition_key",
100+
sort_key_name="sort_key",
101+
attribute_actions_on_encrypt=attribute_actions_on_encrypt,
102+
keyring=kms_mrk_multi_keyring,
103+
allowed_unsigned_attribute_prefix=unsignAttrPrefix,
104+
# Specifying an algorithm suite is not required,
105+
# but is done here to demonstrate how to do so.
106+
# We suggest using the
107+
# `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite,
108+
# which includes AES-GCM with key derivation, signing, and key commitment.
109+
# This is also the default algorithm suite if one is not specified in this config.
110+
# For more information on supported algorithm suites, see:
111+
# https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html
112+
algorithm_suite_id=DBEAlgorithmSuiteId.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384,
113+
)
114+
table_configs[dynamodb_table_name] = table_config
115+
tables_config = DynamoDbTablesEncryptionConfig(table_encryption_configs=table_configs)
116+
117+
# 5. Create the EncryptedClient
118+
encrypted_client = EncryptedClient(
119+
client=boto3.client("dynamodb"),
120+
encryption_config=tables_config,
121+
)
122+
123+
# 6. Put an item into the table. The EncryptedPaginator will paginate through the items in the table
124+
# to find this item.
125+
item = {
126+
"partition_key": {"S": "PythonEncryptedPaginatorExample"},
127+
"sort_key": {"N": "0"},
128+
"attribute1": {"S": "encrypt and sign me!"},
129+
"attribute2": {"S": "sign me!"},
130+
":attribute3": {"S": "ignore me!"},
131+
}
132+
133+
encrypted_client.put_item(
134+
TableName=dynamodb_table_name,
135+
Item=item,
136+
)
137+
138+
# 7. Create the EncryptedPaginator.
139+
# We will use the encrypted `query` paginator, but an encrypted `scan` paginator is also available.
140+
encrypted_paginator = encrypted_client.get_paginator("query")
141+
142+
# 8. Use the EncryptedPaginator to paginate through the items in the table.
143+
# The `paginate` method returns a generator that yields pages as dictionaries.
144+
# The EncryptedPaginator will transparently decrypt the items in each page as they are returned.
145+
# Once the generator is exhausted, the loop will exit.
146+
items = []
147+
for page in encrypted_paginator.paginate(
148+
TableName=dynamodb_table_name,
149+
KeyConditionExpression="partition_key = :partition_key",
150+
ExpressionAttributeValues={":partition_key": {"S": "PythonEncryptedPaginatorExample"}},
151+
):
152+
for item in page["Items"]:
153+
items.append(item)
154+
155+
# 9. Assert the items are as expected.
156+
assert len(items) == 1
157+
assert items[0]["attribute1"]["S"] == "encrypt and sign me!"
158+
assert items[0]["attribute2"]["S"] == "sign me!"
159+
assert items[0][":attribute3"]["S"] == "ignore me!"

Examples/runtimes/python/DynamoDBEncryption/src/encrypted_resource/batch_read_write_example.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
CryptoAction,
2929
)
3030

31+
3132
def encrypted_resource_batch_read_write_example(
3233
kms_key_id: str,
3334
dynamodb_table_name: str,
@@ -124,22 +125,20 @@ def encrypted_resource_batch_read_write_example(
124125
"sort_key": 0,
125126
"attribute1": "encrypt and sign me!",
126127
"attribute2": "sign me!",
127-
":attribute3": "ignore me!",
128+
":attribute3": "ignore me!",
128129
},
129130
{
130131
"partition_key": "PythonEncryptedResourceBatchReadWriteExample2",
131132
"sort_key": 0,
132133
"attribute1": "encrypt and sign me!",
133134
"attribute2": "sign me!",
134-
":attribute3": "ignore me!",
135+
":attribute3": "ignore me!",
135136
},
136137
]
137-
138+
138139
batch_write_items_put_request = {
139140
"RequestItems": {
140-
dynamodb_table_name: [
141-
{"PutRequest": {"Item": item}} for item in items
142-
],
141+
dynamodb_table_name: [{"PutRequest": {"Item": item}} for item in items],
143142
},
144143
}
145144

@@ -153,10 +152,9 @@ def encrypted_resource_batch_read_write_example(
153152
# returns them to the caller, they will be decrypted client-side according to our configuration.
154153
batch_get_items_request = {
155154
"RequestItems": {
156-
dynamodb_table_name:
157-
{
158-
"Keys": [{"partition_key": item["partition_key"], "sort_key": item["sort_key"]} for item in items],
159-
}
155+
dynamodb_table_name: {
156+
"Keys": [{"partition_key": item["partition_key"], "sort_key": item["sort_key"]} for item in items],
157+
}
160158
},
161159
}
162160

@@ -173,7 +171,8 @@ def encrypted_resource_batch_read_write_example(
173171
batch_write_items_delete_request = {
174172
"RequestItems": {
175173
dynamodb_table_name: [
176-
{"DeleteRequest": {"Key": {"partition_key": item["partition_key"], "sort_key": item["sort_key"]}}} for item in items
174+
{"DeleteRequest": {"Key": {"partition_key": item["partition_key"], "sort_key": item["sort_key"]}}}
175+
for item in items
177176
],
178177
},
179178
}

Examples/runtimes/python/DynamoDBEncryption/src/encrypted_resource/encrypted_tables_collection_manager_example.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
3-
"""Example for using the EncryptedTablesCollectionManager provided by EncryptedResource
3+
"""
4+
Example for using the EncryptedTablesCollectionManager provided by EncryptedResource.
45
56
Running this example requires access to the DDB Tables whose names
67
are provided in the function arguments.
@@ -19,7 +20,6 @@
1920
from aws_cryptographic_material_providers.mpl.references import IKeyring
2021
from aws_dbesdk_dynamodb.encrypted.resource import (
2122
EncryptedResource,
22-
EncryptedTablesCollectionManager,
2323
)
2424
from aws_dbesdk_dynamodb.structures.dynamodb import (
2525
DynamoDbTableEncryptionConfig,
@@ -29,6 +29,7 @@
2929
CryptoAction,
3030
)
3131

32+
3233
def encrypted_tables_collection_manager_example(
3334
kms_key_id: str,
3435
dynamodb_table_names: list[str],
@@ -90,7 +91,8 @@ def encrypted_tables_collection_manager_example(
9091

9192
# 4. Create the DynamoDb Encryption configuration for the tables we will be writing to.
9293
# For each table, we create a DynamoDbTableEncryptionConfig and add it to a dictionary.
93-
# This dictionary is then added to a DynamoDbTablesEncryptionConfig, which is used to create the EncryptedResource.
94+
# This dictionary is then added to a DynamoDbTablesEncryptionConfig, which is used to create the
95+
# EncryptedResource.
9496
table_configs = {}
9597
for dynamodb_table_name in dynamodb_table_names:
9698
table_config = DynamoDbTableEncryptionConfig(
@@ -137,14 +139,16 @@ def encrypted_tables_collection_manager_example(
137139
# If you do not, you will write to all tables in the collection.
138140
# This may include tables with incompatible schemas, or tables that you do not have permission to write to.
139141
if encrypted_table.table_name in dynamodb_table_names:
140-
encrypted_table.put_item(Item={
141-
"partition_key": "PythonEncryptedTablesCollectionManagerExample",
142-
"sort_key": 0,
143-
"attribute1": "encrypt and sign me!",
144-
"attribute2": "sign me!",
145-
":attribute3": "ignore me!",
146-
})
147-
142+
encrypted_table.put_item(
143+
Item={
144+
"partition_key": "PythonEncryptedTablesCollectionManagerExample",
145+
"sort_key": 0,
146+
"attribute1": "encrypt and sign me!",
147+
"attribute2": "sign me!",
148+
":attribute3": "ignore me!",
149+
}
150+
)
151+
148152
# 9. Read the items back from the table.
149153
# After the items are retrieved from DynamoDb, but before the EncryptedResource
150154
# returns them to the caller, they will be decrypted client-side according to our configuration.
@@ -154,10 +158,12 @@ def encrypted_tables_collection_manager_example(
154158
# If you do not, you will read from all tables in the collection.
155159
# This may include tables with incompatible schemas, or tables that you do not have permission to read from.
156160
if encrypted_table.table_name in dynamodb_table_names:
157-
get_item_response = encrypted_table.get_item(Key={
158-
"partition_key": "PythonEncryptedTablesCollectionManagerExample",
159-
"sort_key": 0,
160-
})
161+
get_item_response = encrypted_table.get_item(
162+
Key={
163+
"partition_key": "PythonEncryptedTablesCollectionManagerExample",
164+
"sort_key": 0,
165+
}
166+
)
161167

162168
item = get_item_response["Item"]
163169
items.append(item)

Examples/runtimes/python/DynamoDBEncryption/test/encrypted_resource/test_encrypted_tables_collection_manager_example.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"""Test suite for the EncryptedResource batch read/write example."""
44
import pytest
55

6-
from ...src.encrypted_resource.encrypted_tables_collection_manager_example import encrypted_tables_collection_manager_example
6+
from ...src.encrypted_resource.encrypted_tables_collection_manager_example import (
7+
encrypted_tables_collection_manager_example,
8+
)
79

810
pytestmark = [pytest.mark.examples]
911

0 commit comments

Comments
 (0)