1
+
2
+
3
+ use std:: collections:: HashMap ;
4
+ use crate :: test_utils;
5
+ use aws_sdk_dynamodb:: types:: AttributeValue ;
6
+
7
+ use db_esdk:: deps:: aws_cryptography_materialProviders:: types:: material_providers_config:: MaterialProvidersConfig ;
8
+ use db_esdk:: deps:: aws_cryptography_materialProviders:: client;
9
+ use db_esdk:: deps:: aws_cryptography_dbEncryptionSdk_structuredEncryption:: types:: CryptoAction ;
10
+
11
+ use db_esdk:: deps:: aws_cryptography_dbEncryptionSdk_dynamoDb:: types:: DynamoDbTableEncryptionConfig ;
12
+ use db_esdk:: deps:: aws_cryptography_materialProviders:: types:: DbeAlgorithmSuiteId ;
13
+ use db_esdk:: intercept:: DbEsdkInterceptor ;
14
+ use db_esdk:: types:: dynamo_db_tables_encryption_config:: DynamoDbTablesEncryptionConfig ;
15
+
16
+ /*
17
+ This example sets up DynamoDb Encryption for the AWS SDK client
18
+ and uses the low level PutItem and GetItem DDB APIs to demonstrate
19
+ putting a client-side encrypted item into DynamoDb
20
+ and then retrieving and decrypting that item from DynamoDb.
21
+
22
+ Running this example requires access to the DDB Table whose name
23
+ is provided in CLI arguments.
24
+ This table must be configured with the following
25
+ primary key configuration:
26
+ - Partition key is named "partition_key" with type (S)
27
+ - Sort key is named "sort_key" with type (N)
28
+ */
29
+
30
+ pub async fn put_item_get_item ( )
31
+ {
32
+ let kms_key_id = test_utils:: TEST_KMS_KEY_ID ;
33
+ let ddb_table_name = test_utils:: TEST_DDB_TABLE_NAME ;
34
+
35
+ // 1. Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data.
36
+ // For this example, we will create a AWS KMS Keyring with the AWS KMS Key we want to use.
37
+ // We will use the `CreateMrkMultiKeyring` method to create this keyring,
38
+ // as it will correctly handle both single region and Multi-Region KMS Keys.
39
+ let provider_config = MaterialProvidersConfig :: builder ( ) . build ( ) . unwrap ( ) ;
40
+ let mat_prov = client:: Client :: from_conf ( provider_config) . unwrap ( ) ;
41
+ let kms_keyring = mat_prov. create_aws_kms_mrk_multi_keyring ( ) . generator ( kms_key_id) . send ( ) . await . unwrap ( ) ;
42
+
43
+
44
+ // 2. Configure which attributes are encrypted and/or signed when writing new items.
45
+ // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
46
+ // we must explicitly configure how they should be treated during item encryption:
47
+ // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
48
+ // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
49
+ // - DO_NOTHING: The attribute is not encrypted and not included in the signature
50
+ let attribute_actions_on_encrypt = HashMap :: from ( [
51
+ ( "partition_key" . to_string ( ) , CryptoAction :: SignOnly ) ,
52
+ ( "sort_key" . to_string ( ) , CryptoAction :: SignOnly ) ,
53
+ ( "attribute1" . to_string ( ) , CryptoAction :: EncryptAndSign ) ,
54
+ ( "attribute2" . to_string ( ) , CryptoAction :: SignOnly ) ,
55
+ ( ":attribute3" . to_string ( ) , CryptoAction :: DoNothing ) ,
56
+ ] ) ;
57
+
58
+ // 3. Configure which attributes we expect to be included in the signature
59
+ // when reading items. There are two options for configuring this:
60
+ //
61
+ // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
62
+ // When defining your DynamoDb schema and deciding on attribute names,
63
+ // choose a distinguishing prefix (such as ":") for all attributes that
64
+ // you do not want to include in the signature.
65
+ // This has two main benefits:
66
+ // - It is easier to reason about the security and authenticity of data within your item
67
+ // when all unauthenticated data is easily distinguishable by their attribute name.
68
+ // - If you need to add new unauthenticated attributes in the future,
69
+ // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
70
+ // and immediately start writing to that new attribute, without
71
+ // any other configuration update needed.
72
+ // Once you configure this field, it is not safe to update it.
73
+ //
74
+ // - Configure `allowedUnsignedAttributes`: You may also explicitly list
75
+ // a set of attributes that should be considered unauthenticated when encountered
76
+ // on read. Be careful if you use this configuration. Do not remove an attribute
77
+ // name from this configuration, even if you are no longer writing with that attribute,
78
+ // as old items may still include this attribute, and our configuration needs to know
79
+ // to continue to exclude this attribute from the signature scope.
80
+ // If you add new attribute names to this field, you must first deploy the update to this
81
+ // field to all readers in your host fleet before deploying the update to start writing
82
+ // with that new attribute.
83
+ //
84
+ // For this example, we have designed our DynamoDb table such that any attribute name with
85
+ // the ":" prefix should be considered unauthenticated.
86
+ const UNSIGNED_ATTR_PREFIX : & str = ":" ;
87
+
88
+ // 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
89
+ let table_config = DynamoDbTableEncryptionConfig :: builder ( )
90
+ . logical_table_name ( ddb_table_name)
91
+ . partition_key_name ( "partition_key" )
92
+ . sort_key_name ( "sort_key" )
93
+ . attribute_actions_on_encrypt ( attribute_actions_on_encrypt)
94
+ . keyring ( kms_keyring)
95
+ . allowed_unsigned_attribute_prefix ( UNSIGNED_ATTR_PREFIX )
96
+ // Specifying an algorithm suite is not required,
97
+ // but is done here to demonstrate how to do so.
98
+ // We suggest using the
99
+ // `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite,
100
+ // which includes AES-GCM with key derivation, signing, and key commitment.
101
+ // This is also the default algorithm suite if one is not specified in this config.
102
+ // For more information on supported algorithm suites, see:
103
+ // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html
104
+ . algorithm_suite_id ( DbeAlgorithmSuiteId :: AlgAes256GcmHkdfSha512CommitKeyEcdsaP384SymsigHmacSha384 )
105
+ . build ( ) . unwrap ( ) ;
106
+
107
+ let table_configs = DynamoDbTablesEncryptionConfig :: builder ( )
108
+ . table_encryption_configs ( HashMap :: from ( [ ( ddb_table_name. to_string ( ) , table_config) ] ) )
109
+ . build ( )
110
+ . unwrap ( ) ;
111
+
112
+ // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs
113
+ let sdk_config = aws_config:: load_defaults ( aws_config:: BehaviorVersion :: latest ( ) ) . await ;
114
+ let dynamo_config = aws_sdk_dynamodb:: config:: Builder :: from ( & sdk_config)
115
+ . interceptor ( DbEsdkInterceptor :: new ( table_configs) )
116
+ . build ( ) ;
117
+ let ddb = aws_sdk_dynamodb:: Client :: from_conf ( dynamo_config) ;
118
+
119
+ // 6. Put an item into our table using the above client.
120
+ // Before the item gets sent to DynamoDb, it will be encrypted
121
+ // client-side, according to our configuration.
122
+ let item = HashMap :: from ( [
123
+ ( "partition_key" . to_string ( ) , AttributeValue :: S ( "BasicPutGetExample" . to_string ( ) ) ) ,
124
+ ( "sort_key" . to_string ( ) , AttributeValue :: N ( "0" . to_string ( ) ) ) ,
125
+ ( "attribute1" . to_string ( ) , AttributeValue :: S ( "encrypt and sign me!" . to_string ( ) ) ) ,
126
+ ( "attribute2" . to_string ( ) , AttributeValue :: S ( "sign me!" . to_string ( ) ) ) ,
127
+ ( ":attribute3" . to_string ( ) , AttributeValue :: S ( "ignore me!" . to_string ( ) ) ) ,
128
+ ] ) ;
129
+
130
+ let _resp = ddb
131
+ . put_item ( )
132
+ . table_name ( ddb_table_name)
133
+ . set_item ( Some ( item. clone ( ) ) )
134
+ . send ( )
135
+ . await
136
+ . unwrap ( ) ;
137
+
138
+ // 7. Get the item back from our table using the same client.
139
+ // The client will decrypt the item client-side, and return
140
+ // back the original item.
141
+ let key_to_get = HashMap :: from ( [
142
+ ( "partition_key" . to_string ( ) , AttributeValue :: S ( "BasicPutGetExample" . to_string ( ) ) ) ,
143
+ ( "sort_key" . to_string ( ) , AttributeValue :: N ( "0" . to_string ( ) ) ) ,
144
+ ] ) ;
145
+
146
+ let resp = ddb
147
+ . get_item ( )
148
+ . table_name ( ddb_table_name)
149
+ . set_key ( Some ( key_to_get) )
150
+ // In this example we configure a strongly consistent read
151
+ // because we perform a read immediately after a write (for demonstrative purposes).
152
+ // By default, reads are only eventually consistent.
153
+ // Read our docs to determine which read consistency to use for your application:
154
+ // https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
155
+ . consistent_read ( true )
156
+ . send ( )
157
+ . await
158
+ . unwrap ( ) ;
159
+
160
+ assert_eq ! ( resp. item, Some ( item) ) ;
161
+ println ! ( "put_item_get_item Successful." ) ;
162
+ }
163
+
0 commit comments