Skip to content

Commit 74cfafc

Browse files
MultiPutGetExample
1 parent 7175697 commit 74cfafc

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

Examples/runtimes/go/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ func main() {
1212
keyring.RawAesExample(utils.DdbTableName(), utils.KeyNamespace(), utils.KeyName(), utils.GenerateAes256KeyBytes())
1313
itemencryptor.ItemEncryptDecryptExample(utils.KmsKeyID(), utils.DdbTableName())
1414
misc.GetEncryptedDataKeyDescriptionExample(utils.KmsKeyID(), utils.DdbTableName())
15+
misc.MultiPutGetExample(utils.KmsKeyID(), utils.DdbTableName())
1516
}
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package misc
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"reflect"
10+
11+
mpl "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygenerated"
12+
mpltypes "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygeneratedtypes"
13+
dbesdkdynamodbencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes"
14+
dbesdkstructuredencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes"
15+
"github.com/aws/aws-database-encryption-sdk-dynamodb/dbesdkmiddleware"
16+
"github.com/aws/aws-database-encryption-sdk-dynamodb/examples/utils"
17+
18+
"github.com/aws/aws-sdk-go-v2/aws"
19+
"github.com/aws/aws-sdk-go-v2/config"
20+
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
21+
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
22+
)
23+
24+
func MultiPutGetExample(kmsKeyID, ddbTableName string) {
25+
cfg, err := config.LoadDefaultConfig(context.TODO())
26+
// Initialize the mpl client
27+
matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{})
28+
utils.HandleError(err)
29+
// 1. Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data.
30+
// For this example, we will create a AWS KMS Keyring with the AWS KMS Key we want to use.
31+
// We will use the `CreateAwsKmsMultiKeyring` method to create this keyring,
32+
// as it will correctly handle both single region and Multi-Region KMS Keys.
33+
generatorKeyId := kmsKeyID
34+
awsKmsMultiKeyringInput := mpltypes.CreateAwsKmsMultiKeyringInput{
35+
Generator: &generatorKeyId,
36+
}
37+
keyring, err := matProv.CreateAwsKmsMultiKeyring(context.Background(), awsKmsMultiKeyringInput)
38+
utils.HandleError(err)
39+
40+
// 2. Configure which attributes are encrypted and/or signed when writing new items.
41+
// For each attribute that may exist on the items we plan to write to our DynamoDbTable,
42+
// we must explicitly configure how they should be treated during item encryption:
43+
// - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
44+
// - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
45+
// - DO_NOTHING: The attribute is not encrypted and not included in the signature
46+
attributeActions := map[string]dbesdkstructuredencryptiontypes.CryptoAction{
47+
"partition_key": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, // Partition key must be SIGN_ONLY
48+
"sort_key": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, // Sort key must be SIGN_ONLY
49+
"attribute1": dbesdkstructuredencryptiontypes.CryptoActionEncryptAndSign,
50+
"attribute2": dbesdkstructuredencryptiontypes.CryptoActionSignOnly,
51+
":attribute3": dbesdkstructuredencryptiontypes.CryptoActionDoNothing,
52+
}
53+
54+
// 3. Configure which attributes we expect to be included in the signature
55+
// when reading items. There are two options for configuring this:
56+
//
57+
// - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
58+
// When defining your DynamoDb schema and deciding on attribute names,
59+
// choose a distinguishing prefix (such as ":") for all attributes that
60+
// you do not want to include in the signature.
61+
// This has two main benefits:
62+
// - It is easier to reason about the security and authenticity of data within your item
63+
// when all unauthenticated data is easily distinguishable by their attribute name.
64+
// - If you need to add new unauthenticated attributes in the future,
65+
// you can easily make the corresponding update to your `attributeActionsOnEncrypt`
66+
// and immediately start writing to that new attribute, without
67+
// any other configuration update needed.
68+
// Once you configure this field, it is not safe to update it.
69+
//
70+
// - Configure `allowedUnsignedAttributes`: You may also explicitly list
71+
// a set of attributes that should be considered unauthenticated when encountered
72+
// on read. Be careful if you use this configuration. Do not remove an attribute
73+
// name from this configuration, even if you are no longer writing with that attribute,
74+
// as old items may still include this attribute, and our configuration needs to know
75+
// to continue to exclude this attribute from the signature scope.
76+
// If you add new attribute names to this field, you must first deploy the update to this
77+
// field to all readers in your host fleet before deploying the update to start writing
78+
// with that new attribute.
79+
//
80+
// For this example, we have designed our DynamoDb table such that any attribute name with
81+
// the ":" prefix should be considered unauthenticated.
82+
allowedUnsignedAttributePrefix := ":"
83+
84+
// 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
85+
partitionKey := "partition_key"
86+
sortKeyName := "sort_key"
87+
algorithmSuiteID := mpltypes.DBEAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeyEcdsaP384SymsigHmacSha384
88+
tableConfig := dbesdkdynamodbencryptiontypes.DynamoDbTableEncryptionConfig{
89+
LogicalTableName: ddbTableName,
90+
PartitionKeyName: partitionKey,
91+
SortKeyName: &sortKeyName,
92+
AttributeActionsOnEncrypt: attributeActions,
93+
Keyring: keyring,
94+
AllowedUnsignedAttributePrefix: &allowedUnsignedAttributePrefix,
95+
AlgorithmSuiteId: &algorithmSuiteID,
96+
}
97+
tableConfigsMap := make(map[string]dbesdkdynamodbencryptiontypes.DynamoDbTableEncryptionConfig)
98+
tableConfigsMap[ddbTableName] = tableConfig
99+
listOfTableConfigs := dbesdkdynamodbencryptiontypes.DynamoDbTablesEncryptionConfig{
100+
TableEncryptionConfigs: tableConfigsMap,
101+
}
102+
// 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs
103+
dbEsdkMiddleware, err := dbesdkmiddleware.NewDBEsdkMiddleware(listOfTableConfigs)
104+
utils.HandleError(err)
105+
ddb := dynamodb.NewFromConfig(cfg, dbEsdkMiddleware.CreateMiddleware())
106+
107+
// 6. Put an item into our table using the above client.
108+
// Before the item gets sent to DynamoDb, it will be encrypted
109+
// client-side, according to our configuration.
110+
item := map[string]types.AttributeValue{
111+
"partition_key": &types.AttributeValueMemberS{Value: "BasicPutGetExample"},
112+
"sort_key": &types.AttributeValueMemberN{Value: "0"},
113+
"attribute1": &types.AttributeValueMemberS{Value: "encrypt and sign me!"},
114+
"attribute2": &types.AttributeValueMemberS{Value: "sign me!"},
115+
":attribute3": &types.AttributeValueMemberS{Value: "ignore me!"},
116+
}
117+
putbatchWriteItemInput := &dynamodb.PutItemInput{
118+
TableName: aws.String(ddbTableName),
119+
Item: item,
120+
}
121+
_, err = ddb.PutItem(context.TODO(), putbatchWriteItemInput)
122+
utils.HandleError(err)
123+
124+
transactWriteItemsInput := &dynamodb.TransactWriteItemsInput{
125+
TransactItems: []types.TransactWriteItem{
126+
{
127+
Put: &types.Put{
128+
TableName: aws.String(ddbTableName),
129+
Item: item,
130+
},
131+
},
132+
},
133+
}
134+
_, err = ddb.TransactWriteItems(context.TODO(), transactWriteItemsInput)
135+
utils.HandleError(err)
136+
137+
// 7. Get the item back from our table using the same client.
138+
// The client will decrypt the item client-side, and return
139+
// back the original item.
140+
key := map[string]types.AttributeValue{
141+
"partition_key": &types.AttributeValueMemberS{Value: "BasicPutGetExample"},
142+
"sort_key": &types.AttributeValueMemberN{Value: "0"},
143+
}
144+
getInput := &dynamodb.GetItemInput{
145+
TableName: aws.String(ddbTableName),
146+
Key: key,
147+
// In this example we configure a strongly consistent read
148+
// because we perform a read immediately after a write (for demonstrative purposes).
149+
// By default, reads are only eventually consistent.
150+
// Read our docs to determine which read consistency to use for your application:
151+
// https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
152+
ConsistentRead: aws.Bool(true),
153+
}
154+
result, err := ddb.GetItem(context.TODO(), getInput)
155+
utils.HandleError(err)
156+
// Verify the decrypted item
157+
if !reflect.DeepEqual(item, result.Item) {
158+
panic("Decrypted item does not match original item")
159+
}
160+
161+
transactGetItemsInput := &dynamodb.TransactGetItemsInput{
162+
TransactItems: []types.TransactGetItem{
163+
{
164+
Get: &types.Get{
165+
Key: key,
166+
TableName: aws.String(ddbTableName),
167+
},
168+
},
169+
},
170+
}
171+
_, err = ddb.TransactGetItems(context.TODO(), transactGetItemsInput)
172+
utils.HandleError(err)
173+
fmt.Println("MultiPutGetExample successful")
174+
}

0 commit comments

Comments
 (0)