Skip to content

Commit 49c9483

Browse files
item encryptor
1 parent a0a72cf commit 49c9483

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package itemencryptor
5+
6+
import (
7+
"context"
8+
"reflect"
9+
10+
mpl "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygenerated"
11+
mpltypes "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygeneratedtypes"
12+
itemencryptor "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkdynamodbitemencryptorsmithygenerated"
13+
dbesdkitemencryptortypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkdynamodbitemencryptorsmithygeneratedtypes"
14+
dbesdkstructuredencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes"
15+
"github.com/aws/aws-database-encryption-sdk-dynamodb/examples/utils"
16+
"github.com/aws/aws-sdk-go-v2/config"
17+
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
18+
"github.com/aws/aws-sdk-go-v2/service/kms"
19+
)
20+
21+
/*
22+
This example sets up a DynamoDb Item Encryptor and uses
23+
the EncryptItem and DecryptItem APIs to directly encrypt and
24+
decrypt an existing DynamoDb item.
25+
You should use the DynamoDb Item Encryptor
26+
if you already have a DynamoDb Item to encrypt or decrypt,
27+
and do not need to make a Put or Get call to DynamoDb.
28+
For example, if you are using DynamoDb Streams,
29+
you may already be working with an encrypted item obtained from
30+
DynamoDb, and want to directly decrypt the item.
31+
32+
Running this example requires access to the DDB Table whose name
33+
is provided in CLI arguments.
34+
This table must be configured with the following
35+
primary key configuration:
36+
- Partition key is named "partition_key" with type (S)
37+
- Sort key is named "sort_key" with type (S)
38+
*/
39+
40+
func Itemencryptdecrypt(kmsKeyID, ddbTableName string) {
41+
// 1. Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data.
42+
// For this example, we will create a AWS KMS Keyring with the AWS KMS Key we want to use.
43+
// We will use the `CreateMrkMultiKeyring` method to create this keyring,
44+
// as it will correctly handle both single region and Multi-Region KMS Keys.
45+
46+
cfg, err := config.LoadDefaultConfig(context.TODO())
47+
utils.HandleError(err)
48+
// Create KMS client
49+
kmsClient := kms.NewFromConfig(cfg)
50+
// Initialize the mpl client
51+
matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{})
52+
utils.HandleError(err)
53+
// Create the Aws Kms Keyring
54+
awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{
55+
KmsClient: kmsClient,
56+
KmsKeyId: kmsKeyID,
57+
}
58+
keyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput)
59+
utils.HandleError(err)
60+
61+
// 2. Configure which attributes are encrypted and/or signed when writing new items.
62+
// For each attribute that may exist on the items we plan to write to our DynamoDbTable,
63+
// we must explicitly configure how they should be treated during item encryption:
64+
// - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
65+
// - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
66+
// - DO_NOTHING: The attribute is not encrypted and not included in the signature
67+
attributeActions := map[string]dbesdkstructuredencryptiontypes.CryptoAction{
68+
"partition_key": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, // Partition key must be SIGN_ONLY
69+
"sort_key": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, // Sort key must be SIGN_ONLY
70+
"attribute1": dbesdkstructuredencryptiontypes.CryptoActionEncryptAndSign,
71+
"attribute2": dbesdkstructuredencryptiontypes.CryptoActionSignOnly,
72+
":attribute3": dbesdkstructuredencryptiontypes.CryptoActionDoNothing,
73+
}
74+
75+
// 3. Configure which attributes we expect to be included in the signature
76+
// when reading items. There are two options for configuring this:
77+
//
78+
// - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
79+
// When defining your DynamoDb schema and deciding on attribute names,
80+
// choose a distinguishing prefix (such as ":") for all attributes that
81+
// you do not want to include in the signature.
82+
// This has two main benefits:
83+
// - It is easier to reason about the security and authenticity of data within your item
84+
// when all unauthenticated data is easily distinguishable by their attribute name.
85+
// - If you need to add new unauthenticated attributes in the future,
86+
// you can easily make the corresponding update to your `attributeActionsOnEncrypt`
87+
// and immediately start writing to that new attribute, without
88+
// any other configuration update needed.
89+
// Once you configure this field, it is not safe to update it.
90+
//
91+
// - Configure `allowedUnsignedAttributes`: You may also explicitly list
92+
// a set of attributes that should be considered unauthenticated when encountered
93+
// on read. Be careful if you use this configuration. Do not remove an attribute
94+
// name from this configuration, even if you are no longer writing with that attribute,
95+
// as old items may still include this attribute, and our configuration needs to know
96+
// to continue to exclude this attribute from the signature scope.
97+
// If you add new attribute names to this field, you must first deploy the update to this
98+
// field to all readers in your host fleet before deploying the update to start writing
99+
// with that new attribute.
100+
//
101+
// For this example, we have designed our DynamoDb table such that any attribute name with
102+
// the ":" prefix should be considered unauthenticated.
103+
allowedUnsignedAttributePrefix := ":"
104+
105+
// 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
106+
partitionKey := "partition_key"
107+
sortKeyName := "sort_key"
108+
algorithmSuiteID := mpltypes.DBEAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeyEcdsaP384SymsigHmacSha384
109+
itemEncryptorConfig := dbesdkitemencryptortypes.DynamoDbItemEncryptorConfig{
110+
LogicalTableName: ddbTableName,
111+
PartitionKeyName: partitionKey,
112+
SortKeyName: &sortKeyName,
113+
AttributeActionsOnEncrypt: attributeActions,
114+
Keyring: keyring,
115+
AllowedUnsignedAttributePrefix: &allowedUnsignedAttributePrefix,
116+
// Specifying an algorithm suite is not required,
117+
// but is done here to demonstrate how to do so.
118+
// We suggest using the
119+
// `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite,
120+
// which includes AES-GCM with key derivation, signing, and key commitment.
121+
// This is also the default algorithm suite if one is not specified in this config.
122+
// For more information on supported algorithm suites, see:
123+
// https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html
124+
AlgorithmSuiteId: &algorithmSuiteID,
125+
}
126+
127+
// 5. Create the DynamoDb Item Encryptor
128+
itemEncryptorClient, err := itemencryptor.NewClient(itemEncryptorConfig)
129+
utils.HandleError(err)
130+
131+
// 6. Directly encrypt a DynamoDb item using the DynamoDb Item Encryptor
132+
item := map[string]types.AttributeValue{
133+
"partition_key": &types.AttributeValueMemberS{Value: "ItemEncryptDecryptExample"},
134+
"sort_key": &types.AttributeValueMemberS{Value: "0"},
135+
"attribute1": &types.AttributeValueMemberS{Value: "encrypt and sign me!"},
136+
"attribute2": &types.AttributeValueMemberS{Value: "sign me!"},
137+
":attribute3": &types.AttributeValueMemberS{Value: "ignore me!"},
138+
}
139+
encryptItemInput := &dbesdkitemencryptortypes.EncryptItemInput{
140+
PlaintextItem: item,
141+
}
142+
encryptItemOutput, err := itemEncryptorClient.EncryptItem(context.Background(), *encryptItemInput)
143+
utils.HandleError(err)
144+
145+
// Check if partition_key is "ItemEncryptDecryptExample"
146+
if partitionKeyAttr, ok := encryptItemOutput.EncryptedItem["partition_key"].(*types.AttributeValueMemberS); ok {
147+
if partitionKeyAttr.Value != "ItemEncryptDecryptExample" {
148+
panic("Partition key is not 'ItemEncryptDecryptExample'")
149+
}
150+
} else {
151+
// Handle the case where partition_key is not a string attribute
152+
panic("Partition key is not a string attribute or doesn't exist")
153+
}
154+
155+
decryptItemInput := &dbesdkitemencryptortypes.DecryptItemInput{
156+
EncryptedItem: encryptItemOutput.EncryptedItem,
157+
}
158+
159+
decryptedItem, err := itemEncryptorClient.DecryptItem(context.Background(), *decryptItemInput)
160+
utils.HandleError(err)
161+
162+
if !reflect.DeepEqual(item, decryptedItem.PlaintextItem) {
163+
panic("Decrypted item does not match original item")
164+
}
165+
}

0 commit comments

Comments
 (0)