Skip to content

Commit b5705ee

Browse files
authored
chore: Add examples to examine contents of query error list (#1251)
* chore: Add examples to examine contents of query error list
1 parent 07bba8b commit b5705ee

File tree

6 files changed

+366
-6
lines changed

6 files changed

+366
-6
lines changed

DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DdbMiddlewareConfig.dfy

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ module DdbMiddlewareConfig {
110110
"";
111111
var sort :=
112112
if config.sortKeyName.Some? && config.sortKeyName.value in item then
113-
"\n" + config.sortKeyName.value + " = " + AttrToString(item[config.sortKeyName.value])
113+
"; " + config.sortKeyName.value + " = " + AttrToString(item[config.sortKeyName.value])
114114
else
115115
"";
116116

DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/QueryTransform.dfy

+5-5
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ module QueryTransformTest {
173173
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.CollectionOfErrors(
174174
[
175175
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptor(AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDb(AwsCryptographyDbEncryptionSdkDynamoDbTypes.Error.AwsCryptographyDbEncryptionSdkStructuredEncryption(AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.Error.StructuredEncryptionException(message := "No recipient tag matched.")))),
176-
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 1234\nsortKey = 01020304")
176+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 1234; sortKey = 01020304")
177177
],
178178
message := "Error(s) found decrypting Query results."
179179
);
@@ -192,8 +192,8 @@ module QueryTransformTest {
192192
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.CollectionOfErrors(
193193
[
194194
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptor(AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDb(AwsCryptographyDbEncryptionSdkDynamoDbTypes.Error.AwsCryptographyDbEncryptionSdkStructuredEncryption(AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.Error.StructuredEncryptionException(message := "No recipient tag matched.")))),
195-
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 1234\nsortKey = 01020304"),
196-
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 890\nsortKey = 030104")
195+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 1234; sortKey = 01020304"),
196+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 890; sortKey = 030104")
197197
],
198198
message := "Error(s) found decrypting Query results."
199199
);
@@ -210,8 +210,8 @@ module QueryTransformTest {
210210
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.CollectionOfErrors(
211211
[
212212
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptor(AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDb(AwsCryptographyDbEncryptionSdkDynamoDbTypes.Error.AwsCryptographyDbEncryptionSdkStructuredEncryption(AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.Error.StructuredEncryptionException(message := "No recipient tag matched.")))),
213-
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 1234\nsortKey = 01020304"),
214-
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 890\nsortKey = 030104")
213+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 1234; sortKey = 01020304"),
214+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 890; sortKey = 030104")
215215
],
216216
message := "Error(s) found decrypting Scan results."
217217
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package software.amazon.cryptography.examples;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
6+
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
7+
import software.amazon.awssdk.services.dynamodb.model.*;
8+
import software.amazon.awssdk.services.dynamodb.model.ScanRequest;
9+
import software.amazon.awssdk.services.dynamodb.model.ScanResponse;
10+
import software.amazon.cryptography.dbencryptionsdk.dynamodb.DynamoDbEncryptionInterceptor;
11+
import software.amazon.cryptography.dbencryptionsdk.dynamodb.model.DynamoDbTableEncryptionConfig;
12+
import software.amazon.cryptography.dbencryptionsdk.dynamodb.model.DynamoDbTablesEncryptionConfig;
13+
import software.amazon.cryptography.dbencryptionsdk.dynamodb.transforms.model.CollectionOfErrors;
14+
import software.amazon.cryptography.dbencryptionsdk.structuredencryption.model.CryptoAction;
15+
import software.amazon.cryptography.materialproviders.IKeyring;
16+
import software.amazon.cryptography.materialproviders.MaterialProviders;
17+
import software.amazon.cryptography.materialproviders.model.CreateAwsKmsMrkMultiKeyringInput;
18+
import software.amazon.cryptography.materialproviders.model.DBEAlgorithmSuiteId;
19+
import software.amazon.cryptography.materialproviders.model.MaterialProvidersConfig;
20+
21+
/*
22+
This example sets up DynamoDb Encryption for the AWS SDK client
23+
and uses the low level Scan operation to demonstrate
24+
retrieving the error messages from the returned CollectionOfErrors
25+
when some of the Scan results do not decrypt successfully.
26+
27+
Running this example requires access to the DDB Table whose name
28+
is provided in CLI arguments.
29+
This table must be configured with the following
30+
primary key configuration:
31+
- Partition key is named "partition_key" with type (S)
32+
- Sort key is named "sort_key" with type (N)
33+
*/
34+
public class ScanErrorExample {
35+
36+
public static void ScanError(String kmsKeyId, String ddbTableName) {
37+
// 1. Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data.
38+
// For this example, we will create a AWS KMS Keyring with the AWS KMS Key we want to use.
39+
// We will use the `CreateMrkMultiKeyring` method to create this keyring,
40+
// as it will correctly handle both single region and Multi-Region KMS Keys.
41+
final MaterialProviders matProv = MaterialProviders
42+
.builder()
43+
.MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
44+
.build();
45+
final CreateAwsKmsMrkMultiKeyringInput keyringInput =
46+
CreateAwsKmsMrkMultiKeyringInput.builder().generator(kmsKeyId).build();
47+
final IKeyring kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(
48+
keyringInput
49+
);
50+
51+
// 2. Configure which attributes are encrypted and/or signed when writing new items.
52+
// For each attribute that may exist on the items we plan to write to our DynamoDbTable,
53+
// we must explicitly configure how they should be treated during item encryption:
54+
// - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
55+
// - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
56+
// - DO_NOTHING: The attribute is not encrypted and not included in the signature
57+
final Map<String, CryptoAction> attributeActionsOnEncrypt = new HashMap<>();
58+
attributeActionsOnEncrypt.put("partition_key", CryptoAction.SIGN_ONLY); // Our partition attribute must be SIGN_ONLY
59+
attributeActionsOnEncrypt.put("sort_key", CryptoAction.SIGN_ONLY); // Our sort attribute must be SIGN_ONLY
60+
attributeActionsOnEncrypt.put("attribute1", CryptoAction.ENCRYPT_AND_SIGN);
61+
attributeActionsOnEncrypt.put("attribute2", CryptoAction.SIGN_ONLY);
62+
attributeActionsOnEncrypt.put(":attribute3", CryptoAction.DO_NOTHING);
63+
64+
// 3. Configure which attributes we expect to be included in the signature
65+
// when reading items. There are two options for configuring this:
66+
//
67+
// - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
68+
// When defining your DynamoDb schema and deciding on attribute names,
69+
// choose a distinguishing prefix (such as ":") for all attributes that
70+
// you do not want to include in the signature.
71+
// This has two main benefits:
72+
// - It is easier to reason about the security and authenticity of data within your item
73+
// when all unauthenticated data is easily distinguishable by their attribute name.
74+
// - If you need to add new unauthenticated attributes in the future,
75+
// you can easily make the corresponding update to your `attributeActionsOnEncrypt`
76+
// and immediately start writing to that new attribute, without
77+
// any other configuration update needed.
78+
// Once you configure this field, it is not safe to update it.
79+
//
80+
// - Configure `allowedUnsignedAttributes`: You may also explicitly list
81+
// a set of attributes that should be considered unauthenticated when encountered
82+
// on read. Be careful if you use this configuration. Do not remove an attribute
83+
// name from this configuration, even if you are no longer writing with that attribute,
84+
// as old items may still include this attribute, and our configuration needs to know
85+
// to continue to exclude this attribute from the signature scope.
86+
// If you add new attribute names to this field, you must first deploy the update to this
87+
// field to all readers in your host fleet before deploying the update to start writing
88+
// with that new attribute.
89+
//
90+
// For this example, we have designed our DynamoDb table such that any attribute name with
91+
// the ":" prefix should be considered unauthenticated.
92+
final String unsignAttrPrefix = ":";
93+
94+
// 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
95+
final Map<String, DynamoDbTableEncryptionConfig> tableConfigs =
96+
new HashMap<>();
97+
final DynamoDbTableEncryptionConfig config = DynamoDbTableEncryptionConfig
98+
.builder()
99+
.logicalTableName(ddbTableName)
100+
.partitionKeyName("partition_key")
101+
.sortKeyName("sort_key")
102+
.attributeActionsOnEncrypt(attributeActionsOnEncrypt)
103+
.keyring(kmsKeyring)
104+
.allowedUnsignedAttributePrefix(unsignAttrPrefix)
105+
// Specifying an algorithm suite is not required,
106+
// but is done here to demonstrate how to do so.
107+
// We suggest using the
108+
// `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite,
109+
// which includes AES-GCM with key derivation, signing, and key commitment.
110+
// This is also the default algorithm suite if one is not specified in this config.
111+
// For more information on supported algorithm suites, see:
112+
// https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html
113+
.algorithmSuiteId(
114+
DBEAlgorithmSuiteId.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384
115+
)
116+
.build();
117+
tableConfigs.put(ddbTableName, config);
118+
119+
// 5. Create the DynamoDb Encryption Interceptor
120+
DynamoDbEncryptionInterceptor encryptionInterceptor =
121+
DynamoDbEncryptionInterceptor
122+
.builder()
123+
.config(
124+
DynamoDbTablesEncryptionConfig
125+
.builder()
126+
.tableEncryptionConfigs(tableConfigs)
127+
.build()
128+
)
129+
.build();
130+
131+
// 6. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above
132+
final DynamoDbClient ddb = DynamoDbClient
133+
.builder()
134+
.overrideConfiguration(
135+
ClientOverrideConfiguration
136+
.builder()
137+
.addExecutionInterceptor(encryptionInterceptor)
138+
.build()
139+
)
140+
.build();
141+
142+
// 7. Perform a Scan for which some records will not decrypt
143+
Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
144+
expressionAttributeValues.put(
145+
":prefix",
146+
AttributeValue.builder().s("Broken").build()
147+
);
148+
149+
ScanRequest scanRequest = ScanRequest
150+
.builder()
151+
.tableName(ddbTableName)
152+
.filterExpression("begins_with(partition_key, :prefix)")
153+
.expressionAttributeValues(expressionAttributeValues)
154+
.build();
155+
156+
try {
157+
final ScanResponse scanResponse = ddb.scan(scanRequest);
158+
assert false;
159+
} catch (Exception e) {
160+
print_exception(e, "");
161+
}
162+
}
163+
164+
public static void print_exception(Exception e, String indent) {
165+
System.err.println(indent + e.getMessage());
166+
if (e.getCause() instanceof CollectionOfErrors) {
167+
System.err.println(indent + e.getCause().getMessage());
168+
for (RuntimeException err : ((CollectionOfErrors) e.getCause()).list()) {
169+
print_exception(err, indent + " ");
170+
}
171+
} else if (
172+
e instanceof
173+
software.amazon.cryptography.materialproviders.model.CollectionOfErrors
174+
) {
175+
for (RuntimeException err : ((software.amazon.cryptography.materialproviders.model.CollectionOfErrors) e).list()) {
176+
print_exception(err, indent + " ");
177+
}
178+
}
179+
}
180+
181+
public static void main(final String[] args) {
182+
if (args.length < 2) {
183+
throw new IllegalArgumentException(
184+
"To run this example, include the kmsKeyId as args[0] and ddbTableName as args[1]"
185+
);
186+
}
187+
final String kmsKeyId = args[0];
188+
final String ddbTableName = args[1];
189+
ScanError(kmsKeyId, ddbTableName);
190+
}
191+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package software.amazon.cryptography.examples;
2+
3+
import org.testng.annotations.Test;
4+
5+
public class TestScanErrorExample {
6+
7+
@Test
8+
public void ScanError() {
9+
ScanErrorExample.ScanError(
10+
TestUtils.TEST_KMS_KEY_ID,
11+
TestUtils.TEST_DDB_TABLE_NAME
12+
);
13+
}
14+
}

Examples/runtimes/net/src/Examples.cs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ static async Task Main()
1212
ItemEncryptDecryptExample.PutItemGetItem();
1313

1414
await BasicPutGetExample.PutItemGetItem();
15+
await ScanErrorExample.ScanError();
1516
await GetEncryptedDataKeyDescriptionExample.GetEncryptedDataKeyDescription();
1617
await MultiPutGetExample.MultiPutGet();
1718
await ClientSupplierExample.ClientSupplierPutItemGetItem();

0 commit comments

Comments
 (0)