pub struct Client { /* private fields */ }
Implementations§
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_keyring(&self) -> CreateAwsKmsKeyringFluentBuilder
pub fn create_aws_kms_keyring(&self) -> CreateAwsKmsKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsKeyring
operation.
- The fluent builder is configurable:
grant_tokens(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_grant_tokens(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)kms_client(impl Into<Option<crate::deps::com_amazonaws_kms::client::Client>>)
/set_kms_client(Option<crate::deps::com_amazonaws_kms::client::Client>)
: (undocumented)kms_key_id(impl Into<Option<::std::string::String>>)
/set_kms_key_id(Option<::std::string::String>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsKeyringError>
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_discovery_keyring(
&self,
) -> CreateAwsKmsDiscoveryKeyringFluentBuilder
pub fn create_aws_kms_discovery_keyring( &self, ) -> CreateAwsKmsDiscoveryKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsDiscoveryKeyring
operation.
- The fluent builder is configurable:
discovery_filter(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::DiscoveryFilter>>)
/set_discovery_filter(Option<crate::deps::aws_cryptography_materialProviders::types::DiscoveryFilter>)
: (undocumented)grant_tokens(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_grant_tokens(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)kms_client(impl Into<Option<crate::deps::com_amazonaws_kms::client::Client>>)
/set_kms_client(Option<crate::deps::com_amazonaws_kms::client::Client>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsDiscoveryKeyringError>
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_multi_keyring(
&self,
) -> CreateAwsKmsMultiKeyringFluentBuilder
pub fn create_aws_kms_multi_keyring( &self, ) -> CreateAwsKmsMultiKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsMultiKeyring
operation.
- The fluent builder is configurable:
client_supplier(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::client_supplier::ClientSupplierRef>>)
/set_client_supplier(Option<crate::deps::aws_cryptography_materialProviders::types::client_supplier::ClientSupplierRef>)
: (undocumented)generator(impl Into<Option<::std::string::String>>)
/set_generator(Option<::std::string::String>)
: (undocumented)grant_tokens(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_grant_tokens(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)kms_key_ids(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_kms_key_ids(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsMultiKeyringError>
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_discovery_multi_keyring(
&self,
) -> CreateAwsKmsDiscoveryMultiKeyringFluentBuilder
pub fn create_aws_kms_discovery_multi_keyring( &self, ) -> CreateAwsKmsDiscoveryMultiKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsDiscoveryMultiKeyring
operation.
- The fluent builder is configurable:
client_supplier(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::client_supplier::ClientSupplierRef>>)
/set_client_supplier(Option<crate::deps::aws_cryptography_materialProviders::types::client_supplier::ClientSupplierRef>)
: (undocumented)discovery_filter(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::DiscoveryFilter>>)
/set_discovery_filter(Option<crate::deps::aws_cryptography_materialProviders::types::DiscoveryFilter>)
: (undocumented)grant_tokens(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_grant_tokens(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)regions(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_regions(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsDiscoveryMultiKeyringError>
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_mrk_keyring(&self) -> CreateAwsKmsMrkKeyringFluentBuilder
pub fn create_aws_kms_mrk_keyring(&self) -> CreateAwsKmsMrkKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsMrkKeyring
operation.
- The fluent builder is configurable:
grant_tokens(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_grant_tokens(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)kms_client(impl Into<Option<crate::deps::com_amazonaws_kms::client::Client>>)
/set_kms_client(Option<crate::deps::com_amazonaws_kms::client::Client>)
: (undocumented)kms_key_id(impl Into<Option<::std::string::String>>)
/set_kms_key_id(Option<::std::string::String>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsMrkKeyringError>
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_mrk_multi_keyring(
&self,
) -> CreateAwsKmsMrkMultiKeyringFluentBuilder
pub fn create_aws_kms_mrk_multi_keyring( &self, ) -> CreateAwsKmsMrkMultiKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsMrkMultiKeyring
operation.
- The fluent builder is configurable:
client_supplier(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::client_supplier::ClientSupplierRef>>)
/set_client_supplier(Option<crate::deps::aws_cryptography_materialProviders::types::client_supplier::ClientSupplierRef>)
: (undocumented)generator(impl Into<Option<::std::string::String>>)
/set_generator(Option<::std::string::String>)
: (undocumented)grant_tokens(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_grant_tokens(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)kms_key_ids(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_kms_key_ids(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsMrkMultiKeyringError>
Examples found in repository?
34pub async fn encrypt_decrypt() -> Result<(), crate::BoxError> {
35 let kms_key_id = test_utils::TEST_KMS_KEY_ID;
36 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
37
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 let provider_config = MaterialProvidersConfig::builder().build()?;
43 let mat_prov = mpl_client::Client::from_conf(provider_config)?;
44 let kms_keyring = mat_prov
45 .create_aws_kms_mrk_multi_keyring()
46 .generator(kms_key_id)
47 .send()
48 .await?;
49
50 // 2. Configure which attributes are encrypted and/or signed when writing new items.
51 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
52 // we must explicitly configure how they should be treated during item encryption:
53 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
54 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
55 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
56 let attribute_actions_on_encrypt = HashMap::from([
57 ("partition_key".to_string(), CryptoAction::SignOnly),
58 ("sort_key".to_string(), CryptoAction::SignOnly),
59 ("attribute1".to_string(), CryptoAction::EncryptAndSign),
60 ("attribute2".to_string(), CryptoAction::SignOnly),
61 (":attribute3".to_string(), CryptoAction::DoNothing),
62 ]);
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 const UNSIGNED_ATTR_PREFIX: &str = ":";
93
94 // 4. Create the configuration for the DynamoDb Item Encryptor
95 let config = DynamoDbItemEncryptorConfig::builder()
96 .logical_table_name(ddb_table_name)
97 .partition_key_name("partition_key")
98 .sort_key_name("sort_key")
99 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
100 .keyring(kms_keyring)
101 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
102 // Specifying an algorithm suite is not required,
103 // but is done here to demonstrate how to do so.
104 // We suggest using the
105 // `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite,
106 // which includes AES-GCM with key derivation, signing, and key commitment.
107 // This is also the default algorithm suite if one is not specified in this config.
108 // For more information on supported algorithm suites, see:
109 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html
110 .algorithm_suite_id(
111 DbeAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKeyEcdsaP384SymsigHmacSha384,
112 )
113 .build()?;
114
115 // 5. Create the DynamoDb Item Encryptor
116 let item_encryptor = enc_client::Client::from_conf(config)?;
117
118 // 6. Directly encrypt a DynamoDb item using the DynamoDb Item Encryptor
119 let original_item = HashMap::from([
120 (
121 "partition_key".to_string(),
122 AttributeValue::S("ItemEncryptDecryptExample".to_string()),
123 ),
124 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
125 (
126 "attribute1".to_string(),
127 AttributeValue::S("encrypt and sign me!".to_string()),
128 ),
129 (
130 "attribute2".to_string(),
131 AttributeValue::S("sign me!".to_string()),
132 ),
133 (
134 ":attribute3".to_string(),
135 AttributeValue::S("ignore me!".to_string()),
136 ),
137 ]);
138
139 let encrypted_item = item_encryptor
140 .encrypt_item()
141 .plaintext_item(original_item.clone())
142 .send()
143 .await?
144 .encrypted_item
145 .unwrap();
146
147 // Demonstrate that the item has been encrypted
148 assert_eq!(
149 encrypted_item["partition_key"],
150 AttributeValue::S("ItemEncryptDecryptExample".to_string())
151 );
152 assert_eq!(
153 encrypted_item["sort_key"],
154 AttributeValue::N("0".to_string())
155 );
156 assert!(encrypted_item["attribute1"].is_b());
157 assert!(!encrypted_item["attribute1"].is_s());
158
159 // 7. Directly decrypt the encrypted item using the DynamoDb Item Encryptor
160 let decrypted_item = item_encryptor
161 .decrypt_item()
162 .encrypted_item(encrypted_item)
163 .send()
164 .await?
165 .plaintext_item
166 .unwrap();
167
168 // Demonstrate that GetItem succeeded and returned the decrypted item
169 assert_eq!(decrypted_item, original_item);
170 println!("encrypt_decrypt successful.");
171 Ok(())
172}
More examples
31pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
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()?;
40 let mat_prov = client::Client::from_conf(provider_config)?;
41 let kms_keyring = mat_prov
42 .create_aws_kms_mrk_multi_keyring()
43 .generator(kms_key_id)
44 .send()
45 .await?;
46
47 // 2. Configure which attributes are encrypted and/or signed when writing new items.
48 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
49 // we must explicitly configure how they should be treated during item encryption:
50 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
51 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
52 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
53 let attribute_actions_on_encrypt = HashMap::from([
54 ("partition_key".to_string(), CryptoAction::SignOnly),
55 ("sort_key".to_string(), CryptoAction::SignOnly),
56 ("attribute1".to_string(), CryptoAction::EncryptAndSign),
57 ("attribute2".to_string(), CryptoAction::SignOnly),
58 (":attribute3".to_string(), CryptoAction::DoNothing),
59 ]);
60
61 // 3. Configure which attributes we expect to be included in the signature
62 // when reading items. There are two options for configuring this:
63 //
64 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
65 // When defining your DynamoDb schema and deciding on attribute names,
66 // choose a distinguishing prefix (such as ":") for all attributes that
67 // you do not want to include in the signature.
68 // This has two main benefits:
69 // - It is easier to reason about the security and authenticity of data within your item
70 // when all unauthenticated data is easily distinguishable by their attribute name.
71 // - If you need to add new unauthenticated attributes in the future,
72 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
73 // and immediately start writing to that new attribute, without
74 // any other configuration update needed.
75 // Once you configure this field, it is not safe to update it.
76 //
77 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
78 // a set of attributes that should be considered unauthenticated when encountered
79 // on read. Be careful if you use this configuration. Do not remove an attribute
80 // name from this configuration, even if you are no longer writing with that attribute,
81 // as old items may still include this attribute, and our configuration needs to know
82 // to continue to exclude this attribute from the signature scope.
83 // If you add new attribute names to this field, you must first deploy the update to this
84 // field to all readers in your host fleet before deploying the update to start writing
85 // with that new attribute.
86 //
87 // For this example, we have designed our DynamoDb table such that any attribute name with
88 // the ":" prefix should be considered unauthenticated.
89 const UNSIGNED_ATTR_PREFIX: &str = ":";
90
91 // 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
92 let table_config = DynamoDbTableEncryptionConfig::builder()
93 .logical_table_name(ddb_table_name)
94 .partition_key_name("partition_key")
95 .sort_key_name("sort_key")
96 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
97 .keyring(kms_keyring)
98 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
99 // Specifying an algorithm suite is not required,
100 // but is done here to demonstrate how to do so.
101 // We suggest using the
102 // `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite,
103 // which includes AES-GCM with key derivation, signing, and key commitment.
104 // This is also the default algorithm suite if one is not specified in this config.
105 // For more information on supported algorithm suites, see:
106 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html
107 .algorithm_suite_id(
108 DbeAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKeyEcdsaP384SymsigHmacSha384,
109 )
110 .build()?;
111
112 let table_configs = DynamoDbTablesEncryptionConfig::builder()
113 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
114 .build()?;
115
116 // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs
117 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
118 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
119 .interceptor(DbEsdkInterceptor::new(table_configs)?)
120 .build();
121 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
122
123 // 6. Put an item into our table using the above client.
124 // Before the item gets sent to DynamoDb, it will be encrypted
125 // client-side, according to our configuration.
126 let item = HashMap::from([
127 (
128 "partition_key".to_string(),
129 AttributeValue::S("BasicPutGetExample".to_string()),
130 ),
131 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
132 (
133 "attribute1".to_string(),
134 AttributeValue::S("encrypt and sign me!".to_string()),
135 ),
136 (
137 "attribute2".to_string(),
138 AttributeValue::S("sign me!".to_string()),
139 ),
140 (
141 ":attribute3".to_string(),
142 AttributeValue::S("ignore me!".to_string()),
143 ),
144 ]);
145
146 ddb.put_item()
147 .table_name(ddb_table_name)
148 .set_item(Some(item.clone()))
149 .send()
150 .await?;
151
152 // 7. Get the item back from our table using the same client.
153 // The client will decrypt the item client-side, and return
154 // back the original item.
155 let key_to_get = HashMap::from([
156 (
157 "partition_key".to_string(),
158 AttributeValue::S("BasicPutGetExample".to_string()),
159 ),
160 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
161 ]);
162
163 let resp = ddb
164 .get_item()
165 .table_name(ddb_table_name)
166 .set_key(Some(key_to_get))
167 // In this example we configure a strongly consistent read
168 // because we perform a read immediately after a write (for demonstrative purposes).
169 // By default, reads are only eventually consistent.
170 // Read our docs to determine which read consistency to use for your application:
171 // https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
172 .consistent_read(true)
173 .send()
174 .await?;
175
176 assert_eq!(resp.item, Some(item));
177 println!("put_item_get_item successful.");
178 Ok(())
179}
37pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
38 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
39 let key_arn = test_utils::TEST_MRK_KEY_ID;
40 let account_ids = vec![test_utils::TEST_AWS_ACCOUNT_ID.to_string()];
41 let regions = vec![test_utils::TEST_AWS_REGION.to_string()];
42
43 // 1. Create a single MRK multi-keyring using the key arn.
44 // Although this example demonstrates use of the MRK discovery multi-keyring,
45 // a discovery keyring cannot be used to encrypt. So we will need to construct
46 // a non-discovery keyring for this example to encrypt. For more information on MRK
47 // multi-keyrings, see the MultiMrkKeyringExample in this directory.
48 // Though this is an "MRK multi-keyring", we do not need to provide multiple keys,
49 // and can use single-region KMS keys. We will provide a single key here; this
50 // can be either an MRK or a single-region key.
51 let mpl_config = MaterialProvidersConfig::builder().build()?;
52 let mpl = mpl_client::Client::from_conf(mpl_config)?;
53 let encrypt_keyring = mpl
54 .create_aws_kms_mrk_multi_keyring()
55 .generator(key_arn)
56 .send()
57 .await?;
58
59 // 2. Configure which attributes are encrypted and/or signed when writing new items.
60 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
61 // we must explicitly configure how they should be treated during item encryption:
62 // - ENCRYPT_AND_SIGN: The attribute is encrypted and icncluded in the signature
63 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
64 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
65 let attribute_actions_on_encrypt = HashMap::from([
66 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
67 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
68 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
69 ]);
70
71 // 3. Configure which attributes we expect to be included in the signature
72 // when reading items. There are two options for configuring this:
73 //
74 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
75 // When defining your DynamoDb schema and deciding on attribute names,
76 // choose a distinguishing prefix (such as ":") for all attributes that
77 // you do not want to include in the signature.
78 // This has two main benefits:
79 // - It is easier to reason about the security and authenticity of data within your item
80 // when all unauthenticated data is easily distinguishable by their attribute name.
81 // - If you need to add new unauthenticated attributes in the future,
82 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
83 // and immediately start writing to that new attribute, without
84 // any other configuration update needed.
85 // Once you configure this field, it is not safe to update it.
86 //
87 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
88 // a set of attributes that should be considered unauthenticated when encountered
89 // on read. Be careful if you use this configuration. Do not remove an attribute
90 // name from this configuration, even if you are no longer writing with that attribute,
91 // as old items may still include this attribute, and our configuration needs to know
92 // to continue to exclude this attribute from the signature scope.
93 // If you add new attribute names to this field, you must first deploy the update to this
94 // field to all readers in your host fleet before deploying the update to start writing
95 // with that new attribute.
96 //
97 // For this example, we currently authenticate all attributes. To make it easier to
98 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
99 const UNSIGNED_ATTR_PREFIX: &str = ":";
100
101 // 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
102 let table_config = DynamoDbTableEncryptionConfig::builder()
103 .logical_table_name(ddb_table_name)
104 .partition_key_name("partition_key")
105 .sort_key_name("sort_key")
106 .attribute_actions_on_encrypt(attribute_actions_on_encrypt.clone())
107 .keyring(encrypt_keyring)
108 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
109 .build()?;
110
111 let table_configs = DynamoDbTablesEncryptionConfig::builder()
112 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
113 .build()?;
114
115 // 5. Create a new AWS SDK DynamoDb client using the config above
116 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
117 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
118 .interceptor(DbEsdkInterceptor::new(table_configs)?)
119 .build();
120 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
121
122 // 6. Put an item into our table using the above client.
123 // Before the item gets sent to DynamoDb, it will be encrypted
124 // client-side using the MRK multi-keyring.
125 let item = HashMap::from([
126 (
127 "partition_key".to_string(),
128 AttributeValue::S("awsKmsMrkDiscoveryMultiKeyringItem".to_string()),
129 ),
130 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
131 (
132 "sensitive_data".to_string(),
133 AttributeValue::S("encrypt and sign me!".to_string()),
134 ),
135 ]);
136
137 ddb.put_item()
138 .table_name(ddb_table_name)
139 .set_item(Some(item.clone()))
140 .send()
141 .await?;
142
143 // 7. Construct a discovery filter.
144 // A discovery filter limits the set of encrypted data keys
145 // the keyring can use to decrypt data.
146 // We will only let the keyring use keys in the selected AWS accounts
147 // and in the `aws` partition.
148 // This is the suggested config for most users; for more detailed config, see
149 // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery
150 let discovery_filter = DiscoveryFilter::builder()
151 .partition("aws")
152 .account_ids(account_ids)
153 .build()?;
154
155 // 8. Construct a discovery keyring.
156 // Note that we choose to use the MRK discovery multi-keyring, even though
157 // our original keyring used a single KMS key.
158
159 let decrypt_keyring = mpl
160 .create_aws_kms_mrk_discovery_multi_keyring()
161 .discovery_filter(discovery_filter)
162 .regions(regions)
163 .send()
164 .await?;
165
166 // 9. Create new DDB config and client using the decrypt discovery keyring.
167 // This is the same as the above config, except we pass in the decrypt keyring.
168 let table_config_for_decrypt = DynamoDbTableEncryptionConfig::builder()
169 .logical_table_name(ddb_table_name)
170 .partition_key_name("partition_key")
171 .sort_key_name("sort_key")
172 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
173 .keyring(decrypt_keyring)
174 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
175 .build()?;
176
177 let table_configs_for_decrypt = DynamoDbTablesEncryptionConfig::builder()
178 .table_encryption_configs(HashMap::from([(
179 ddb_table_name.to_string(),
180 table_config_for_decrypt,
181 )]))
182 .build()?;
183
184 let dynamo_config_for_decrypt = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
185 .interceptor(DbEsdkInterceptor::new(table_configs_for_decrypt)?)
186 .build();
187 let ddb_for_decrypt = aws_sdk_dynamodb::Client::from_conf(dynamo_config_for_decrypt);
188
189 // 10. Get the item back from our table using the client.
190 // The client will retrieve encrypted items from the DDB table, then
191 // detect the KMS key that was used to encrypt their data keys.
192 // The client will make a request to KMS to decrypt with the encrypting KMS key.
193 // If the client has permission to decrypt with the KMS key,
194 // the client will decrypt the item client-side using the keyring
195 // and return the original item.
196 let key_to_get = HashMap::from([
197 (
198 "partition_key".to_string(),
199 AttributeValue::S("awsKmsMrkDiscoveryMultiKeyringItem".to_string()),
200 ),
201 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
202 ]);
203
204 let resp = ddb_for_decrypt
205 .get_item()
206 .table_name(ddb_table_name)
207 .set_key(Some(key_to_get))
208 .consistent_read(true)
209 .send()
210 .await?;
211
212 assert_eq!(resp.item, Some(item));
213
214 println!("mrk_discovery_multi_keyring successful.");
215 Ok(())
216}
46pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
47 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
48 let key_arn = test_utils::TEST_KMS_KEY_ID;
49 let aes_key_bytes = generate_aes_key_bytes();
50
51 // 1. Create the raw AES keyring.
52 let mpl_config = MaterialProvidersConfig::builder().build()?;
53 let mpl = mpl_client::Client::from_conf(mpl_config)?;
54 let raw_aes_keyring = mpl
55 .create_raw_aes_keyring()
56 .key_name("my-aes-key-name")
57 .key_namespace("my-key-namespace")
58 .wrapping_key(aes_key_bytes)
59 .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16)
60 .send()
61 .await?;
62
63 // 2. Create the AWS KMS keyring.
64 // We create a MRK multi keyring, as this interface also supports
65 // single-region KMS keys (standard KMS keys),
66 // and creates the KMS client for us automatically.
67 let aws_kms_mrk_multi_keyring = mpl
68 .create_aws_kms_mrk_multi_keyring()
69 .generator(key_arn)
70 .send()
71 .await?;
72
73 // 3. Create the multi-keyring.
74 // We will label the AWS KMS keyring as the generator and the raw AES keyring as the
75 // only child keyring.
76 // You must provide a generator keyring to encrypt data.
77 // You may provide additional child keyrings. Each child keyring will be able to
78 // decrypt data encrypted with the multi-keyring on its own. It does not need
79 // knowledge of any other child keyrings or the generator keyring to decrypt.
80
81 let multi_keyring = mpl
82 .create_multi_keyring()
83 .generator(aws_kms_mrk_multi_keyring)
84 .child_keyrings(vec![raw_aes_keyring.clone()])
85 .send()
86 .await?;
87
88 // 4. Configure which attributes are encrypted and/or signed when writing new items.
89 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
90 // we must explicitly configure how they should be treated during item encryption:
91 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
92 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
93 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
94 let attribute_actions_on_encrypt = HashMap::from([
95 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
96 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
97 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
98 ]);
99
100 // 5. Configure which attributes we expect to be included in the signature
101 // when reading items. There are two options for configuring this:
102 //
103 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
104 // When defining your DynamoDb schema and deciding on attribute names,
105 // choose a distinguishing prefix (such as ":") for all attributes that
106 // you do not want to include in the signature.
107 // This has two main benefits:
108 // - It is easier to reason about the security and authenticity of data within your item
109 // when all unauthenticated data is easily distinguishable by their attribute name.
110 // - If you need to add new unauthenticated attributes in the future,
111 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
112 // and immediately start writing to that new attribute, without
113 // any other configuration update needed.
114 // Once you configure this field, it is not safe to update it.
115 //
116 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
117 // a set of attributes that should be considered unauthenticated when encountered
118 // on read. Be careful if you use this configuration. Do not remove an attribute
119 // name from this configuration, even if you are no longer writing with that attribute,
120 // as old items may still include this attribute, and our configuration needs to know
121 // to continue to exclude this attribute from the signature scope.
122 // If you add new attribute names to this field, you must first deploy the update to this
123 // field to all readers in your host fleet before deploying the update to start writing
124 // with that new attribute.
125 //
126 // For this example, we currently authenticate all attributes. To make it easier to
127 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
128 const UNSIGNED_ATTR_PREFIX: &str = ":";
129
130 // 6. Create the DynamoDb Encryption configuration for the table we will be writing to.
131 // Note that this example creates one config/client combination for PUT, and another
132 // for GET. The PUT config uses the multi-keyring, while the GET config uses the
133 // raw AES keyring. This is solely done to demonstrate that a keyring included as
134 // a child of a multi-keyring can be used to decrypt data on its own.
135 let table_config = DynamoDbTableEncryptionConfig::builder()
136 .logical_table_name(ddb_table_name)
137 .partition_key_name("partition_key")
138 .sort_key_name("sort_key")
139 .attribute_actions_on_encrypt(attribute_actions_on_encrypt.clone())
140 .keyring(multi_keyring)
141 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
142 .build()?;
143
144 let table_configs = DynamoDbTablesEncryptionConfig::builder()
145 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
146 .build()?;
147
148 // 7. Create a new AWS SDK DynamoDb client using the config above
149 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
150 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
151 .interceptor(DbEsdkInterceptor::new(table_configs)?)
152 .build();
153 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
154
155 // 8. Put an item into our table using the above client.
156 // Before the item gets sent to DynamoDb, it will be encrypted
157 // client-side using the multi-keyring.
158 // The item will be encrypted with all wrapping keys in the keyring,
159 // so that it can be decrypted with any one of the keys.
160 let item = HashMap::from([
161 (
162 "partition_key".to_string(),
163 AttributeValue::S("multiKeyringItem".to_string()),
164 ),
165 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
166 (
167 "sensitive_data".to_string(),
168 AttributeValue::S("encrypt and sign me!".to_string()),
169 ),
170 ]);
171
172 ddb.put_item()
173 .table_name(ddb_table_name)
174 .set_item(Some(item.clone()))
175 .send()
176 .await?;
177
178 // 9. Get the item back from our table using the above client.
179 // The client will decrypt the item client-side using the AWS KMS
180 // keyring, and return back the original item.
181 // Since the generator key is the first available key in the keyring,
182 // that is the key that will be used to decrypt this item.
183 let key_to_get = HashMap::from([
184 (
185 "partition_key".to_string(),
186 AttributeValue::S("multiKeyringItem".to_string()),
187 ),
188 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
189 ]);
190
191 let resp = ddb
192 .get_item()
193 .table_name(ddb_table_name)
194 .set_key(Some(key_to_get.clone()))
195 .consistent_read(true)
196 .send()
197 .await?;
198
199 assert_eq!(resp.item, Some(item.clone()));
200
201 // 10. Create a new config and client with only the raw AES keyring to GET the item
202 // This is the same setup as above, except the config uses the `rawAesKeyring`.
203 let only_aes_table_config = DynamoDbTableEncryptionConfig::builder()
204 .logical_table_name(ddb_table_name)
205 .partition_key_name("partition_key")
206 .sort_key_name("sort_key")
207 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
208 .keyring(raw_aes_keyring)
209 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
210 .build()?;
211
212 let only_aes_table_configs = DynamoDbTablesEncryptionConfig::builder()
213 .table_encryption_configs(HashMap::from([(
214 ddb_table_name.to_string(),
215 only_aes_table_config,
216 )]))
217 .build()?;
218
219 let only_aes_dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
220 .interceptor(DbEsdkInterceptor::new(only_aes_table_configs)?)
221 .build();
222 let only_aes_ddb = aws_sdk_dynamodb::Client::from_conf(only_aes_dynamo_config);
223
224 // 11. Get the item back from our table using the client
225 // configured with only the raw AES keyring.
226 // The client will decrypt the item client-side using the raw
227 // AES keyring, and return back the original item.
228 let resp = only_aes_ddb
229 .get_item()
230 .table_name(ddb_table_name)
231 .set_key(Some(key_to_get.clone()))
232 .consistent_read(true)
233 .send()
234 .await?;
235
236 assert_eq!(resp.item, Some(item.clone()));
237
238 println!("multi_keyring successful.");
239 Ok(())
240}
31pub async fn multi_put_get() -> Result<(), crate::BoxError> {
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()?;
40 let mat_prov = client::Client::from_conf(provider_config)?;
41 let kms_keyring = mat_prov
42 .create_aws_kms_mrk_multi_keyring()
43 .generator(kms_key_id)
44 .send()
45 .await?;
46
47 // 2. Configure which attributes are encrypted and/or signed when writing new items.
48 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
49 // we must explicitly configure how they should be treated during item encryption:
50 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
51 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
52 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
53 let attribute_actions_on_encrypt = HashMap::from([
54 ("partition_key".to_string(), CryptoAction::SignOnly),
55 ("sort_key".to_string(), CryptoAction::SignOnly),
56 ("attribute1".to_string(), CryptoAction::EncryptAndSign),
57 ("attribute2".to_string(), CryptoAction::SignOnly),
58 (":attribute3".to_string(), CryptoAction::DoNothing),
59 ]);
60
61 // 3. Configure which attributes we expect to be included in the signature
62 // when reading items. There are two options for configuring this:
63 //
64 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
65 // When defining your DynamoDb schema and deciding on attribute names,
66 // choose a distinguishing prefix (such as ":") for all attributes that
67 // you do not want to include in the signature.
68 // This has two main benefits:
69 // - It is easier to reason about the security and authenticity of data within your item
70 // when all unauthenticated data is easily distinguishable by their attribute name.
71 // - If you need to add new unauthenticated attributes in the future,
72 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
73 // and immediately start writing to that new attribute, without
74 // any other configuration update needed.
75 // Once you configure this field, it is not safe to update it.
76 //
77 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
78 // a set of attributes that should be considered unauthenticated when encountered
79 // on read. Be careful if you use this configuration. Do not remove an attribute
80 // name from this configuration, even if you are no longer writing with that attribute,
81 // as old items may still include this attribute, and our configuration needs to know
82 // to continue to exclude this attribute from the signature scope.
83 // If you add new attribute names to this field, you must first deploy the update to this
84 // field to all readers in your host fleet before deploying the update to start writing
85 // with that new attribute.
86 //
87 // For this example, we have designed our DynamoDb table such that any attribute name with
88 // the ":" prefix should be considered unauthenticated.
89 const UNSIGNED_ATTR_PREFIX: &str = ":";
90
91 // 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
92 let table_config = DynamoDbTableEncryptionConfig::builder()
93 .logical_table_name(ddb_table_name)
94 .partition_key_name("partition_key")
95 .sort_key_name("sort_key")
96 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
97 .keyring(kms_keyring)
98 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
99 // Specifying an algorithm suite is not required,
100 // but is done here to demonstrate how to do so.
101 // We suggest using the
102 // `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite,
103 // which includes AES-GCM with key derivation, signing, and key commitment.
104 // This is also the default algorithm suite if one is not specified in this config.
105 // For more information on supported algorithm suites, see:
106 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html
107 .algorithm_suite_id(
108 DbeAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKeyEcdsaP384SymsigHmacSha384,
109 )
110 .build()?;
111
112 let table_configs = DynamoDbTablesEncryptionConfig::builder()
113 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
114 .build()?;
115
116 // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs
117 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
118 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
119 .interceptor(DbEsdkInterceptor::new(table_configs)?)
120 .build();
121 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
122
123 // 6. Put an item into our table using the above client.
124 // Before the item gets sent to DynamoDb, it will be encrypted
125 // client-side, according to our configuration.
126 let batch_write_item = HashMap::from([
127 (
128 "partition_key".to_string(),
129 AttributeValue::S("BatchWriteItemExample".to_string()),
130 ),
131 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
132 (
133 "attribute1".to_string(),
134 AttributeValue::S("encrypt and sign me!".to_string()),
135 ),
136 (
137 "attribute2".to_string(),
138 AttributeValue::S("sign me!".to_string()),
139 ),
140 (
141 ":attribute3".to_string(),
142 AttributeValue::S("ignore me!".to_string()),
143 ),
144 ]);
145 let put_request = aws_sdk_dynamodb::types::PutRequest::builder()
146 .set_item(Some(batch_write_item))
147 .build()?;
148
149 let batch_write_request = aws_sdk_dynamodb::types::WriteRequest::builder()
150 .put_request(put_request)
151 .build();
152
153 let transact_write_item = HashMap::from([
154 (
155 "partition_key".to_string(),
156 AttributeValue::S("TransactWriteItemExample".to_string()),
157 ),
158 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
159 (
160 "attribute1".to_string(),
161 AttributeValue::S("encrypt and sign me!".to_string()),
162 ),
163 (
164 "attribute2".to_string(),
165 AttributeValue::S("sign me!".to_string()),
166 ),
167 (
168 ":attribute3".to_string(),
169 AttributeValue::S("ignore me!".to_string()),
170 ),
171 ]);
172 let transact_put = aws_sdk_dynamodb::types::Put::builder()
173 .table_name(ddb_table_name)
174 .set_item(Some(transact_write_item))
175 .build()?;
176
177 let transact_item = aws_sdk_dynamodb::types::TransactWriteItem::builder()
178 .put(transact_put)
179 .build();
180
181 ddb.batch_write_item()
182 .request_items(ddb_table_name, vec![batch_write_request])
183 .send()
184 .await?;
185
186 ddb.transact_write_items()
187 .transact_items(transact_item)
188 .send()
189 .await?;
190
191 // 7. Get the item back from our table using the same client.
192 // The client will decrypt the item client-side, and return
193 // back the original item.
194 let batch_get_keys = HashMap::from([
195 (
196 "partition_key".to_string(),
197 AttributeValue::S("BatchWriteItemExample".to_string()),
198 ),
199 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
200 ]);
201 let keys_and_attr = aws_sdk_dynamodb::types::KeysAndAttributes::builder()
202 .keys(batch_get_keys)
203 .consistent_read(true)
204 .build()?;
205
206 let batch_get_response = ddb
207 .batch_get_item()
208 .request_items(ddb_table_name, keys_and_attr)
209 .send()
210 .await?;
211
212 let returned_item = &batch_get_response.responses.unwrap()[ddb_table_name][0];
213 assert_eq!(
214 returned_item["attribute1"],
215 AttributeValue::S("encrypt and sign me!".to_string())
216 );
217
218 let transact_get_keys = HashMap::from([
219 (
220 "partition_key".to_string(),
221 AttributeValue::S("TransactWriteItemExample".to_string()),
222 ),
223 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
224 ]);
225 let transact_get = aws_sdk_dynamodb::types::Get::builder()
226 .table_name(ddb_table_name)
227 .set_key(Some(transact_get_keys))
228 .build()?;
229
230 let transact_get_item = aws_sdk_dynamodb::types::TransactGetItem::builder()
231 .get(transact_get)
232 .build();
233
234 let transact_get_response = ddb
235 .transact_get_items()
236 .transact_items(transact_get_item)
237 .send()
238 .await?;
239
240 let the_item = transact_get_response.responses.as_ref().unwrap()[0]
241 .item
242 .as_ref()
243 .unwrap();
244 assert_eq!(
245 the_item["attribute1"],
246 AttributeValue::S("encrypt and sign me!".to_string())
247 );
248
249 println!("multi_put_get successful.");
250 Ok(())
251}
37pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
38 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
39 // Note that we pass in an MRK in us-east-1...
40 let key_arn = test_utils::TEST_MRK_REPLICA_KEY_ID_US_EAST_1.to_string();
41 let account_ids = vec![test_utils::TEST_AWS_ACCOUNT_ID.to_string()];
42 // ...and access its replica in eu-west-1
43 let regions = vec!["eu-west-1".to_string()];
44
45 // 1. Create a single MRK multi-keyring.
46 // This can be either a single-region KMS key or an MRK.
47 // For this example to succeed, the key's region must either
48 // 1) be in the regions list, or
49 // 2) the key must be an MRK with a replica defined
50 // in a region in the regions list, and the client
51 // must have the correct permissions to access the replica.
52 let mpl_config = MaterialProvidersConfig::builder().build()?;
53 let mpl = mpl_client::Client::from_conf(mpl_config)?;
54
55 // Create the multi-keyring using our custom client supplier
56 // defined in the RegionalRoleClientSupplier class in this directory.
57 // Note: RegionalRoleClientSupplier will internally use the key_arn's region
58 // to retrieve the correct IAM role.
59
60 let mrk_keyring_with_client_supplier = mpl
61 .create_aws_kms_mrk_multi_keyring()
62 .client_supplier(RegionalRoleClientSupplier {})
63 .generator(key_arn)
64 .send()
65 .await?;
66
67 // 2. Configure which attributes are encrypted and/or signed when writing new items.
68 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
69 // we must explicitly configure how they should be treated during item encryption:
70 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
71 // - SIGN_ONLY: The attribute is not encrypted, but is still included in the signature
72 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
73 let attribute_actions_on_encrypt = HashMap::from([
74 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
75 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
76 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
77 ]);
78
79 // 3. Configure which attributes we expect to be included in the signature
80 // when reading items. There are two options for configuring this:
81 //
82 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
83 // When defining your DynamoDb schema and deciding on attribute names,
84 // choose a distinguishing prefix (such as ":") for all attributes that
85 // you do not want to include in the signature.
86 // This has two main benefits:
87 // - It is easier to reason about the security and authenticity of data within your item
88 // when all unauthenticated data is easily distinguishable by their attribute name.
89 // - If you need to add new unauthenticated attributes in the future,
90 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
91 // and immediately start writing to that new attribute, without
92 // any other configuration update needed.
93 // Once you configure this field, it is not safe to update it.
94 //
95 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
96 // a set of attributes that should be considered unauthenticated when encountered
97 // on read. Be careful if you use this configuration. Do not remove an attribute
98 // name from this configuration, even if you are no longer writing with that attribute,
99 // as old items may still include this attribute, and our configuration needs to know
100 // to continue to exclude this attribute from the signature scope.
101 // If you add new attribute names to this field, you must first deploy the update to this
102 // field to all readers in your host fleet before deploying the update to start writing
103 // with that new attribute.
104 //
105 // For this example, we currently authenticate all attributes. To make it easier to
106 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
107 const UNSIGNED_ATTR_PREFIX: &str = ":";
108
109 // 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
110 let table_config = DynamoDbTableEncryptionConfig::builder()
111 .logical_table_name(ddb_table_name)
112 .partition_key_name("partition_key")
113 .sort_key_name("sort_key")
114 .attribute_actions_on_encrypt(attribute_actions_on_encrypt.clone())
115 .keyring(mrk_keyring_with_client_supplier)
116 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
117 .build()?;
118
119 let table_configs = DynamoDbTablesEncryptionConfig::builder()
120 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
121 .build()?;
122
123 // 5. Create a new AWS SDK DynamoDb client using the DynamoDb Config above
124 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
125 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
126 .interceptor(DbEsdkInterceptor::new(table_configs)?)
127 .build();
128 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
129
130 // 6. Put an item into our table using the above client.
131 // Before the item gets sent to DynamoDb, it will be encrypted
132 // client-side using the MRK multi-keyring.
133 // The data key protecting this item will be encrypted
134 // with all the KMS Keys in this keyring, so that it can be
135 // decrypted with any one of those KMS Keys.
136 let item = HashMap::from([
137 (
138 "partition_key".to_string(),
139 AttributeValue::S("clientSupplierItem".to_string()),
140 ),
141 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
142 (
143 "sensitive_data".to_string(),
144 AttributeValue::S("encrypt and sign me!".to_string()),
145 ),
146 ]);
147
148 ddb.put_item()
149 .table_name(ddb_table_name)
150 .set_item(Some(item.clone()))
151 .send()
152 .await?;
153
154 // 7. Get the item back from our table using the same keyring.
155 // The client will decrypt the item client-side using the MRK
156 // and return the original item.
157 let key_to_get = HashMap::from([
158 (
159 "partition_key".to_string(),
160 AttributeValue::S("clientSupplierItem".to_string()),
161 ),
162 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
163 ]);
164
165 let resp = ddb
166 .get_item()
167 .table_name(ddb_table_name)
168 .set_key(Some(key_to_get.clone()))
169 .consistent_read(true)
170 .send()
171 .await?;
172
173 assert_eq!(
174 resp.item.unwrap()["sensitive_data"],
175 AttributeValue::S("encrypt and sign me!".to_string())
176 );
177
178 // 8. Create a MRK discovery multi-keyring with a custom client supplier.
179 // A discovery MRK multi-keyring will be composed of
180 // multiple discovery MRK keyrings, one for each region.
181 // Each component keyring has its own KMS client in a particular region.
182 // When we provide a client supplier to the multi-keyring, all component
183 // keyrings will use that client supplier configuration.
184 // In our tests, we make `key_arn` an MRK with a replica, and
185 // provide only the replica region in our discovery filter.
186 let discovery_filter = DiscoveryFilter::builder()
187 .partition("aws")
188 .account_ids(account_ids)
189 .build()?;
190
191 let mrk_discovery_client_supplier_keyring = mpl
192 .create_aws_kms_mrk_discovery_multi_keyring()
193 .client_supplier(RegionalRoleClientSupplier {})
194 .discovery_filter(discovery_filter)
195 .regions(regions)
196 .send()
197 .await?;
198
199 // 9. Create a new config and client using the discovery keyring.
200 // This is the same setup as above, except we provide the discovery keyring to the config.
201 let only_replica_table_config = DynamoDbTableEncryptionConfig::builder()
202 .logical_table_name(ddb_table_name)
203 .partition_key_name("partition_key")
204 .sort_key_name("sort_key")
205 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
206 .keyring(mrk_discovery_client_supplier_keyring)
207 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
208 .build()?;
209
210 let only_replica_table_configs = DynamoDbTablesEncryptionConfig::builder()
211 .table_encryption_configs(HashMap::from([(
212 ddb_table_name.to_string(),
213 only_replica_table_config,
214 )]))
215 .build()?;
216
217 let only_replica_dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
218 .interceptor(DbEsdkInterceptor::new(only_replica_table_configs)?)
219 .build();
220 let only_replica_ddb = aws_sdk_dynamodb::Client::from_conf(only_replica_dynamo_config);
221
222 // 10. Get the item back from our table using the discovery keyring client.
223 // The client will decrypt the item client-side using the keyring,
224 // and return the original item.
225 // The discovery keyring will only use KMS keys in the provided regions and
226 // AWS accounts. Since we have provided it with a custom client supplier
227 // which uses different IAM roles based on the key region,
228 // the discovery keyring will use a particular IAM role to decrypt
229 // based on the region of the KMS key it uses to decrypt.
230
231 let resp = only_replica_ddb
232 .get_item()
233 .table_name(ddb_table_name)
234 .set_key(Some(key_to_get))
235 .consistent_read(true)
236 .send()
237 .await?;
238
239 assert_eq!(
240 resp.item.unwrap()["sensitive_data"],
241 AttributeValue::S("encrypt and sign me!".to_string())
242 );
243
244 println!("client_supplier_example successful.");
245 Ok(())
246}
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_mrk_discovery_keyring(
&self,
) -> CreateAwsKmsMrkDiscoveryKeyringFluentBuilder
pub fn create_aws_kms_mrk_discovery_keyring( &self, ) -> CreateAwsKmsMrkDiscoveryKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsMrkDiscoveryKeyring
operation.
- The fluent builder is configurable:
discovery_filter(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::DiscoveryFilter>>)
/set_discovery_filter(Option<crate::deps::aws_cryptography_materialProviders::types::DiscoveryFilter>)
: (undocumented)grant_tokens(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_grant_tokens(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)kms_client(impl Into<Option<crate::deps::com_amazonaws_kms::client::Client>>)
/set_kms_client(Option<crate::deps::com_amazonaws_kms::client::Client>)
: (undocumented)region(impl Into<Option<::std::string::String>>)
/set_region(Option<::std::string::String>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsMrkDiscoveryKeyringError>
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_mrk_discovery_multi_keyring(
&self,
) -> CreateAwsKmsMrkDiscoveryMultiKeyringFluentBuilder
pub fn create_aws_kms_mrk_discovery_multi_keyring( &self, ) -> CreateAwsKmsMrkDiscoveryMultiKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsMrkDiscoveryMultiKeyring
operation.
- The fluent builder is configurable:
client_supplier(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::client_supplier::ClientSupplierRef>>)
/set_client_supplier(Option<crate::deps::aws_cryptography_materialProviders::types::client_supplier::ClientSupplierRef>)
: (undocumented)discovery_filter(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::DiscoveryFilter>>)
/set_discovery_filter(Option<crate::deps::aws_cryptography_materialProviders::types::DiscoveryFilter>)
: (undocumented)grant_tokens(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_grant_tokens(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)regions(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_regions(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsMrkDiscoveryMultiKeyringError>
Examples found in repository?
37pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
38 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
39 let key_arn = test_utils::TEST_MRK_KEY_ID;
40 let account_ids = vec![test_utils::TEST_AWS_ACCOUNT_ID.to_string()];
41 let regions = vec![test_utils::TEST_AWS_REGION.to_string()];
42
43 // 1. Create a single MRK multi-keyring using the key arn.
44 // Although this example demonstrates use of the MRK discovery multi-keyring,
45 // a discovery keyring cannot be used to encrypt. So we will need to construct
46 // a non-discovery keyring for this example to encrypt. For more information on MRK
47 // multi-keyrings, see the MultiMrkKeyringExample in this directory.
48 // Though this is an "MRK multi-keyring", we do not need to provide multiple keys,
49 // and can use single-region KMS keys. We will provide a single key here; this
50 // can be either an MRK or a single-region key.
51 let mpl_config = MaterialProvidersConfig::builder().build()?;
52 let mpl = mpl_client::Client::from_conf(mpl_config)?;
53 let encrypt_keyring = mpl
54 .create_aws_kms_mrk_multi_keyring()
55 .generator(key_arn)
56 .send()
57 .await?;
58
59 // 2. Configure which attributes are encrypted and/or signed when writing new items.
60 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
61 // we must explicitly configure how they should be treated during item encryption:
62 // - ENCRYPT_AND_SIGN: The attribute is encrypted and icncluded in the signature
63 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
64 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
65 let attribute_actions_on_encrypt = HashMap::from([
66 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
67 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
68 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
69 ]);
70
71 // 3. Configure which attributes we expect to be included in the signature
72 // when reading items. There are two options for configuring this:
73 //
74 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
75 // When defining your DynamoDb schema and deciding on attribute names,
76 // choose a distinguishing prefix (such as ":") for all attributes that
77 // you do not want to include in the signature.
78 // This has two main benefits:
79 // - It is easier to reason about the security and authenticity of data within your item
80 // when all unauthenticated data is easily distinguishable by their attribute name.
81 // - If you need to add new unauthenticated attributes in the future,
82 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
83 // and immediately start writing to that new attribute, without
84 // any other configuration update needed.
85 // Once you configure this field, it is not safe to update it.
86 //
87 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
88 // a set of attributes that should be considered unauthenticated when encountered
89 // on read. Be careful if you use this configuration. Do not remove an attribute
90 // name from this configuration, even if you are no longer writing with that attribute,
91 // as old items may still include this attribute, and our configuration needs to know
92 // to continue to exclude this attribute from the signature scope.
93 // If you add new attribute names to this field, you must first deploy the update to this
94 // field to all readers in your host fleet before deploying the update to start writing
95 // with that new attribute.
96 //
97 // For this example, we currently authenticate all attributes. To make it easier to
98 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
99 const UNSIGNED_ATTR_PREFIX: &str = ":";
100
101 // 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
102 let table_config = DynamoDbTableEncryptionConfig::builder()
103 .logical_table_name(ddb_table_name)
104 .partition_key_name("partition_key")
105 .sort_key_name("sort_key")
106 .attribute_actions_on_encrypt(attribute_actions_on_encrypt.clone())
107 .keyring(encrypt_keyring)
108 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
109 .build()?;
110
111 let table_configs = DynamoDbTablesEncryptionConfig::builder()
112 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
113 .build()?;
114
115 // 5. Create a new AWS SDK DynamoDb client using the config above
116 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
117 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
118 .interceptor(DbEsdkInterceptor::new(table_configs)?)
119 .build();
120 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
121
122 // 6. Put an item into our table using the above client.
123 // Before the item gets sent to DynamoDb, it will be encrypted
124 // client-side using the MRK multi-keyring.
125 let item = HashMap::from([
126 (
127 "partition_key".to_string(),
128 AttributeValue::S("awsKmsMrkDiscoveryMultiKeyringItem".to_string()),
129 ),
130 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
131 (
132 "sensitive_data".to_string(),
133 AttributeValue::S("encrypt and sign me!".to_string()),
134 ),
135 ]);
136
137 ddb.put_item()
138 .table_name(ddb_table_name)
139 .set_item(Some(item.clone()))
140 .send()
141 .await?;
142
143 // 7. Construct a discovery filter.
144 // A discovery filter limits the set of encrypted data keys
145 // the keyring can use to decrypt data.
146 // We will only let the keyring use keys in the selected AWS accounts
147 // and in the `aws` partition.
148 // This is the suggested config for most users; for more detailed config, see
149 // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery
150 let discovery_filter = DiscoveryFilter::builder()
151 .partition("aws")
152 .account_ids(account_ids)
153 .build()?;
154
155 // 8. Construct a discovery keyring.
156 // Note that we choose to use the MRK discovery multi-keyring, even though
157 // our original keyring used a single KMS key.
158
159 let decrypt_keyring = mpl
160 .create_aws_kms_mrk_discovery_multi_keyring()
161 .discovery_filter(discovery_filter)
162 .regions(regions)
163 .send()
164 .await?;
165
166 // 9. Create new DDB config and client using the decrypt discovery keyring.
167 // This is the same as the above config, except we pass in the decrypt keyring.
168 let table_config_for_decrypt = DynamoDbTableEncryptionConfig::builder()
169 .logical_table_name(ddb_table_name)
170 .partition_key_name("partition_key")
171 .sort_key_name("sort_key")
172 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
173 .keyring(decrypt_keyring)
174 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
175 .build()?;
176
177 let table_configs_for_decrypt = DynamoDbTablesEncryptionConfig::builder()
178 .table_encryption_configs(HashMap::from([(
179 ddb_table_name.to_string(),
180 table_config_for_decrypt,
181 )]))
182 .build()?;
183
184 let dynamo_config_for_decrypt = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
185 .interceptor(DbEsdkInterceptor::new(table_configs_for_decrypt)?)
186 .build();
187 let ddb_for_decrypt = aws_sdk_dynamodb::Client::from_conf(dynamo_config_for_decrypt);
188
189 // 10. Get the item back from our table using the client.
190 // The client will retrieve encrypted items from the DDB table, then
191 // detect the KMS key that was used to encrypt their data keys.
192 // The client will make a request to KMS to decrypt with the encrypting KMS key.
193 // If the client has permission to decrypt with the KMS key,
194 // the client will decrypt the item client-side using the keyring
195 // and return the original item.
196 let key_to_get = HashMap::from([
197 (
198 "partition_key".to_string(),
199 AttributeValue::S("awsKmsMrkDiscoveryMultiKeyringItem".to_string()),
200 ),
201 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
202 ]);
203
204 let resp = ddb_for_decrypt
205 .get_item()
206 .table_name(ddb_table_name)
207 .set_key(Some(key_to_get))
208 .consistent_read(true)
209 .send()
210 .await?;
211
212 assert_eq!(resp.item, Some(item));
213
214 println!("mrk_discovery_multi_keyring successful.");
215 Ok(())
216}
More examples
37pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
38 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
39 // Note that we pass in an MRK in us-east-1...
40 let key_arn = test_utils::TEST_MRK_REPLICA_KEY_ID_US_EAST_1.to_string();
41 let account_ids = vec![test_utils::TEST_AWS_ACCOUNT_ID.to_string()];
42 // ...and access its replica in eu-west-1
43 let regions = vec!["eu-west-1".to_string()];
44
45 // 1. Create a single MRK multi-keyring.
46 // This can be either a single-region KMS key or an MRK.
47 // For this example to succeed, the key's region must either
48 // 1) be in the regions list, or
49 // 2) the key must be an MRK with a replica defined
50 // in a region in the regions list, and the client
51 // must have the correct permissions to access the replica.
52 let mpl_config = MaterialProvidersConfig::builder().build()?;
53 let mpl = mpl_client::Client::from_conf(mpl_config)?;
54
55 // Create the multi-keyring using our custom client supplier
56 // defined in the RegionalRoleClientSupplier class in this directory.
57 // Note: RegionalRoleClientSupplier will internally use the key_arn's region
58 // to retrieve the correct IAM role.
59
60 let mrk_keyring_with_client_supplier = mpl
61 .create_aws_kms_mrk_multi_keyring()
62 .client_supplier(RegionalRoleClientSupplier {})
63 .generator(key_arn)
64 .send()
65 .await?;
66
67 // 2. Configure which attributes are encrypted and/or signed when writing new items.
68 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
69 // we must explicitly configure how they should be treated during item encryption:
70 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
71 // - SIGN_ONLY: The attribute is not encrypted, but is still included in the signature
72 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
73 let attribute_actions_on_encrypt = HashMap::from([
74 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
75 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
76 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
77 ]);
78
79 // 3. Configure which attributes we expect to be included in the signature
80 // when reading items. There are two options for configuring this:
81 //
82 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
83 // When defining your DynamoDb schema and deciding on attribute names,
84 // choose a distinguishing prefix (such as ":") for all attributes that
85 // you do not want to include in the signature.
86 // This has two main benefits:
87 // - It is easier to reason about the security and authenticity of data within your item
88 // when all unauthenticated data is easily distinguishable by their attribute name.
89 // - If you need to add new unauthenticated attributes in the future,
90 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
91 // and immediately start writing to that new attribute, without
92 // any other configuration update needed.
93 // Once you configure this field, it is not safe to update it.
94 //
95 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
96 // a set of attributes that should be considered unauthenticated when encountered
97 // on read. Be careful if you use this configuration. Do not remove an attribute
98 // name from this configuration, even if you are no longer writing with that attribute,
99 // as old items may still include this attribute, and our configuration needs to know
100 // to continue to exclude this attribute from the signature scope.
101 // If you add new attribute names to this field, you must first deploy the update to this
102 // field to all readers in your host fleet before deploying the update to start writing
103 // with that new attribute.
104 //
105 // For this example, we currently authenticate all attributes. To make it easier to
106 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
107 const UNSIGNED_ATTR_PREFIX: &str = ":";
108
109 // 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
110 let table_config = DynamoDbTableEncryptionConfig::builder()
111 .logical_table_name(ddb_table_name)
112 .partition_key_name("partition_key")
113 .sort_key_name("sort_key")
114 .attribute_actions_on_encrypt(attribute_actions_on_encrypt.clone())
115 .keyring(mrk_keyring_with_client_supplier)
116 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
117 .build()?;
118
119 let table_configs = DynamoDbTablesEncryptionConfig::builder()
120 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
121 .build()?;
122
123 // 5. Create a new AWS SDK DynamoDb client using the DynamoDb Config above
124 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
125 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
126 .interceptor(DbEsdkInterceptor::new(table_configs)?)
127 .build();
128 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
129
130 // 6. Put an item into our table using the above client.
131 // Before the item gets sent to DynamoDb, it will be encrypted
132 // client-side using the MRK multi-keyring.
133 // The data key protecting this item will be encrypted
134 // with all the KMS Keys in this keyring, so that it can be
135 // decrypted with any one of those KMS Keys.
136 let item = HashMap::from([
137 (
138 "partition_key".to_string(),
139 AttributeValue::S("clientSupplierItem".to_string()),
140 ),
141 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
142 (
143 "sensitive_data".to_string(),
144 AttributeValue::S("encrypt and sign me!".to_string()),
145 ),
146 ]);
147
148 ddb.put_item()
149 .table_name(ddb_table_name)
150 .set_item(Some(item.clone()))
151 .send()
152 .await?;
153
154 // 7. Get the item back from our table using the same keyring.
155 // The client will decrypt the item client-side using the MRK
156 // and return the original item.
157 let key_to_get = HashMap::from([
158 (
159 "partition_key".to_string(),
160 AttributeValue::S("clientSupplierItem".to_string()),
161 ),
162 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
163 ]);
164
165 let resp = ddb
166 .get_item()
167 .table_name(ddb_table_name)
168 .set_key(Some(key_to_get.clone()))
169 .consistent_read(true)
170 .send()
171 .await?;
172
173 assert_eq!(
174 resp.item.unwrap()["sensitive_data"],
175 AttributeValue::S("encrypt and sign me!".to_string())
176 );
177
178 // 8. Create a MRK discovery multi-keyring with a custom client supplier.
179 // A discovery MRK multi-keyring will be composed of
180 // multiple discovery MRK keyrings, one for each region.
181 // Each component keyring has its own KMS client in a particular region.
182 // When we provide a client supplier to the multi-keyring, all component
183 // keyrings will use that client supplier configuration.
184 // In our tests, we make `key_arn` an MRK with a replica, and
185 // provide only the replica region in our discovery filter.
186 let discovery_filter = DiscoveryFilter::builder()
187 .partition("aws")
188 .account_ids(account_ids)
189 .build()?;
190
191 let mrk_discovery_client_supplier_keyring = mpl
192 .create_aws_kms_mrk_discovery_multi_keyring()
193 .client_supplier(RegionalRoleClientSupplier {})
194 .discovery_filter(discovery_filter)
195 .regions(regions)
196 .send()
197 .await?;
198
199 // 9. Create a new config and client using the discovery keyring.
200 // This is the same setup as above, except we provide the discovery keyring to the config.
201 let only_replica_table_config = DynamoDbTableEncryptionConfig::builder()
202 .logical_table_name(ddb_table_name)
203 .partition_key_name("partition_key")
204 .sort_key_name("sort_key")
205 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
206 .keyring(mrk_discovery_client_supplier_keyring)
207 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
208 .build()?;
209
210 let only_replica_table_configs = DynamoDbTablesEncryptionConfig::builder()
211 .table_encryption_configs(HashMap::from([(
212 ddb_table_name.to_string(),
213 only_replica_table_config,
214 )]))
215 .build()?;
216
217 let only_replica_dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
218 .interceptor(DbEsdkInterceptor::new(only_replica_table_configs)?)
219 .build();
220 let only_replica_ddb = aws_sdk_dynamodb::Client::from_conf(only_replica_dynamo_config);
221
222 // 10. Get the item back from our table using the discovery keyring client.
223 // The client will decrypt the item client-side using the keyring,
224 // and return the original item.
225 // The discovery keyring will only use KMS keys in the provided regions and
226 // AWS accounts. Since we have provided it with a custom client supplier
227 // which uses different IAM roles based on the key region,
228 // the discovery keyring will use a particular IAM role to decrypt
229 // based on the region of the KMS key it uses to decrypt.
230
231 let resp = only_replica_ddb
232 .get_item()
233 .table_name(ddb_table_name)
234 .set_key(Some(key_to_get))
235 .consistent_read(true)
236 .send()
237 .await?;
238
239 assert_eq!(
240 resp.item.unwrap()["sensitive_data"],
241 AttributeValue::S("encrypt and sign me!".to_string())
242 );
243
244 println!("client_supplier_example successful.");
245 Ok(())
246}
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_hierarchical_keyring(
&self,
) -> CreateAwsKmsHierarchicalKeyringFluentBuilder
pub fn create_aws_kms_hierarchical_keyring( &self, ) -> CreateAwsKmsHierarchicalKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsHierarchicalKeyring
operation.
- The fluent builder is configurable:
branch_key_id(impl Into<Option<::std::string::String>>)
/set_branch_key_id(Option<::std::string::String>)
: (undocumented)branch_key_id_supplier(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::branch_key_id_supplier::BranchKeyIdSupplierRef>>)
/set_branch_key_id_supplier(Option<crate::deps::aws_cryptography_materialProviders::types::branch_key_id_supplier::BranchKeyIdSupplierRef>)
: (undocumented)cache(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::CacheType>>)
/set_cache(Option<crate::deps::aws_cryptography_materialProviders::types::CacheType>)
: (undocumented)key_store(impl Into<Option<crate::deps::aws_cryptography_keyStore::client::Client>>)
/set_key_store(Option<crate::deps::aws_cryptography_keyStore::client::Client>)
: (undocumented)partition_id(impl Into<Option<::std::string::String>>)
/set_partition_id(Option<::std::string::String>)
: (undocumented)ttl_seconds(impl Into<Option<::std::primitive::i64>>)
/set_ttl_seconds(Option<::std::primitive::i64>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsHierarchicalKeyringError>
Examples found in repository?
63pub async fn put_item_get_item(
64 tenant1_branch_key_id: &str,
65 tenant2_branch_key_id: &str,
66) -> Result<(), crate::BoxError> {
67 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
68
69 let keystore_table_name = test_utils::TEST_KEYSTORE_NAME;
70 let logical_keystore_name = test_utils::TEST_LOGICAL_KEYSTORE_NAME;
71 let kms_key_id = test_utils::TEST_KEYSTORE_KMS_KEY_ID;
72
73 // Initial KeyStore Setup: This example requires that you have already
74 // created your KeyStore, and have populated it with two new branch keys.
75 // See the "Create KeyStore Table Example" and "Create KeyStore Key Example"
76 // for an example of how to do this.
77
78 // 1. Configure your KeyStore resource.
79 // This SHOULD be the same configuration that you used
80 // to initially create and populate your KeyStore.
81 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
82 let key_store_config = KeyStoreConfig::builder()
83 .kms_client(aws_sdk_kms::Client::new(&sdk_config))
84 .ddb_client(aws_sdk_dynamodb::Client::new(&sdk_config))
85 .ddb_table_name(keystore_table_name)
86 .logical_key_store_name(logical_keystore_name)
87 .kms_configuration(KmsConfiguration::KmsKeyArn(kms_key_id.to_string()))
88 .build()?;
89
90 let key_store = keystore_client::Client::from_conf(key_store_config)?;
91
92 // 2. Create a Branch Key ID Supplier. See ExampleBranchKeyIdSupplier in this directory.
93 let dbesdk_config = DynamoDbEncryptionConfig::builder().build()?;
94 let dbesdk = dbesdk_client::Client::from_conf(dbesdk_config)?;
95 let supplier = ExampleBranchKeyIdSupplier::new(tenant1_branch_key_id, tenant2_branch_key_id);
96
97 let branch_key_id_supplier = dbesdk
98 .create_dynamo_db_encryption_branch_key_id_supplier()
99 .ddb_key_branch_key_id_supplier(supplier)
100 .send()
101 .await?
102 .branch_key_id_supplier
103 .unwrap();
104
105 // 3. Create the Hierarchical Keyring, using the Branch Key ID Supplier above.
106 // With this configuration, the AWS SDK Client ultimately configured will be capable
107 // of encrypting or decrypting items for either tenant (assuming correct KMS access).
108 // If you want to restrict the client to only encrypt or decrypt for a single tenant,
109 // configure this Hierarchical Keyring using `.branchKeyId(tenant1BranchKeyId)` instead
110 // of `.branchKeyIdSupplier(branchKeyIdSupplier)`.
111 let mpl_config = MaterialProvidersConfig::builder().build()?;
112 let mpl = mpl_client::Client::from_conf(mpl_config)?;
113
114 let hierarchical_keyring = mpl
115 .create_aws_kms_hierarchical_keyring()
116 .branch_key_id_supplier(branch_key_id_supplier)
117 .key_store(key_store)
118 .ttl_seconds(600)
119 .send()
120 .await?;
121
122 // 4. Configure which attributes are encrypted and/or signed when writing new items.
123 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
124 // we must explicitly configure how they should be treated during item encryption:
125 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
126 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
127 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
128 let attribute_actions_on_encrypt = HashMap::from([
129 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
130 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
131 (
132 "tenant_sensitive_data".to_string(),
133 CryptoAction::EncryptAndSign,
134 ),
135 ]);
136
137 // 5. Configure which attributes we expect to be included in the signature
138 // when reading items. There are two options for configuring this:
139 //
140 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
141 // When defining your DynamoDb schema and deciding on attribute names,
142 // choose a distinguishing prefix (such as ":") for all attributes that
143 // you do not want to include in the signature.
144 // This has two main benefits:
145 // - It is easier to reason about the security and authenticity of data within your item
146 // when all unauthenticated data is easily distinguishable by their attribute name.
147 // - If you need to add new unauthenticated attributes in the future,
148 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
149 // and immediately start writing to that new attribute, without
150 // any other configuration update needed.
151 // Once you configure this field, it is not safe to update it.
152 //
153 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
154 // a set of attributes that should be considered unauthenticated when encountered
155 // on read. Be careful if you use this configuration. Do not remove an attribute
156 // name from this configuration, even if you are no longer writing with that attribute,
157 // as old items may still include this attribute, and our configuration needs to know
158 // to continue to exclude this attribute from the signature scope.
159 // If you add new attribute names to this field, you must first deploy the update to this
160 // field to all readers in your host fleet before deploying the update to start writing
161 // with that new attribute.
162 //
163 // For this example, we currently authenticate all attributes. To make it easier to
164 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
165 const UNSIGNED_ATTR_PREFIX: &str = ":";
166
167 // 6. Create the DynamoDb Encryption configuration for the table we will be writing to.
168 let table_config = DynamoDbTableEncryptionConfig::builder()
169 .logical_table_name(ddb_table_name)
170 .partition_key_name("partition_key")
171 .sort_key_name("sort_key")
172 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
173 .keyring(hierarchical_keyring)
174 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
175 .build()?;
176
177 let table_configs = DynamoDbTablesEncryptionConfig::builder()
178 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
179 .build()?;
180
181 // 7. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above
182 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
183 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
184 .interceptor(DbEsdkInterceptor::new(table_configs)?)
185 .build();
186 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
187
188 // 8. Put an item into our table using the above client.
189 // Before the item gets sent to DynamoDb, it will be encrypted
190 // client-side, according to our configuration.
191 // Because the item we are writing uses "tenantId1" as our partition value,
192 // based on the code we wrote in the ExampleBranchKeySupplier,
193 // `tenant1BranchKeyId` will be used to encrypt this item.
194 let item = HashMap::from([
195 (
196 "partition_key".to_string(),
197 AttributeValue::S("tenant1Id".to_string()),
198 ),
199 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
200 (
201 "tenant_sensitive_data".to_string(),
202 AttributeValue::S("encrypt and sign me!".to_string()),
203 ),
204 ]);
205
206 ddb.put_item()
207 .table_name(ddb_table_name)
208 .set_item(Some(item.clone()))
209 .send()
210 .await?;
211
212 // 9. Get the item back from our table using the same client.
213 // The client will decrypt the item client-side, and return
214 // back the original item.
215 // Because the returned item's partition value is "tenantId1",
216 // based on the code we wrote in the ExampleBranchKeySupplier,
217 // `tenant1BranchKeyId` will be used to decrypt this item.
218 let key_to_get = HashMap::from([
219 (
220 "partition_key".to_string(),
221 AttributeValue::S("tenant1Id".to_string()),
222 ),
223 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
224 ]);
225
226 let resp = ddb
227 .get_item()
228 .table_name(ddb_table_name)
229 .set_key(Some(key_to_get))
230 .consistent_read(true)
231 .send()
232 .await?;
233
234 assert_eq!(resp.item, Some(item));
235 println!("hierarchical_keyring successful.");
236 Ok(())
237}
More examples
60pub async fn put_and_query_with_beacon(branch_key_id: &str) -> Result<(), crate::BoxError> {
61 let ddb_table_name = test_utils::UNIT_INSPECTION_TEST_DDB_TABLE_NAME;
62 let branch_key_wrapping_kms_key_arn = test_utils::TEST_BRANCH_KEY_WRAPPING_KMS_KEY_ARN;
63 let branch_key_ddb_table_name = test_utils::TEST_BRANCH_KEYSTORE_DDB_TABLE_NAME;
64
65 // 1. Create Beacons.
66 // These are the same beacons as in the "BasicSearchableEncryptionExample" in this directory.
67 // See that file to see details on beacon construction and parameters.
68 // While we will not directly query against these beacons,
69 // you must create standard beacons on encrypted fields
70 // that we wish to use in compound beacons.
71 let last4_beacon = StandardBeacon::builder()
72 .name("inspector_id_last4")
73 .length(10)
74 .build()?;
75
76 let unit_beacon = StandardBeacon::builder().name("unit").length(30).build()?;
77
78 let standard_beacon_list = vec![last4_beacon, unit_beacon];
79
80 // 2. Define encrypted parts.
81 // Encrypted parts define the beacons that can be used to construct a compound beacon,
82 // and how the compound beacon prefixes those beacon values.
83
84 // A encrypted part must receive:
85 // - name: Name of a standard beacon
86 // - prefix: Any string. This is plaintext that prefixes the beaconized value in the compound beacon.
87 // Prefixes must be unique across the configuration, and must not be a prefix of another prefix;
88 // i.e. for all configured prefixes, the first N characters of a prefix must not equal another prefix.
89 // In practice, it is suggested to have a short value distinguishable from other parts served on the prefix.
90 // For this example, we will choose "L-" as the prefix for "Last 4 digits of inspector ID".
91 // With this prefix and the standard beacon's bit length definition (10), the beaconized
92 // version of the inspector ID's last 4 digits will appear as
93 // `L-000` to `L-3ff` inside a compound beacon.
94
95 // For this example, we will choose "U-" as the prefix for "unit".
96 // With this prefix and the standard beacon's bit length definition (30), a unit beacon will appear
97 // as `U-00000000` to `U-3fffffff` inside a compound beacon.
98 let encrypted_parts_list = vec![
99 EncryptedPart::builder()
100 .name("inspector_id_last4")
101 .prefix("L-")
102 .build()?,
103 EncryptedPart::builder().name("unit").prefix("U-").build()?,
104 ];
105
106 // 3. Define compound beacon.
107 // A compound beacon allows one to serve multiple beacons or attributes from a single index.
108 // A compound beacon must receive:
109 // - name: The name of the beacon. Compound beacon values will be written to `aws_ddb_e_[name]`.
110 // - split: A character separating parts in a compound beacon
111 // A compound beacon may also receive:
112 // - encrypted: A list of encrypted parts. This is effectively a list of beacons. We provide the list
113 // that we created above.
114 // - constructors: A list of constructors. This is an ordered list of possible ways to create a beacon.
115 // We have not defined any constructors here; see the complex example for how to do this.
116 // The client will provide a default constructor, which will write a compound beacon as:
117 // all signed parts in the order they are added to the signed list;
118 // all encrypted parts in order they are added to the encrypted list; all parts required.
119 // In this example, we expect compound beacons to be written as
120 // `L-XXX.U-YYYYYYYY`, since our encrypted list looks like
121 // [last4EncryptedPart, unitEncryptedPart].
122 // - signed: A list of signed parts, i.e. plaintext attributes. This would be provided if we
123 // wanted to use plaintext values as part of constructing our compound beacon. We do not
124 // provide this here; see the Complex example for an example.
125 let compound_beacon_list = vec![CompoundBeacon::builder()
126 .name("last4UnitCompound")
127 .split(".")
128 .encrypted(encrypted_parts_list)
129 .build()?];
130
131 // 4. Configure the Keystore
132 // These are the same constructions as in the Basic example, which describes these in more detail.
133
134 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
135 let key_store_config = KeyStoreConfig::builder()
136 .kms_client(aws_sdk_kms::Client::new(&sdk_config))
137 .ddb_client(aws_sdk_dynamodb::Client::new(&sdk_config))
138 .ddb_table_name(branch_key_ddb_table_name)
139 .logical_key_store_name(branch_key_ddb_table_name)
140 .kms_configuration(KmsConfiguration::KmsKeyArn(
141 branch_key_wrapping_kms_key_arn.to_string(),
142 ))
143 .build()?;
144
145 let key_store = keystore_client::Client::from_conf(key_store_config)?;
146
147 // 5. Create BeaconVersion.
148 // This is similar to the Basic example, except we have also provided a compoundBeaconList.
149 // We must also continue to provide all of the standard beacons that compose a compound beacon list.
150 let beacon_version = BeaconVersion::builder()
151 .standard_beacons(standard_beacon_list)
152 .compound_beacons(compound_beacon_list)
153 .version(1) // MUST be 1
154 .key_store(key_store.clone())
155 .key_source(BeaconKeySource::Single(
156 SingleKeyStore::builder()
157 // `keyId` references a beacon key.
158 // For every branch key we create in the keystore,
159 // we also create a beacon key.
160 // This beacon key is not the same as the branch key,
161 // but is created with the same ID as the branch key.
162 .key_id(branch_key_id)
163 .cache_ttl(6000)
164 .build()?,
165 ))
166 .build()?;
167 let beacon_versions = vec![beacon_version];
168
169 // 6. Create a Hierarchical Keyring
170 // This is the same configuration as in the Basic example.
171
172 let mpl_config = MaterialProvidersConfig::builder().build()?;
173 let mpl = mpl_client::Client::from_conf(mpl_config)?;
174 let kms_keyring = mpl
175 .create_aws_kms_hierarchical_keyring()
176 .branch_key_id(branch_key_id)
177 .key_store(key_store)
178 .ttl_seconds(6000)
179 .send()
180 .await?;
181
182 // 7. Configure which attributes are encrypted and/or signed when writing new items.
183 let attribute_actions_on_encrypt = HashMap::from([
184 ("work_id".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
185 ("inspection_date".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
186 (
187 "inspector_id_last4".to_string(),
188 CryptoAction::EncryptAndSign,
189 ), // Beaconized attributes must be encrypted
190 ("unit".to_string(), CryptoAction::EncryptAndSign), // Beaconized attributes must be encrypted
191 ]);
192
193 // We do not need to define a crypto action on last4UnitCompound.
194 // We only need to define crypto actions on attributes that we pass to PutItem.
195
196 // 8. Create the DynamoDb Encryption configuration for the table we will be writing to.
197 // The beaconVersions are added to the search configuration.
198 let table_config = DynamoDbTableEncryptionConfig::builder()
199 .logical_table_name(ddb_table_name)
200 .partition_key_name("work_id")
201 .sort_key_name("inspection_date")
202 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
203 .keyring(kms_keyring)
204 .search(
205 SearchConfig::builder()
206 .write_version(1) // MUST be 1
207 .versions(beacon_versions)
208 .build()?,
209 )
210 .build()?;
211
212 // 9. Create config
213 let encryption_config = DynamoDbTablesEncryptionConfig::builder()
214 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
215 .build()?;
216
217 // 10. Create an item with both attributes used in the compound beacon.
218 let item = HashMap::from([
219 (
220 "work_id".to_string(),
221 AttributeValue::S("9ce39272-8068-4efd-a211-cd162ad65d4c".to_string()),
222 ),
223 (
224 "inspection_date".to_string(),
225 AttributeValue::S("2023-06-13".to_string()),
226 ),
227 (
228 "inspector_id_last4".to_string(),
229 AttributeValue::S("5678".to_string()),
230 ),
231 (
232 "unit".to_string(),
233 AttributeValue::S("011899988199".to_string()),
234 ),
235 ]);
236
237 // 11. If developing or debugging, verify config by checking compound beacon values directly
238 let trans = transform_client::Client::from_conf(encryption_config.clone())?;
239 let resolve_output = trans
240 .resolve_attributes()
241 .table_name(ddb_table_name)
242 .item(item.clone())
243 .version(1)
244 .send()
245 .await?;
246
247 // Verify that there are no virtual fields
248 assert_eq!(resolve_output.virtual_fields.unwrap().len(), 0);
249
250 // Verify that CompoundBeacons has the expected value
251 let compound_beacons = resolve_output.compound_beacons.unwrap();
252 assert_eq!(compound_beacons.len(), 1);
253 assert_eq!(
254 compound_beacons["last4UnitCompound"],
255 "L-5678.U-011899988199"
256 );
257 // Note : the compound beacon actually stored in the table is not "L-5678.U-011899988199"
258 // but rather something like "L-abc.U-123", as both parts are EncryptedParts
259 // and therefore the text is replaced by the associated beacon
260
261 // 12. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above
262 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
263 .interceptor(DbEsdkInterceptor::new(encryption_config)?)
264 .build();
265 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
266
267 // 13. Write the item to the table
268 ddb.put_item()
269 .table_name(ddb_table_name)
270 .set_item(Some(item.clone()))
271 .send()
272 .await?;
273
274 // 14. Query for the item we just put.
275 let expression_attribute_values = HashMap::from([
276 // This query expression takes a few factors into consideration:
277 // - The configured prefix for the last 4 digits of an inspector ID is "L-";
278 // the prefix for the unit is "U-"
279 // - The configured split character, separating component parts, is "."
280 // - The default constructor adds encrypted parts in the order they are in the encrypted list, which
281 // configures `last4` to come before `unit``
282 // NOTE: We did not need to create a compound beacon for this query. This query could have also been
283 // done by querying on the partition and sort key, as was done in the Basic example.
284 // This is intended to be a simple example to demonstrate how one might set up a compound beacon.
285 // For examples where compound beacons are required, see the Complex example.
286 // The most basic extension to this example that would require a compound beacon would add a third
287 // part to the compound beacon, then query against three parts.
288 (
289 ":value".to_string(),
290 AttributeValue::S("L-5678.U-011899988199".to_string()),
291 ),
292 ]);
293
294 // GSIs are sometimes a little bit delayed, so we retry if the query comes up empty.
295 for _i in 0..10 {
296 let query_response = ddb
297 .query()
298 .table_name(ddb_table_name)
299 .index_name(GSI_NAME)
300 .key_condition_expression("last4UnitCompound = :value")
301 .set_expression_attribute_values(Some(expression_attribute_values.clone()))
302 .send()
303 .await?;
304
305 // if no results, sleep and try again
306 if query_response.items.is_none() || query_response.items.as_ref().unwrap().is_empty() {
307 std::thread::sleep(std::time::Duration::from_millis(20));
308 continue;
309 }
310
311 let attribute_values = query_response.items.unwrap();
312 // Validate only 1 item was returned: the item we just put
313 assert_eq!(attribute_values.len(), 1);
314 let returned_item = &attribute_values[0];
315 // Validate the item has the expected attributes
316 assert_eq!(
317 returned_item["inspector_id_last4"],
318 AttributeValue::S("5678".to_string())
319 );
320 assert_eq!(
321 returned_item["unit"],
322 AttributeValue::S("011899988199".to_string())
323 );
324 break;
325 }
326 println!("compound_beacon_searchable_encryption successful.");
327 Ok(())
328}
56pub async fn put_and_query_with_beacon(branch_key_id: &str) -> Result<(), crate::BoxError> {
57 let ddb_table_name = test_utils::UNIT_INSPECTION_TEST_DDB_TABLE_NAME;
58 let branch_key_wrapping_kms_key_arn = test_utils::TEST_BRANCH_KEY_WRAPPING_KMS_KEY_ARN;
59 let branch_key_ddb_table_name = test_utils::TEST_BRANCH_KEYSTORE_DDB_TABLE_NAME;
60
61 // 1. Create Beacons.
62 let standard_beacon_list = vec![
63 // The fruit beacon allows searching on the encrypted fruit attribute
64 // We have selected 30 as an example beacon length, but you should go to
65 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/choosing-beacon-length.html
66 // when creating your beacons.
67 StandardBeacon::builder().name("fruit").length(30).build()?,
68 // The basket beacon allows searching on the encrypted basket attribute
69 // Basket is used as a Set, and therefore needs a beacon style to reflect that.
70 // Further, we need to be able to compare the items in basket to the fruit attribute
71 // so we `share` this beacon with `fruit`.
72 // Since we need both of these things, we use the SharedSet style.
73 StandardBeacon::builder()
74 .name("basket")
75 .length(30)
76 .style(BeaconStyle::SharedSet(
77 SharedSet::builder().other("fruit").build()?,
78 ))
79 .build()?,
80 // The dessert beacon allows searching on the encrypted dessert attribute
81 // We need to be able to compare the dessert attribute to the fruit attribute
82 // so we `share` this beacon with `fruit`.
83 StandardBeacon::builder()
84 .name("dessert")
85 .length(30)
86 .style(BeaconStyle::Shared(
87 Shared::builder().other("fruit").build()?,
88 ))
89 .build()?,
90 // The veggieBeacon allows searching on the encrypted veggies attribute
91 // veggies is used as a Set, and therefore needs a beacon style to reflect that.
92 StandardBeacon::builder()
93 .name("veggies")
94 .length(30)
95 .style(BeaconStyle::AsSet(AsSet::builder().build()?))
96 .build()?,
97 // The work_typeBeacon allows searching on the encrypted work_type attribute
98 // We only use it as part of the compound work_unit beacon,
99 // so we disable its use as a standalone beacon
100 StandardBeacon::builder()
101 .name("work_type")
102 .length(30)
103 .style(BeaconStyle::PartOnly(PartOnly::builder().build()?))
104 .build()?,
105 ];
106
107 // Here we build a compound beacon from work_id and work_type
108 // If we had tried to make a StandardBeacon from work_type, we would have seen an error
109 // because work_type is "PartOnly"
110 let encrypted_part_list = vec![EncryptedPart::builder()
111 .name("work_type")
112 .prefix("T-")
113 .build()?];
114
115 let signed_part_list = vec![SignedPart::builder().name("work_id").prefix("I-").build()?];
116
117 let compound_beacon_list = vec![CompoundBeacon::builder()
118 .name("work_unit")
119 .split(".")
120 .encrypted(encrypted_part_list)
121 .signed(signed_part_list)
122 .build()?];
123
124 // 2. Configure the Keystore
125 // These are the same constructions as in the Basic example, which describes these in more detail.
126 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
127 let key_store_config = KeyStoreConfig::builder()
128 .kms_client(aws_sdk_kms::Client::new(&sdk_config))
129 .ddb_client(aws_sdk_dynamodb::Client::new(&sdk_config))
130 .ddb_table_name(branch_key_ddb_table_name)
131 .logical_key_store_name(branch_key_ddb_table_name)
132 .kms_configuration(KmsConfiguration::KmsKeyArn(
133 branch_key_wrapping_kms_key_arn.to_string(),
134 ))
135 .build()?;
136
137 let key_store = keystore_client::Client::from_conf(key_store_config)?;
138
139 // 3. Create BeaconVersion.
140 // This is similar to the Basic example
141 let beacon_version = BeaconVersion::builder()
142 .standard_beacons(standard_beacon_list)
143 .compound_beacons(compound_beacon_list)
144 .version(1) // MUST be 1
145 .key_store(key_store.clone())
146 .key_source(BeaconKeySource::Single(
147 SingleKeyStore::builder()
148 // `keyId` references a beacon key.
149 // For every branch key we create in the keystore,
150 // we also create a beacon key.
151 // This beacon key is not the same as the branch key,
152 // but is created with the same ID as the branch key.
153 .key_id(branch_key_id)
154 .cache_ttl(6000)
155 .build()?,
156 ))
157 .build()?;
158 let beacon_versions = vec![beacon_version];
159
160 // 4. Create a Hierarchical Keyring
161 // This is the same configuration as in the Basic example.
162 let mpl_config = MaterialProvidersConfig::builder().build()?;
163 let mpl = mpl_client::Client::from_conf(mpl_config)?;
164 let kms_keyring = mpl
165 .create_aws_kms_hierarchical_keyring()
166 .branch_key_id(branch_key_id)
167 .key_store(key_store)
168 .ttl_seconds(6000)
169 .send()
170 .await?;
171
172 // 5. Configure which attributes are encrypted and/or signed when writing new items.
173 let attribute_actions_on_encrypt = HashMap::from([
174 ("work_id".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
175 ("inspection_date".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
176 ("dessert".to_string(), CryptoAction::EncryptAndSign), // Beaconized attributes must be encrypted
177 ("fruit".to_string(), CryptoAction::EncryptAndSign), // Beaconized attributes must be encrypted
178 ("basket".to_string(), CryptoAction::EncryptAndSign), // Beaconized attributes must be encrypted
179 ("veggies".to_string(), CryptoAction::EncryptAndSign), // Beaconized attributes must be encrypted
180 ("work_type".to_string(), CryptoAction::EncryptAndSign), // Beaconized attributes must be encrypted
181 ]);
182
183 // 6. Create the DynamoDb Encryption configuration for the table we will be writing to.
184 // The beaconVersions are added to the search configuration.
185 let table_config = DynamoDbTableEncryptionConfig::builder()
186 .logical_table_name(ddb_table_name)
187 .partition_key_name("work_id")
188 .sort_key_name("inspection_date")
189 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
190 .keyring(kms_keyring)
191 .search(
192 SearchConfig::builder()
193 .write_version(1) // MUST be 1
194 .versions(beacon_versions)
195 .build()?,
196 )
197 .build()?;
198
199 // 7. Create config
200 let encryption_config = DynamoDbTablesEncryptionConfig::builder()
201 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
202 .build()?;
203
204 // 8. Create item one, specifically with "dessert != fruit", and "fruit in basket".
205 let item1 = HashMap::from([
206 ("work_id".to_string(), AttributeValue::S("1".to_string())),
207 (
208 "inspection_date".to_string(),
209 AttributeValue::S("2023-06-13".to_string()),
210 ),
211 ("dessert".to_string(), AttributeValue::S("cake".to_string())),
212 ("fruit".to_string(), AttributeValue::S("banana".to_string())),
213 (
214 "basket".to_string(),
215 AttributeValue::Ss(vec![
216 "banana".to_string(),
217 "apple".to_string(),
218 "pear".to_string(),
219 ]),
220 ),
221 (
222 "veggies".to_string(),
223 AttributeValue::Ss(vec![
224 "beans".to_string(),
225 "carrots".to_string(),
226 "celery".to_string(),
227 ]),
228 ),
229 (
230 "work_type".to_string(),
231 AttributeValue::S("small".to_string()),
232 ),
233 ]);
234
235 // 9. Create item two, specifically with "dessert == fruit", and "fruit not in basket".
236 let item2 = HashMap::from([
237 ("work_id".to_string(), AttributeValue::S("2".to_string())),
238 (
239 "inspection_date".to_string(),
240 AttributeValue::S("2023-06-13".to_string()),
241 ),
242 (
243 "dessert".to_string(),
244 AttributeValue::S("orange".to_string()),
245 ),
246 ("fruit".to_string(), AttributeValue::S("orange".to_string())),
247 (
248 "basket".to_string(),
249 AttributeValue::Ss(vec![
250 "strawberry".to_string(),
251 "blueberry".to_string(),
252 "blackberry".to_string(),
253 ]),
254 ),
255 (
256 "veggies".to_string(),
257 AttributeValue::Ss(vec![
258 "beans".to_string(),
259 "carrots".to_string(),
260 "peas".to_string(),
261 ]),
262 ),
263 (
264 "work_type".to_string(),
265 AttributeValue::S("large".to_string()),
266 ),
267 ]);
268
269 // 10. Create a new AWS SDK DynamoDb client using the DynamoDb Config above
270 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
271 .interceptor(DbEsdkInterceptor::new(encryption_config)?)
272 .build();
273 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
274
275 // 11. Add the two items
276 ddb.put_item()
277 .table_name(ddb_table_name)
278 .set_item(Some(item1.clone()))
279 .send()
280 .await?;
281
282 ddb.put_item()
283 .table_name(ddb_table_name)
284 .set_item(Some(item2.clone()))
285 .send()
286 .await?;
287
288 // 12. Test the first type of Set operation :
289 // Select records where the basket attribute holds a particular value
290 let expression_attribute_values = HashMap::from([(
291 ":value".to_string(),
292 AttributeValue::S("banana".to_string()),
293 )]);
294
295 let scan_response = ddb
296 .scan()
297 .table_name(ddb_table_name)
298 .filter_expression("contains(basket, :value)")
299 .set_expression_attribute_values(Some(expression_attribute_values.clone()))
300 .send()
301 .await?;
302
303 let attribute_values = scan_response.items.unwrap();
304 // Validate only 1 item was returned: item1
305 assert_eq!(attribute_values.len(), 1);
306 let returned_item = &attribute_values[0];
307 // Validate the item has the expected attributes
308 assert_eq!(returned_item["work_id"], item1["work_id"]);
309
310 // 13. Test the second type of Set operation :
311 // Select records where the basket attribute holds the fruit attribute
312 let scan_response = ddb
313 .scan()
314 .table_name(ddb_table_name)
315 .filter_expression("contains(basket, fruit)")
316 .send()
317 .await?;
318
319 let attribute_values = scan_response.items.unwrap();
320 // Validate only 1 item was returned: item1
321 assert_eq!(attribute_values.len(), 1);
322 let returned_item = &attribute_values[0];
323 // Validate the item has the expected attributes
324 assert_eq!(returned_item["work_id"], item1["work_id"]);
325
326 // 14. Test the third type of Set operation :
327 // Select records where the fruit attribute exists in a particular set
328 let basket3 = vec![
329 "boysenberry".to_string(),
330 "orange".to_string(),
331 "grape".to_string(),
332 ];
333 let expression_attribute_values =
334 HashMap::from([(":value".to_string(), AttributeValue::Ss(basket3))]);
335
336 let scan_response = ddb
337 .scan()
338 .table_name(ddb_table_name)
339 .filter_expression("contains(:value, fruit)")
340 .set_expression_attribute_values(Some(expression_attribute_values.clone()))
341 .send()
342 .await?;
343
344 let attribute_values = scan_response.items.unwrap();
345 // Validate only 1 item was returned: item1
346 assert_eq!(attribute_values.len(), 1);
347 let returned_item = &attribute_values[0];
348 // Validate the item has the expected attributes
349 assert_eq!(returned_item["work_id"], item2["work_id"]);
350
351 // 15. Test a Shared search. Select records where the dessert attribute matches the fruit attribute
352 let scan_response = ddb
353 .scan()
354 .table_name(ddb_table_name)
355 .filter_expression("dessert = fruit")
356 .send()
357 .await?;
358
359 let attribute_values = scan_response.items.unwrap();
360 // Validate only 1 item was returned: item1
361 assert_eq!(attribute_values.len(), 1);
362 let returned_item = &attribute_values[0];
363 // Validate the item has the expected attributes
364 assert_eq!(returned_item["work_id"], item2["work_id"]);
365
366 // 15. Test the AsSet attribute 'veggies' :
367 // Select records where the veggies attribute holds a particular value
368 let expression_attribute_values =
369 HashMap::from([(":value".to_string(), AttributeValue::S("peas".to_string()))]);
370
371 let scan_response = ddb
372 .scan()
373 .table_name(ddb_table_name)
374 .filter_expression("contains(veggies, :value)")
375 .set_expression_attribute_values(Some(expression_attribute_values.clone()))
376 .send()
377 .await?;
378
379 let attribute_values = scan_response.items.unwrap();
380 // Validate only 1 item was returned: item1
381 assert_eq!(attribute_values.len(), 1);
382 let returned_item = &attribute_values[0];
383 // Validate the item has the expected attributes
384 assert_eq!(returned_item["work_id"], item2["work_id"]);
385
386 // 16. Test the compound beacon 'work_unit' :
387 let expression_attribute_values = HashMap::from([(
388 ":value".to_string(),
389 AttributeValue::S("I-1.T-small".to_string()),
390 )]);
391
392 let scan_response = ddb
393 .scan()
394 .table_name(ddb_table_name)
395 .filter_expression("work_unit = :value")
396 .set_expression_attribute_values(Some(expression_attribute_values.clone()))
397 .send()
398 .await?;
399
400 let attribute_values = scan_response.items.unwrap();
401 // Validate only 1 item was returned: item1
402 assert_eq!(attribute_values.len(), 1);
403 let returned_item = &attribute_values[0];
404 // Validate the item has the expected attributes
405 assert_eq!(returned_item["work_id"], item1["work_id"]);
406
407 println!("beacon_styles_searchable_encryption successful.");
408 Ok(())
409}
119pub async fn put_and_query_with_beacon(branch_key_id: &str) -> Result<(), crate::BoxError> {
120 let ddb_table_name = test_utils::SIMPLE_BEACON_TEST_DDB_TABLE_NAME;
121 let branch_key_wrapping_kms_key_arn = test_utils::TEST_BRANCH_KEY_WRAPPING_KMS_KEY_ARN;
122 let branch_key_ddb_table_name = test_utils::TEST_BRANCH_KEYSTORE_DDB_TABLE_NAME;
123
124 // 1. Construct a length-1 prefix virtual transform.
125 // `hasTestResult` is a binary attribute, containing either `true` or `false`.
126 // As an example to demonstrate virtual transforms, we will truncate the value
127 // of `hasTestResult` in the virtual field to the length-1 prefix of the binary value, i.e.:
128 // - "true" -> "t"
129 // - "false -> "f"
130 // This is not necessary. This is done as a demonstration of virtual transforms.
131 // Virtual transform operations treat all attributes as strings
132 // (i.e. the boolean value `true` is interpreted as a string "true"),
133 // so its length-1 prefix is just "t".
134
135 let length1_prefix_virtual_transform_list = vec![VirtualTransform::Prefix(
136 GetPrefix::builder().length(1).build()?,
137 )];
138
139 // 2. Construct the VirtualParts required for the VirtualField
140 let has_test_result_part = VirtualPart::builder()
141 .loc("hasTestResult")
142 .trans(length1_prefix_virtual_transform_list)
143 .build()?;
144
145 let state_part = VirtualPart::builder().loc("state").build()?;
146 // Note that we do not apply any transform to the `state` attribute,
147 // and the virtual field will read in the attribute as-is.
148
149 // 3. Construct the VirtualField from the VirtualParts
150 // Note that the order that virtual parts are added to the virtualPartList
151 // dictates the order in which they are concatenated to build the virtual field.
152 // You must add virtual parts in the same order on write as you do on read.
153 let virtual_part_list = vec![state_part, has_test_result_part];
154
155 let state_and_has_test_result_field = VirtualField::builder()
156 .name("stateAndHasTestResult")
157 .parts(virtual_part_list)
158 .build()?;
159
160 let virtual_field_list = vec![state_and_has_test_result_field];
161
162 // 4. Configure our beacon.
163 // The virtual field is assumed to hold a US 2-letter state abbreviation
164 // (56 possible values = 50 states + 6 territories) concatenated with a binary attribute
165 // (2 possible values: true/false hasTestResult field), we expect a population size of
166 // 56 * 2 = 112 possible values.
167 // We will also assume that these values are reasonably well-distributed across
168 // customer IDs. In practice, this will not be true. We would expect
169 // more populous states to appear more frequently in the database.
170 // A more complex analysis would show that a stricter upper bound
171 // is necessary to account for this by hiding information from the
172 // underlying distribution.
173 //
174 // This link provides guidance for choosing a beacon length:
175 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/choosing-beacon-length.html
176 // We follow the guidance in the link above to determine reasonable bounds for beacon length:
177 // - min: log(sqrt(112))/log(2) ~= 3.4, round down to 3
178 // - max: log((112/2))/log(2) ~= 5.8, round up to 6
179 // You will somehow need to round results to a nearby integer.
180 // We choose to round to the nearest integer; you might consider a different rounding approach.
181 // Rounding up will return fewer expected "false positives" in queries,
182 // leading to fewer decrypt calls and better performance,
183 // but it is easier to identify which beacon values encode distinct plaintexts.
184 // Rounding down will return more expected "false positives" in queries,
185 // leading to more decrypt calls and worse performance,
186 // but it is harder to identify which beacon values encode distinct plaintexts.
187 // We can choose a beacon length between 3 and 6:
188 // - Closer to 3, we expect more "false positives" to be returned,
189 // making it harder to identify which beacon values encode distinct plaintexts,
190 // but leading to more decrypt calls and worse performance
191 // - Closer to 6, we expect fewer "false positives" returned in queries,
192 // leading to fewer decrypt calls and better performance,
193 // but it is easier to identify which beacon values encode distinct plaintexts.
194 // As an example, we will choose 5.
195 // Values stored in aws_dbe_b_stateAndHasTestResult will be 5 bits long (0x00 - 0x1f)
196 // There will be 2^5 = 32 possible HMAC values.
197 // With a well-distributed dataset (112 values), for a particular beacon we expect
198 // (112/32) = 3.5 combinations of abbreviation + true/false attribute
199 // sharing that beacon value.
200 let standard_beacon_list = vec![StandardBeacon::builder()
201 .name("stateAndHasTestResult")
202 .length(5)
203 .build()?];
204
205 // 5. Configure Keystore.
206 // This example expects that you have already set up a KeyStore with a single branch key.
207 // See the "CreateKeyStoreTableExample" and "CreateKeyStoreKeyExample" files for how to do this.
208 // After you create a branch key, you should persist its ID for use in this example.
209 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
210 let key_store_config = KeyStoreConfig::builder()
211 .kms_client(aws_sdk_kms::Client::new(&sdk_config))
212 .ddb_client(aws_sdk_dynamodb::Client::new(&sdk_config))
213 .ddb_table_name(branch_key_ddb_table_name)
214 .logical_key_store_name(branch_key_ddb_table_name)
215 .kms_configuration(KmsConfiguration::KmsKeyArn(
216 branch_key_wrapping_kms_key_arn.to_string(),
217 ))
218 .build()?;
219
220 let key_store = keystore_client::Client::from_conf(key_store_config)?;
221
222 // 6. Create BeaconVersion.
223 // The BeaconVersion inside the list holds the list of beacons on the table.
224 // The BeaconVersion also stores information about the keystore.
225 // BeaconVersion must be provided:
226 // - keyStore: The keystore configured in the previous step.
227 // - keySource: A configuration for the key source.
228 // For simple use cases, we can configure a 'singleKeySource' which
229 // statically configures a single beaconKey. That is the approach this example takes.
230 // For use cases where you want to use different beacon keys depending on the data
231 // (for example if your table holds data for multiple tenants, and you want to use
232 // a different beacon key per tenant), look into configuring a MultiKeyStore:
233 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/searchable-encryption-multitenant.html
234 // We also provide our standard beacon list and virtual fields here.
235 let beacon_version = BeaconVersion::builder()
236 .standard_beacons(standard_beacon_list)
237 .virtual_fields(virtual_field_list)
238 .version(1) // MUST be 1
239 .key_store(key_store.clone())
240 .key_source(BeaconKeySource::Single(
241 SingleKeyStore::builder()
242 // `keyId` references a beacon key.
243 // For every branch key we create in the keystore,
244 // we also create a beacon key.
245 // This beacon key is not the same as the branch key,
246 // but is created with the same ID as the branch key.
247 .key_id(branch_key_id)
248 .cache_ttl(6000)
249 .build()?,
250 ))
251 .build()?;
252 let beacon_versions = vec![beacon_version];
253
254 // 7. Create a Hierarchical Keyring
255 // This is a KMS keyring that utilizes the keystore table.
256 // This config defines how items are encrypted and decrypted.
257 // NOTE: You should configure this to use the same keystore as your search config.
258 let mpl_config = MaterialProvidersConfig::builder().build()?;
259 let mpl = mpl_client::Client::from_conf(mpl_config)?;
260 let kms_keyring = mpl
261 .create_aws_kms_hierarchical_keyring()
262 .branch_key_id(branch_key_id)
263 .key_store(key_store)
264 .ttl_seconds(6000)
265 .send()
266 .await?;
267
268 // 8. Configure which attributes are encrypted and/or signed when writing new items.
269 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
270 // we must explicitly configure how they should be treated during item encryption:
271 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
272 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
273 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
274 // Any attributes that will be used in beacons must be configured as ENCRYPT_AND_SIGN.
275 let attribute_actions_on_encrypt = HashMap::from([
276 ("customer_id".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
277 ("create_time".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
278 ("state".to_string(), CryptoAction::EncryptAndSign), // Beaconized attributes must be encrypted
279 ("hasTestResult".to_string(), CryptoAction::EncryptAndSign), // Beaconized attributes must be encrypted
280 ]);
281
282 // 9. Create the DynamoDb Encryption configuration for the table we will be writing to.
283 // The beaconVersions are added to the search configuration.
284 let table_config = DynamoDbTableEncryptionConfig::builder()
285 .logical_table_name(ddb_table_name)
286 .partition_key_name("customer_id")
287 .sort_key_name("create_time")
288 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
289 .keyring(kms_keyring)
290 .search(
291 SearchConfig::builder()
292 .write_version(1) // MUST be 1
293 .versions(beacon_versions)
294 .build()?,
295 )
296 .build()?;
297
298 // 10. Create config
299 let encryption_config = DynamoDbTablesEncryptionConfig::builder()
300 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
301 .build()?;
302
303 // 11. Create test items
304
305 // Create item with hasTestResult=true
306 let item_with_has_test_result = HashMap::from([
307 (
308 "customer_id".to_string(),
309 AttributeValue::S("ABC-123".to_string()),
310 ),
311 (
312 "create_time".to_string(),
313 AttributeValue::N("1681495205".to_string()),
314 ),
315 ("state".to_string(), AttributeValue::S("CA".to_string())),
316 ("hasTestResult".to_string(), AttributeValue::Bool(true)),
317 ]);
318
319 // Create item with hasTestResult=false
320 let item_with_no_has_test_result = HashMap::from([
321 (
322 "customer_id".to_string(),
323 AttributeValue::S("DEF-456".to_string()),
324 ),
325 (
326 "create_time".to_string(),
327 AttributeValue::N("1681495205".to_string()),
328 ),
329 ("state".to_string(), AttributeValue::S("CA".to_string())),
330 ("hasTestResult".to_string(), AttributeValue::Bool(false)),
331 ]);
332
333 // 12. If developing or debugging, verify config by checking virtual field values directly
334 let trans = transform_client::Client::from_conf(encryption_config.clone())?;
335 let resolve_output = trans
336 .resolve_attributes()
337 .table_name(ddb_table_name)
338 .item(item_with_has_test_result.clone())
339 .version(1)
340 .send()
341 .await?;
342
343 // CompoundBeacons is empty because we have no Compound Beacons configured
344 assert_eq!(resolve_output.compound_beacons.unwrap().len(), 0);
345
346 // Verify that VirtualFields has the expected value
347 let virtual_fields = resolve_output.virtual_fields.unwrap();
348 assert_eq!(virtual_fields.len(), 1);
349 assert_eq!(virtual_fields["stateAndHasTestResult"], "CAt");
350
351 // 13. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above
352 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
353 .interceptor(DbEsdkInterceptor::new(encryption_config)?)
354 .build();
355 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
356
357 // 14. Put two items into our table using the above client.
358 // The two items will differ only in their `customer_id` attribute (primary key)
359 // and their `hasTestResult` attribute.
360 // We will query against these items to demonstrate how to use our setup above
361 // to query against our `stateAndHasTestResult` beacon.
362 // Before the item gets sent to DynamoDb, it will be encrypted
363 // client-side, according to our configuration.
364 // Since our configuration includes a beacon on a virtual field named
365 // `stateAndHasTestResult`, the client will add an attribute
366 // to the item with name `aws_dbe_b_stateAndHasTestResult`.
367 // Its value will be an HMAC truncated to as many bits as the
368 // beacon's `length` parameter; i.e. 5.
369
370 ddb.put_item()
371 .table_name(ddb_table_name)
372 .set_item(Some(item_with_has_test_result.clone()))
373 .send()
374 .await?;
375
376 ddb.put_item()
377 .table_name(ddb_table_name)
378 .set_item(Some(item_with_no_has_test_result.clone()))
379 .send()
380 .await?;
381
382 // 15. Query by stateAndHasTestResult attribute.
383 // Note that we are constructing the query as if we were querying on plaintext values.
384 // However, the DDB encryption client will detect that this attribute name has a beacon configured.
385 // The client will add the beaconized attribute name and attribute value to the query,
386 // and transform the query to use the beaconized name and value.
387 // Internally, the client will query for and receive all items with a matching HMAC value in the beacon field.
388 // This may include a number of "false positives" with different ciphertext, but the same truncated HMAC.
389 // e.g. if truncate(HMAC("CAt"), 5) == truncate(HMAC("DCf"), 5), the query will return both items.
390 // The client will decrypt all returned items to determine which ones have the expected attribute values,
391 // and only surface items with the correct plaintext to the user.
392 // This procedure is internal to the client and is abstracted away from the user;
393 // e.g. the user will only see "CAt" and never "DCf", though the actual query returned both.
394 let expression_attribute_values = HashMap::from([
395 // We are querying for the item with `state`="CA" and `hasTestResult`=`true`.
396 // Since we added virtual parts as `state` then `hasTestResult`,
397 // we must write our query expression in the same order.
398 // We constructed our virtual field as `state`+`hasTestResult`,
399 // so we add the two parts in that order.
400 // Since we also created a virtual transform that truncated `hasTestResult`
401 // to its length-1 prefix, i.e. "true" -> "t",
402 // we write that field as its length-1 prefix in the query.
403 (
404 ":stateAndHasTestResult".to_string(),
405 AttributeValue::S("CAt".to_string()),
406 ),
407 ]);
408
409 // GSIs are sometimes a little bit delayed, so we retry if the query comes up empty.
410 for _i in 0..10 {
411 let query_response = ddb
412 .query()
413 .table_name(ddb_table_name)
414 .index_name(GSI_NAME)
415 .key_condition_expression("stateAndHasTestResult = :stateAndHasTestResult")
416 .set_expression_attribute_values(Some(expression_attribute_values.clone()))
417 .send()
418 .await?;
419
420 // if no results, sleep and try again
421 if query_response.items.is_none() || query_response.items.as_ref().unwrap().is_empty() {
422 std::thread::sleep(std::time::Duration::from_millis(20));
423 continue;
424 }
425
426 let attribute_values = query_response.items.unwrap();
427 // Validate only 1 item was returned: the item we just put
428 assert_eq!(attribute_values.len(), 1);
429 let returned_item = &attribute_values[0];
430 // Validate the item has the expected attributes
431 assert_eq!(returned_item["state"], AttributeValue::S("CA".to_string()));
432 assert_eq!(returned_item["hasTestResult"], AttributeValue::Bool(true));
433 break;
434 }
435 println!("virtual_beacon_searchable_encryption successful.");
436 Ok(())
437}
54pub async fn put_and_query_with_beacon(branch_key_id: &str) -> Result<(), crate::BoxError> {
55 // The whole thing is wrapped in a future to ensure that everything is Send and Sync
56 let future = async move {
57 let ddb_table_name = test_utils::UNIT_INSPECTION_TEST_DDB_TABLE_NAME;
58 let branch_key_wrapping_kms_key_arn = test_utils::TEST_BRANCH_KEY_WRAPPING_KMS_KEY_ARN;
59 let branch_key_ddb_table_name = test_utils::TEST_BRANCH_KEYSTORE_DDB_TABLE_NAME;
60
61 // 1. Configure Beacons.
62 // The beacon name must be the name of a table attribute that will be encrypted.
63 // The `length` parameter dictates how many bits are in the beacon attribute value.
64 // The following link provides guidance on choosing a beacon length:
65 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/choosing-beacon-length.html
66
67 // The configured DDB table has a GSI on the `aws_dbe_b_inspector_id_last4` AttributeName.
68 // This field holds the last 4 digits of an inspector ID.
69 // For our example, this field may range from 0 to 9,999 (10,000 possible values).
70 // For our example, we assume a full inspector ID is an integer
71 // ranging from 0 to 99,999,999. We do not assume that the full inspector ID's
72 // values are uniformly distributed across its range of possible values.
73 // In many use cases, the prefix of an identifier encodes some information
74 // about that identifier (e.g. zipcode and SSN prefixes encode geographic
75 // information), while the suffix does not and is more uniformly distributed.
76 // We will assume that the inspector ID field matches a similar use case.
77 // So for this example, we only store and use the last
78 // 4 digits of the inspector ID, which we assume is uniformly distributed.
79 // Since the full ID's range is divisible by the range of the last 4 digits,
80 // then the last 4 digits of the inspector ID are uniformly distributed
81 // over the range from 0 to 9,999.
82 // See our documentation for why you should avoid creating beacons over non-uniform distributions
83 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/searchable-encryption.html#are-beacons-right-for-me
84 // A single inspector ID suffix may be assigned to multiple `work_id`s.
85 //
86 // This link provides guidance for choosing a beacon length:
87 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/choosing-beacon-length.html
88 // We follow the guidance in the link above to determine reasonable bounds
89 // for the length of a beacon on the last 4 digits of an inspector ID:
90 // - min: log(sqrt(10,000))/log(2) ~= 6.6, round up to 7
91 // - max: log((10,000/2))/log(2) ~= 12.3, round down to 12
92 // You will somehow need to round results to a nearby integer.
93 // We choose to round to the nearest integer; you might consider a different rounding approach.
94 // Rounding up will return fewer expected "false positives" in queries,
95 // leading to fewer decrypt calls and better performance,
96 // but it is easier to identify which beacon values encode distinct plaintexts.
97 // Rounding down will return more expected "false positives" in queries,
98 // leading to more decrypt calls and worse performance,
99 // but it is harder to identify which beacon values encode distinct plaintexts.
100 // We can choose a beacon length between 7 and 12:
101 // - Closer to 7, we expect more "false positives" to be returned,
102 // making it harder to identify which beacon values encode distinct plaintexts,
103 // but leading to more decrypt calls and worse performance
104 // - Closer to 12, we expect fewer "false positives" returned in queries,
105 // leading to fewer decrypt calls and better performance,
106 // but it is easier to identify which beacon values encode distinct plaintexts.
107 // As an example, we will choose 10.
108 //
109 // Values stored in aws_dbe_b_inspector_id_last4 will be 10 bits long (0x000 - 0x3ff)
110 // There will be 2^10 = 1,024 possible HMAC values.
111 // With a sufficiently large number of well-distributed inspector IDs,
112 // for a particular beacon we expect (10,000/1,024) ~= 9.8 4-digit inspector ID suffixes
113 // sharing that beacon value.
114 let last4_beacon = StandardBeacon::builder()
115 .name("inspector_id_last4")
116 .length(10)
117 .build()?;
118
119 // The configured DDB table has a GSI on the `aws_dbe_b_unit` AttributeName.
120 // This field holds a unit serial number.
121 // For this example, this is a 12-digit integer from 0 to 999,999,999,999 (10^12 possible values).
122 // We will assume values for this attribute are uniformly distributed across this range.
123 // A single unit serial number may be assigned to multiple `work_id`s.
124 //
125 // This link provides guidance for choosing a beacon length:
126 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/choosing-beacon-length.html
127 // We follow the guidance in the link above to determine reasonable bounds
128 // for the length of a beacon on a unit serial number:
129 // - min: log(sqrt(999,999,999,999))/log(2) ~= 19.9, round up to 20
130 // - max: log((999,999,999,999/2))/log(2) ~= 38.9, round up to 39
131 // We can choose a beacon length between 20 and 39:
132 // - Closer to 20, we expect more "false positives" to be returned,
133 // making it harder to identify which beacon values encode distinct plaintexts,
134 // but leading to more decrypt calls and worse performance
135 // - Closer to 39, we expect fewer "false positives" returned in queries,
136 // leading to fewer decrypt calls and better performance,
137 // but it is easier to identify which beacon values encode distinct plaintexts.
138 // As an example, we will choose 30.
139 //
140 // Values stored in aws_dbe_b_unit will be 30 bits long (0x00000000 - 0x3fffffff)
141 // There will be 2^30 = 1,073,741,824 ~= 1.1B possible HMAC values.
142 // With a sufficiently large number of well-distributed inspector IDs,
143 // for a particular beacon we expect (10^12/2^30) ~= 931.3 unit serial numbers
144 // sharing that beacon value.
145 let unit_beacon = StandardBeacon::builder().name("unit").length(30).build()?;
146
147 let standard_beacon_list = vec![last4_beacon, unit_beacon];
148
149 // 2. Configure Keystore.
150 // The keystore is a separate DDB table where the client stores encryption and decryption materials.
151 // In order to configure beacons on the DDB client, you must configure a keystore.
152 //
153 // This example expects that you have already set up a KeyStore with a single branch key.
154 // See the "Create KeyStore Table Example" and "Create KeyStore Key Example" for how to do this.
155 // After you create a branch key, you should persist its ID for use in this example.
156 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
157 let key_store_config = KeyStoreConfig::builder()
158 .kms_client(aws_sdk_kms::Client::new(&sdk_config))
159 .ddb_client(aws_sdk_dynamodb::Client::new(&sdk_config))
160 .ddb_table_name(branch_key_ddb_table_name)
161 .logical_key_store_name(branch_key_ddb_table_name)
162 .kms_configuration(KmsConfiguration::KmsKeyArn(
163 branch_key_wrapping_kms_key_arn.to_string(),
164 ))
165 .build()?;
166
167 let key_store = keystore_client::Client::from_conf(key_store_config)?;
168
169 // 3. Create BeaconVersion.
170 // The BeaconVersion inside the list holds the list of beacons on the table.
171 // The BeaconVersion also stores information about the keystore.
172 // BeaconVersion must be provided:
173 // - keyStore: The keystore configured in step 2.
174 // - keySource: A configuration for the key source.
175 // For simple use cases, we can configure a 'singleKeySource' which
176 // statically configures a single beaconKey. That is the approach this example takes.
177 // For use cases where you want to use different beacon keys depending on the data
178 // (for example if your table holds data for multiple tenants, and you want to use
179 // a different beacon key per tenant), look into configuring a MultiKeyStore:
180 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/searchable-encryption-multitenant.html
181
182 let beacon_version = BeaconVersion::builder()
183 .standard_beacons(standard_beacon_list)
184 .version(1) // MUST be 1
185 .key_store(key_store.clone())
186 .key_source(BeaconKeySource::Single(
187 SingleKeyStore::builder()
188 // `keyId` references a beacon key.
189 // For every branch key we create in the keystore,
190 // we also create a beacon key.
191 // This beacon key is not the same as the branch key,
192 // but is created with the same ID as the branch key.
193 .key_id(branch_key_id)
194 .cache_ttl(6000)
195 .build()?,
196 ))
197 .build()?;
198 let beacon_versions = vec![beacon_version];
199
200 // 4. Create a Hierarchical Keyring
201 // This is a KMS keyring that utilizes the keystore table.
202 // This config defines how items are encrypted and decrypted.
203 // NOTE: You should configure this to use the same keystore as your search config.
204 let provider_config = MaterialProvidersConfig::builder().build()?;
205 let mat_prov = client::Client::from_conf(provider_config)?;
206 let kms_keyring = mat_prov
207 .create_aws_kms_hierarchical_keyring()
208 .branch_key_id(branch_key_id)
209 .key_store(key_store)
210 .ttl_seconds(6000)
211 .send()
212 .await?;
213
214 // 5. Configure which attributes are encrypted and/or signed when writing new items.
215 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
216 // we must explicitly configure how they should be treated during item encryption:
217 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
218 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
219 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
220 // Any attributes that will be used in beacons must be configured as ENCRYPT_AND_SIGN.
221 let attribute_actions_on_encrypt = HashMap::from([
222 ("work_id".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
223 ("inspection_date".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
224 (
225 "inspector_id_last4".to_string(),
226 CryptoAction::EncryptAndSign,
227 ), // Beaconized attributes must be encrypted
228 ("unit".to_string(), CryptoAction::EncryptAndSign), // Beaconized attributes must be encrypted
229 ]);
230
231 // 6. Create the DynamoDb Encryption configuration for the table we will be writing to.
232 // The beaconVersions are added to the search configuration.
233 let table_config = DynamoDbTableEncryptionConfig::builder()
234 .logical_table_name(ddb_table_name)
235 .partition_key_name("work_id")
236 .sort_key_name("inspection_date")
237 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
238 .keyring(kms_keyring)
239 .search(
240 SearchConfig::builder()
241 .write_version(1) // MUST be 1
242 .versions(beacon_versions)
243 .build()?,
244 )
245 .build()?;
246
247 let table_configs = DynamoDbTablesEncryptionConfig::builder()
248 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
249 .build()?;
250
251 // 7. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs
252 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
253 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
254 .interceptor(DbEsdkInterceptor::new(table_configs)?)
255 .build();
256 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
257
258 // 8. Put an item into our table using the above client.
259 // Before the item gets sent to DynamoDb, it will be encrypted
260 // client-side, according to our configuration.
261 // Since our configuration includes beacons for `inspector_id_last4` and `unit`,
262 // the client will add two additional attributes to the item. These attributes will have names
263 // `aws_dbe_b_inspector_id_last4` and `aws_dbe_b_unit`. Their values will be HMACs
264 // truncated to as many bits as the beacon's `length` parameter; e.g.
265 // aws_dbe_b_inspector_id_last4 = truncate(HMAC("4321"), 10)
266 // aws_dbe_b_unit = truncate(HMAC("123456789012"), 30)
267
268 let item = HashMap::from([
269 (
270 "work_id".to_string(),
271 AttributeValue::S("1313ba89-5661-41eb-ba6c-cb1b4cb67b2d".to_string()),
272 ),
273 (
274 "inspection_date".to_string(),
275 AttributeValue::S("2023-06-13".to_string()),
276 ),
277 (
278 "inspector_id_last4".to_string(),
279 AttributeValue::S("4321".to_string()),
280 ),
281 (
282 "unit".to_string(),
283 AttributeValue::S("123456789012".to_string()),
284 ),
285 ]);
286
287 ddb.put_item()
288 .table_name(ddb_table_name)
289 .set_item(Some(item.clone()))
290 .send()
291 .await?;
292
293 // 9. Query for the item we just put.
294 // Note that we are constructing the query as if we were querying on plaintext values.
295 // However, the DDB encryption client will detect that this attribute name has a beacon configured.
296 // The client will add the beaconized attribute name and attribute value to the query,
297 // and transform the query to use the beaconized name and value.
298 // Internally, the client will query for and receive all items with a matching HMAC value in the beacon field.
299 // This may include a number of "false positives" with different ciphertext, but the same truncated HMAC.
300 // e.g. if truncate(HMAC("123456789012"), 30)
301 // == truncate(HMAC("098765432109"), 30),
302 // the query will return both items.
303 // The client will decrypt all returned items to determine which ones have the expected attribute values,
304 // and only surface items with the correct plaintext to the user.
305 // This procedure is internal to the client and is abstracted away from the user;
306 // e.g. the user will only see "123456789012" and never
307 // "098765432109", though the actual query returned both.
308 let expression_attributes_names = HashMap::from([
309 ("#last4".to_string(), "inspector_id_last4".to_string()),
310 ("#unit".to_string(), "unit".to_string()),
311 ]);
312
313 let expression_attribute_values = HashMap::from([
314 (":last4".to_string(), AttributeValue::S("4321".to_string())),
315 (
316 ":unit".to_string(),
317 AttributeValue::S("123456789012".to_string()),
318 ),
319 ]);
320
321 // GSIs do not update instantly
322 // so if the results come back empty
323 // we retry after a short sleep
324 for _i in 0..10 {
325 let query_response = ddb
326 .query()
327 .table_name(ddb_table_name)
328 .index_name(GSI_NAME)
329 .key_condition_expression("#last4 = :last4 and #unit = :unit")
330 .set_expression_attribute_names(Some(expression_attributes_names.clone()))
331 .set_expression_attribute_values(Some(expression_attribute_values.clone()))
332 .send()
333 .await?;
334
335 // if no results, sleep and try again
336 if query_response.items.is_none() || query_response.items.as_ref().unwrap().is_empty() {
337 std::thread::sleep(std::time::Duration::from_millis(20));
338 continue;
339 }
340
341 let attribute_values = query_response.items.unwrap();
342 // Validate only 1 item was returned: the item we just put
343 assert_eq!(attribute_values.len(), 1);
344 let returned_item = &attribute_values[0];
345 // Validate the item has the expected attributes
346 assert_eq!(
347 returned_item["inspector_id_last4"],
348 AttributeValue::S("4321".to_string())
349 );
350 assert_eq!(
351 returned_item["unit"],
352 AttributeValue::S("123456789012".to_string())
353 );
354 break;
355 }
356 println!("basic_searchable_encryption successful.");
357 Ok(())
358 };
359 future.await
360}
39pub async fn setup_beacon_config(
40 ddb_table_name: &str,
41 branch_key_id: &str,
42 branch_key_wrapping_kms_key_arn: &str,
43 branch_key_ddb_table_name: &str,
44) -> Result<aws_sdk_dynamodb::Client, crate::BoxError> {
45 // 1. Create keystore and branch key
46 // These are the same constructions as in the Basic examples, which describe this in more detail.
47 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
48 let key_store_config = KeyStoreConfig::builder()
49 .kms_client(aws_sdk_kms::Client::new(&sdk_config))
50 .ddb_client(aws_sdk_dynamodb::Client::new(&sdk_config))
51 .ddb_table_name(branch_key_ddb_table_name)
52 .logical_key_store_name(branch_key_ddb_table_name)
53 .kms_configuration(KmsConfiguration::KmsKeyArn(
54 branch_key_wrapping_kms_key_arn.to_string(),
55 ))
56 .build()?;
57
58 let key_store = keystore_client::Client::from_conf(key_store_config)?;
59
60 // 2. Create standard beacons
61 // For this example, we use a standard beacon length of 4.
62 // The BasicSearchableEncryptionExample gives a more thorough consideration of beacon length.
63 // For production applications, one should always exercise rigor when deciding beacon length, including
64 // examining population size and considering performance.
65 let standard_beacon_list = vec![
66 StandardBeacon::builder()
67 .name("EmployeeID")
68 .length(4)
69 .build()?,
70 StandardBeacon::builder()
71 .name("TicketNumber")
72 .length(4)
73 .build()?,
74 StandardBeacon::builder()
75 .name("ProjectName")
76 .length(4)
77 .build()?,
78 StandardBeacon::builder()
79 .name("EmployeeEmail")
80 .length(4)
81 .build()?,
82 StandardBeacon::builder()
83 .name("CreatorEmail")
84 .length(4)
85 .build()?,
86 StandardBeacon::builder()
87 .name("ProjectStatus")
88 .length(4)
89 .build()?,
90 StandardBeacon::builder()
91 .name("OrganizerEmail")
92 .length(4)
93 .build()?,
94 StandardBeacon::builder()
95 .name("ManagerEmail")
96 .length(4)
97 .build()?,
98 StandardBeacon::builder()
99 .name("AssigneeEmail")
100 .length(4)
101 .build()?,
102 StandardBeacon::builder()
103 .name("Severity")
104 .length(4)
105 .build()?,
106 StandardBeacon::builder()
107 .name("City")
108 .loc("Location.City")
109 .length(4)
110 .build()?,
111 StandardBeacon::builder()
112 .name("Building")
113 .loc("Location.Building")
114 .length(4)
115 .build()?,
116 StandardBeacon::builder()
117 .name("Floor")
118 .loc("Location.Floor")
119 .length(4)
120 .build()?,
121 StandardBeacon::builder()
122 .name("Room")
123 .loc("Location.Room")
124 .length(4)
125 .build()?,
126 StandardBeacon::builder()
127 .name("Desk")
128 .loc("Location.Desk")
129 .length(4)
130 .build()?,
131 ];
132
133 // 3. Define encrypted parts
134 // Note that some of the prefixes are modified from the suggested prefixes in Demo.md.
135 // This is because all prefixes must be unique in a configuration.
136 // Encrypted parts are described in more detail in the CompoundBeaconSearchableEncryptionExample.
137 let encrypted_parts_list = vec![
138 EncryptedPart::builder()
139 .name("EmployeeID")
140 .prefix("E-")
141 .build()?,
142 EncryptedPart::builder()
143 .name("TicketNumber")
144 .prefix("T-")
145 .build()?,
146 EncryptedPart::builder()
147 .name("ProjectName")
148 .prefix("P-")
149 .build()?,
150 EncryptedPart::builder()
151 .name("EmployeeEmail")
152 .prefix("EE-")
153 .build()?,
154 EncryptedPart::builder()
155 .name("CreatorEmail")
156 .prefix("CE-")
157 .build()?,
158 EncryptedPart::builder()
159 .name("OrganizerEmail")
160 .prefix("OE-")
161 .build()?,
162 EncryptedPart::builder()
163 .name("ManagerEmail")
164 .prefix("ME-")
165 .build()?,
166 EncryptedPart::builder()
167 .name("AssigneeEmail")
168 .prefix("AE-")
169 .build()?,
170 EncryptedPart::builder()
171 .name("ProjectStatus")
172 .prefix("PSts-")
173 .build()?,
174 EncryptedPart::builder().name("City").prefix("C-").build()?,
175 EncryptedPart::builder()
176 .name("Severity")
177 .prefix("S-")
178 .build()?,
179 EncryptedPart::builder()
180 .name("Building")
181 .prefix("B-")
182 .build()?,
183 EncryptedPart::builder()
184 .name("Floor")
185 .prefix("F-")
186 .build()?,
187 EncryptedPart::builder().name("Room").prefix("R-").build()?,
188 EncryptedPart::builder().name("Desk").prefix("D-").build()?,
189 ];
190
191 // 4. Define signed parts
192 // These are unencrypted attributes we would like to use in beacon queries.
193 // In this example, all of these represent dates or times.
194 // Keeping these attributes unencrypted allows us to use them in comparison-based queries. If a signed
195 // part is the first part in a compound beacon, then that part can be used in comparison for sorting.
196 let signed_parts_list = vec![
197 SignedPart::builder()
198 .name("TicketModTime")
199 .prefix("M-")
200 .build()?,
201 SignedPart::builder()
202 .name("MeetingStart")
203 .prefix("MS-")
204 .build()?,
205 SignedPart::builder()
206 .name("TimeCardStart")
207 .prefix("TC-")
208 .build()?,
209 SignedPart::builder()
210 .name("ProjectStart")
211 .prefix("PS-")
212 .build()?,
213 ];
214
215 // 5. Create constructor parts
216 // Constructor parts are used to assemble constructors (constructors described more in next step).
217 // For each attribute that will be used in a constructor, there must be a corresponding constructor part.
218 // A constructor part must receive:
219 // - name: Name of a standard beacon
220 // - required: Whether this attribute must be present in the item to match a constructor
221 // In this example, we will define each constructor part once and re-use it across multiple constructors.
222 // The parts below are defined by working backwards from the constructors in "PK Constructors",
223 // "SK constructors", etc. sections in Demo.md.
224 let employee_id_constructor_part = ConstructorPart::builder()
225 .name("EmployeeID")
226 .required(true)
227 .build()?;
228 let ticket_number_constructor_part = ConstructorPart::builder()
229 .name("TicketNumber")
230 .required(true)
231 .build()?;
232 let project_name_constructor_part = ConstructorPart::builder()
233 .name("ProjectName")
234 .required(true)
235 .build()?;
236 let ticket_mod_time_constructor_part = ConstructorPart::builder()
237 .name("TicketModTime")
238 .required(true)
239 .build()?;
240 let meeting_start_constructor_part = ConstructorPart::builder()
241 .name("MeetingStart")
242 .required(true)
243 .build()?;
244 let timecard_start_constructor_part = ConstructorPart::builder()
245 .name("TimeCardStart")
246 .required(true)
247 .build()?;
248 let employee_email_constructor_part = ConstructorPart::builder()
249 .name("EmployeeEmail")
250 .required(true)
251 .build()?;
252 let creator_email_constructor_part = ConstructorPart::builder()
253 .name("CreatorEmail")
254 .required(true)
255 .build()?;
256 let project_status_constructor_part = ConstructorPart::builder()
257 .name("ProjectStatus")
258 .required(true)
259 .build()?;
260 let organizer_email_constructor_part = ConstructorPart::builder()
261 .name("OrganizerEmail")
262 .required(true)
263 .build()?;
264 let project_start_constructor_part = ConstructorPart::builder()
265 .name("ProjectStart")
266 .required(true)
267 .build()?;
268 let manager_email_constructor_part = ConstructorPart::builder()
269 .name("ManagerEmail")
270 .required(true)
271 .build()?;
272 let assignee_email_constructor_part = ConstructorPart::builder()
273 .name("AssigneeEmail")
274 .required(true)
275 .build()?;
276 let city_constructor_part = ConstructorPart::builder()
277 .name("City")
278 .required(true)
279 .build()?;
280 let severity_constructor_part = ConstructorPart::builder()
281 .name("Severity")
282 .required(true)
283 .build()?;
284 let building_constructor_part = ConstructorPart::builder()
285 .name("Building")
286 .required(true)
287 .build()?;
288 let floor_constructor_part = ConstructorPart::builder()
289 .name("Floor")
290 .required(true)
291 .build()?;
292 let room_constructor_part = ConstructorPart::builder()
293 .name("Room")
294 .required(true)
295 .build()?;
296 let desk_constructor_part = ConstructorPart::builder()
297 .name("Desk")
298 .required(true)
299 .build()?;
300
301 // 6. Define constructors
302 // Constructors define how encrypted and signed parts are assembled into compound beacons.
303 // The constructors below are based off of the "PK Constructors", "SK constructors", etc. sections in Demo.md.
304
305 // The employee ID constructor only requires an employee ID.
306 // If an item has an attribute with name "EmployeeID", it will match this constructor.
307 // If this is the first matching constructor in the constructor list (constructor list described more below),
308 // the compound beacon will use this constructor, and the compound beacon will be written as `E-X`.
309
310 let employee_id_constructor = Constructor::builder()
311 .parts(vec![employee_id_constructor_part])
312 .build()?;
313 let ticket_number_constructor = Constructor::builder()
314 .parts(vec![ticket_number_constructor_part])
315 .build()?;
316 let project_name_constructor = Constructor::builder()
317 .parts(vec![project_name_constructor_part])
318 .build()?;
319 let ticket_mod_time_constructor = Constructor::builder()
320 .parts(vec![ticket_mod_time_constructor_part])
321 .build()?;
322 let building_constructor = Constructor::builder()
323 .parts(vec![building_constructor_part.clone()])
324 .build()?;
325
326 // This constructor requires all of "MeetingStart", "Location.Floor", and "Location.Room" attributes.
327 // If an item has all of these attributes, it will match this constructor.
328 // If this is the first matching constructor in the constructor list (constructor list described more below),
329 // the compound beacon will use this constructor, and the compound beacon will be written as `MS-X~F-Y~R-Z`.
330 // In a constructor with multiple constructor parts, the order the constructor parts are added to
331 // the constructor part list defines how the compound beacon is written.
332 // We can rearrange the beacon parts by changing the order the constructors were added to the list.
333 let meeting_start_floor_room_constructor = Constructor::builder()
334 .parts(vec![
335 meeting_start_constructor_part,
336 floor_constructor_part.clone(),
337 room_constructor_part,
338 ])
339 .build()?;
340
341 let timecard_start_constructor = Constructor::builder()
342 .parts(vec![timecard_start_constructor_part.clone()])
343 .build()?;
344 let timecard_start_employee_email_constructor = Constructor::builder()
345 .parts(vec![
346 timecard_start_constructor_part,
347 employee_email_constructor_part.clone(),
348 ])
349 .build()?;
350 let creator_email_constructor = Constructor::builder()
351 .parts(vec![creator_email_constructor_part])
352 .build()?;
353 let project_status_constructor = Constructor::builder()
354 .parts(vec![project_status_constructor_part])
355 .build()?;
356 let employee_email_constructor = Constructor::builder()
357 .parts(vec![employee_email_constructor_part])
358 .build()?;
359 let organizer_email_constructor = Constructor::builder()
360 .parts(vec![organizer_email_constructor_part])
361 .build()?;
362 let project_start_constructor = Constructor::builder()
363 .parts(vec![project_start_constructor_part])
364 .build()?;
365 let manager_email_constructor = Constructor::builder()
366 .parts(vec![manager_email_constructor_part])
367 .build()?;
368 let assignee_email_constructor = Constructor::builder()
369 .parts(vec![assignee_email_constructor_part])
370 .build()?;
371 let city_constructor = Constructor::builder()
372 .parts(vec![city_constructor_part])
373 .build()?;
374 let severity_constructor = Constructor::builder()
375 .parts(vec![severity_constructor_part])
376 .build()?;
377 let building_floor_desk_constructor = Constructor::builder()
378 .parts(vec![
379 building_constructor_part,
380 floor_constructor_part,
381 desk_constructor_part,
382 ])
383 .build()?;
384
385 // 7. Add constructors to the compound beacon constructor list in desired construction order
386 // In a compound beacon with multiple constructors, the order the constructors are added to
387 // the constructor list determines their priority.
388 // The first constructor added to a constructor list will be the first constructor that is executed.
389 // The client will evaluate constructors until one matches, and will use the first one that matches.
390 // If no constructors match, an attribute value is not written for that beacon.
391 // A general strategy is to add constructors with unique conditions at the beginning of the list,
392 // and add constructors with general conditions at the end of the list. This would allow a given
393 // item to trigger the constructor most specific to its attributes.
394 let pk0_constructor_list = vec![
395 employee_id_constructor.clone(),
396 building_constructor,
397 ticket_number_constructor,
398 project_name_constructor.clone(),
399 ];
400 let sk0_constructor_list = vec![
401 ticket_mod_time_constructor.clone(),
402 meeting_start_floor_room_constructor.clone(),
403 timecard_start_employee_email_constructor,
404 project_name_constructor,
405 employee_id_constructor.clone(),
406 ];
407 let pk1_constructor_list = vec![
408 creator_email_constructor,
409 employee_email_constructor,
410 project_status_constructor,
411 organizer_email_constructor,
412 ];
413 let sk1_constructor_list = vec![
414 meeting_start_floor_room_constructor,
415 timecard_start_constructor,
416 ticket_mod_time_constructor.clone(),
417 project_start_constructor,
418 employee_id_constructor,
419 ];
420 let pk2_constructor_list = vec![manager_email_constructor, assignee_email_constructor];
421 let pk3_constructor_list = vec![city_constructor, severity_constructor];
422 let sk3_constructor_list = vec![building_floor_desk_constructor, ticket_mod_time_constructor];
423
424 // 8. Define compound beacons
425 // Compound beacon construction is defined in more detail in CompoundBeaconSearchableEncryptionExample.
426 // Note that the split character must be a character that is not used in any attribute value.
427 let compound_beacon_list = vec![
428 CompoundBeacon::builder()
429 .name("PK")
430 .split("~")
431 .constructors(pk0_constructor_list)
432 .build()?,
433 CompoundBeacon::builder()
434 .name("SK")
435 .split("~")
436 .constructors(sk0_constructor_list)
437 .build()?,
438 CompoundBeacon::builder()
439 .name("PK1")
440 .split("~")
441 .constructors(pk1_constructor_list)
442 .build()?,
443 CompoundBeacon::builder()
444 .name("SK1")
445 .split("~")
446 .constructors(sk1_constructor_list)
447 .build()?,
448 CompoundBeacon::builder()
449 .name("PK2")
450 .split("~")
451 .constructors(pk2_constructor_list)
452 .build()?,
453 CompoundBeacon::builder()
454 .name("PK3")
455 .split("~")
456 .constructors(pk3_constructor_list)
457 .build()?,
458 CompoundBeacon::builder()
459 .name("SK3")
460 .split("~")
461 .constructors(sk3_constructor_list)
462 .build()?,
463 ];
464
465 // 9. Create BeaconVersion
466 let beacon_versions = BeaconVersion::builder()
467 .standard_beacons(standard_beacon_list)
468 .compound_beacons(compound_beacon_list)
469 .encrypted_parts(encrypted_parts_list)
470 .signed_parts(signed_parts_list)
471 .version(1)
472 .key_store(key_store.clone())
473 .key_source(BeaconKeySource::Single(
474 SingleKeyStore::builder()
475 .key_id(branch_key_id)
476 .cache_ttl(6000)
477 .build()?,
478 ))
479 .build()?;
480 let beacon_versions = vec![beacon_versions];
481
482 // 10. Create a Hierarchical Keyring
483 let mpl_config = MaterialProvidersConfig::builder().build()?;
484 let mpl = mpl_client::Client::from_conf(mpl_config)?;
485 let kms_keyring = mpl
486 .create_aws_kms_hierarchical_keyring()
487 .branch_key_id(branch_key_id)
488 .key_store(key_store)
489 .ttl_seconds(600)
490 .send()
491 .await?;
492
493 // 11. Define crypto actions
494 let attribute_actions_on_encrypt = HashMap::from([
495 // Our partition key must be configured as SIGN_ONLY
496 ("partition_key".to_string(), CryptoAction::SignOnly),
497 // Attributes used in beacons must be configured as ENCRYPT_AND_SIGN
498 ("EmployeeID".to_string(), CryptoAction::EncryptAndSign),
499 ("TicketNumber".to_string(), CryptoAction::EncryptAndSign),
500 ("ProjectName".to_string(), CryptoAction::EncryptAndSign),
501 ("EmployeeName".to_string(), CryptoAction::EncryptAndSign),
502 ("EmployeeEmail".to_string(), CryptoAction::EncryptAndSign),
503 ("CreatorEmail".to_string(), CryptoAction::EncryptAndSign),
504 ("ProjectStatus".to_string(), CryptoAction::EncryptAndSign),
505 ("OrganizerEmail".to_string(), CryptoAction::EncryptAndSign),
506 ("ManagerEmail".to_string(), CryptoAction::EncryptAndSign),
507 ("AssigneeEmail".to_string(), CryptoAction::EncryptAndSign),
508 ("City".to_string(), CryptoAction::EncryptAndSign),
509 ("Severity".to_string(), CryptoAction::EncryptAndSign),
510 ("Location".to_string(), CryptoAction::EncryptAndSign),
511 // These are not beaconized attributes, but are sensitive data that must be encrypted
512 ("Attendees".to_string(), CryptoAction::EncryptAndSign),
513 ("Subject".to_string(), CryptoAction::EncryptAndSign),
514 // Signed parts and unencrypted attributes can be configured as SIGN_ONLY or DO_NOTHING
515 // For this example, we will set these to SIGN_ONLY to ensure authenticity
516 ("TicketModTime".to_string(), CryptoAction::SignOnly),
517 ("MeetingStart".to_string(), CryptoAction::SignOnly),
518 ("TimeCardStart".to_string(), CryptoAction::SignOnly),
519 ("EmployeeTitle".to_string(), CryptoAction::SignOnly),
520 ("Description".to_string(), CryptoAction::SignOnly),
521 ("ProjectTarget".to_string(), CryptoAction::SignOnly),
522 ("Hours".to_string(), CryptoAction::SignOnly),
523 ("Role".to_string(), CryptoAction::SignOnly),
524 ("Message".to_string(), CryptoAction::SignOnly),
525 ("ProjectStart".to_string(), CryptoAction::SignOnly),
526 ("Duration".to_string(), CryptoAction::SignOnly),
527 ]);
528
529 // 12. Set up table config
530 let table_config = DynamoDbTableEncryptionConfig::builder()
531 .logical_table_name(ddb_table_name)
532 .partition_key_name("partition_key")
533 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
534 .keyring(kms_keyring)
535 .search(
536 SearchConfig::builder()
537 .write_version(1)
538 .versions(beacon_versions)
539 .build()?,
540 )
541 .build()?;
542
543 let table_configs = DynamoDbTablesEncryptionConfig::builder()
544 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
545 .build()?;
546
547 // 13. Create a new AWS SDK DynamoDb client using the config above
548 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
549 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
550 .interceptor(DbEsdkInterceptor::new(table_configs)?)
551 .build();
552
553 Ok(aws_sdk_dynamodb::Client::from_conf(dynamo_config))
554}
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_rsa_keyring(&self) -> CreateAwsKmsRsaKeyringFluentBuilder
pub fn create_aws_kms_rsa_keyring(&self) -> CreateAwsKmsRsaKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsRsaKeyring
operation.
- The fluent builder is configurable:
encryption_algorithm(impl Into<Option<aws_sdk_kms::types::EncryptionAlgorithmSpec>>)
/set_encryption_algorithm(Option<aws_sdk_kms::types::EncryptionAlgorithmSpec>)
: (undocumented)grant_tokens(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_grant_tokens(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)kms_client(impl Into<Option<crate::deps::com_amazonaws_kms::client::Client>>)
/set_kms_client(Option<crate::deps::com_amazonaws_kms::client::Client>)
: (undocumented)kms_key_id(impl Into<Option<::std::string::String>>)
/set_kms_key_id(Option<::std::string::String>)
: (undocumented)public_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_public_key(Option<::aws_smithy_types::Blob>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsRsaKeyringError>
Examples found in repository?
47pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
48 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
49 let rsa_key_arn = test_utils::TEST_KMS_RSA_KEY_ID;
50
51 // You may provide your own RSA public key at EXAMPLE_RSA_PUBLIC_KEY_FILENAME.
52 // This must be the public key for the RSA key represented at rsaKeyArn.
53 // If this file is not present, this will write a UTF-8 encoded PEM file for you.
54 if should_get_new_public_key(DEFAULT_EXAMPLE_RSA_PUBLIC_KEY_FILENAME) {
55 write_public_key_pem_for_rsa_key(
56 test_utils::TEST_KMS_RSA_KEY_ID,
57 DEFAULT_EXAMPLE_RSA_PUBLIC_KEY_FILENAME,
58 )
59 .await?;
60 }
61
62 // 1. Load UTF-8 encoded public key PEM file.
63 // You may have an RSA public key file already defined.
64 // If not, the main method in this class will call
65 // the KMS RSA key, retrieve its public key, and store it
66 // in a PEM file for example use.
67 let mut file = File::open(Path::new(DEFAULT_EXAMPLE_RSA_PUBLIC_KEY_FILENAME))?;
68 let mut public_key_utf8_bytes = Vec::new();
69 file.read_to_end(&mut public_key_utf8_bytes)?;
70
71 // 2. Create a KMS RSA keyring.
72 // This keyring takes in:
73 // - kmsClient
74 // - kmsKeyId: Must be an ARN representing a KMS RSA key
75 // - publicKey: A ByteBuffer of a UTF-8 encoded PEM file representing the public
76 // key for the key passed into kmsKeyId
77 // - encryptionAlgorithm: Must be either RSAES_OAEP_SHA_256 or RSAES_OAEP_SHA_1
78 let mpl_config = MaterialProvidersConfig::builder().build()?;
79 let mpl = mpl_client::Client::from_conf(mpl_config)?;
80 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
81 let kms_rsa_keyring = mpl
82 .create_aws_kms_rsa_keyring()
83 .kms_key_id(rsa_key_arn)
84 .public_key(public_key_utf8_bytes)
85 .encryption_algorithm(aws_sdk_kms::types::EncryptionAlgorithmSpec::RsaesOaepSha256)
86 .kms_client(aws_sdk_kms::Client::new(&sdk_config))
87 .send()
88 .await?;
89
90 // 3. Configure which attributes are encrypted and/or signed when writing new items.
91 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
92 // we must explicitly configure how they should be treated during item encryption:
93 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
94 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
95 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
96 let attribute_actions_on_encrypt = HashMap::from([
97 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
98 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
99 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
100 ]);
101
102 // 4. Configure which attributes we expect to be included in the signature
103 // when reading items. There are two options for configuring this:
104 //
105 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
106 // When defining your DynamoDb schema and deciding on attribute names,
107 // choose a distinguishing prefix (such as ":") for all attributes that
108 // you do not want to include in the signature.
109 // This has two main benefits:
110 // - It is easier to reason about the security and authenticity of data within your item
111 // when all unauthenticated data is easily distinguishable by their attribute name.
112 // - If you need to add new unauthenticated attributes in the future,
113 // you can easily make the corresponding update to your `attributeActions`
114 // and immediately start writing to that new attribute, without
115 // any other configuration update needed.
116 // Once you configure this field, it is not safe to update it.
117 //
118 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
119 // a set of attributes that should be considered unauthenticated when encountered
120 // on read. Be careful if you use this configuration. Do not remove an attribute
121 // name from this configuration, even if you are no longer writing with that attribute,
122 // as old items may still include this attribute, and our configuration needs to know
123 // to continue to exclude this attribute from the signature scope.
124 // If you add new attribute names to this field, you must first deploy the update to this
125 // field to all readers in your host fleet before deploying the update to start writing
126 // with that new attribute.
127 //
128 // For this example, we currently authenticate all attributes. To make it easier to
129 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
130 const UNSIGNED_ATTR_PREFIX: &str = ":";
131
132 // 5. Create the DynamoDb Encryption configuration for the table we will be writing to.
133 // Note: To use the KMS RSA keyring, your table config must specify an algorithmSuite
134 // that does not use asymmetric signing.
135 let table_config = DynamoDbTableEncryptionConfig::builder()
136 .logical_table_name(ddb_table_name)
137 .partition_key_name("partition_key")
138 .sort_key_name("sort_key")
139 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
140 .keyring(kms_rsa_keyring)
141 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
142 // Specify algorithmSuite without asymmetric signing here
143 // As of v3.0.0, the only supported algorithmSuite without asymmetric signing is
144 // ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_SYMSIG_HMAC_SHA384.
145 .algorithm_suite_id(DbeAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKeySymsigHmacSha384)
146 .build()?;
147
148 let table_configs = DynamoDbTablesEncryptionConfig::builder()
149 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
150 .build()?;
151
152 // 6. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above
153 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
154 .interceptor(DbEsdkInterceptor::new(table_configs)?)
155 .build();
156 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
157
158 // 7. Put an item into our table using the above client.
159 // Before the item gets sent to DynamoDb, it will be encrypted
160 // client-side, according to our configuration.
161 let item = HashMap::from([
162 (
163 "partition_key".to_string(),
164 AttributeValue::S("awsKmsRsaKeyringItem".to_string()),
165 ),
166 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
167 (
168 "sensitive_data".to_string(),
169 AttributeValue::S("encrypt and sign me!".to_string()),
170 ),
171 ]);
172
173 ddb.put_item()
174 .table_name(ddb_table_name)
175 .set_item(Some(item.clone()))
176 .send()
177 .await?;
178
179 // 8. Get the item back from our table using the client.
180 // The client will decrypt the item client-side using the RSA keyring
181 // and return the original item.
182 let key_to_get = HashMap::from([
183 (
184 "partition_key".to_string(),
185 AttributeValue::S("awsKmsRsaKeyringItem".to_string()),
186 ),
187 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
188 ]);
189
190 let resp = ddb
191 .get_item()
192 .table_name(ddb_table_name)
193 .set_key(Some(key_to_get))
194 .consistent_read(true)
195 .send()
196 .await?;
197
198 assert_eq!(resp.item, Some(item));
199 println!("kms_rsa_keyring successful.");
200 Ok(())
201}
Source§impl Client
impl Client
Sourcepub fn create_aws_kms_ecdh_keyring(
&self,
) -> CreateAwsKmsEcdhKeyringFluentBuilder
pub fn create_aws_kms_ecdh_keyring( &self, ) -> CreateAwsKmsEcdhKeyringFluentBuilder
Constructs a fluent builder for the CreateAwsKmsEcdhKeyring
operation.
- The fluent builder is configurable:
key_agreement_scheme(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::KmsEcdhStaticConfigurations>>)
/set_key_agreement_scheme(Option<crate::deps::aws_cryptography_materialProviders::types::KmsEcdhStaticConfigurations>)
: (undocumented)curve_spec(impl Into<Option<crate::deps::aws_cryptography_primitives::types::EcdhCurveSpec>>)
/set_curve_spec(Option<crate::deps::aws_cryptography_primitives::types::EcdhCurveSpec>)
: (undocumented)grant_tokens(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_grant_tokens(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)kms_client(impl Into<Option<crate::deps::com_amazonaws_kms::client::Client>>)
/set_kms_client(Option<crate::deps::com_amazonaws_kms::client::Client>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateAwsKmsEcdhKeyringError>
Source§impl Client
impl Client
Sourcepub fn create_multi_keyring(&self) -> CreateMultiKeyringFluentBuilder
pub fn create_multi_keyring(&self) -> CreateMultiKeyringFluentBuilder
Constructs a fluent builder for the CreateMultiKeyring
operation.
- The fluent builder is configurable:
child_keyrings(impl Into<Option<::std::vec::Vec<crate::deps::aws_cryptography_materialProviders::types::keyring::KeyringRef>>>)
/set_child_keyrings(Option<::std::vec::Vec<crate::deps::aws_cryptography_materialProviders::types::keyring::KeyringRef>>)
: (undocumented)generator(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::keyring::KeyringRef>>)
/set_generator(Option<crate::deps::aws_cryptography_materialProviders::types::keyring::KeyringRef>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateMultiKeyringError>
Examples found in repository?
46pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
47 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
48 let key_arn = test_utils::TEST_KMS_KEY_ID;
49 let aes_key_bytes = generate_aes_key_bytes();
50
51 // 1. Create the raw AES keyring.
52 let mpl_config = MaterialProvidersConfig::builder().build()?;
53 let mpl = mpl_client::Client::from_conf(mpl_config)?;
54 let raw_aes_keyring = mpl
55 .create_raw_aes_keyring()
56 .key_name("my-aes-key-name")
57 .key_namespace("my-key-namespace")
58 .wrapping_key(aes_key_bytes)
59 .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16)
60 .send()
61 .await?;
62
63 // 2. Create the AWS KMS keyring.
64 // We create a MRK multi keyring, as this interface also supports
65 // single-region KMS keys (standard KMS keys),
66 // and creates the KMS client for us automatically.
67 let aws_kms_mrk_multi_keyring = mpl
68 .create_aws_kms_mrk_multi_keyring()
69 .generator(key_arn)
70 .send()
71 .await?;
72
73 // 3. Create the multi-keyring.
74 // We will label the AWS KMS keyring as the generator and the raw AES keyring as the
75 // only child keyring.
76 // You must provide a generator keyring to encrypt data.
77 // You may provide additional child keyrings. Each child keyring will be able to
78 // decrypt data encrypted with the multi-keyring on its own. It does not need
79 // knowledge of any other child keyrings or the generator keyring to decrypt.
80
81 let multi_keyring = mpl
82 .create_multi_keyring()
83 .generator(aws_kms_mrk_multi_keyring)
84 .child_keyrings(vec![raw_aes_keyring.clone()])
85 .send()
86 .await?;
87
88 // 4. Configure which attributes are encrypted and/or signed when writing new items.
89 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
90 // we must explicitly configure how they should be treated during item encryption:
91 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
92 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
93 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
94 let attribute_actions_on_encrypt = HashMap::from([
95 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
96 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
97 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
98 ]);
99
100 // 5. Configure which attributes we expect to be included in the signature
101 // when reading items. There are two options for configuring this:
102 //
103 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
104 // When defining your DynamoDb schema and deciding on attribute names,
105 // choose a distinguishing prefix (such as ":") for all attributes that
106 // you do not want to include in the signature.
107 // This has two main benefits:
108 // - It is easier to reason about the security and authenticity of data within your item
109 // when all unauthenticated data is easily distinguishable by their attribute name.
110 // - If you need to add new unauthenticated attributes in the future,
111 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
112 // and immediately start writing to that new attribute, without
113 // any other configuration update needed.
114 // Once you configure this field, it is not safe to update it.
115 //
116 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
117 // a set of attributes that should be considered unauthenticated when encountered
118 // on read. Be careful if you use this configuration. Do not remove an attribute
119 // name from this configuration, even if you are no longer writing with that attribute,
120 // as old items may still include this attribute, and our configuration needs to know
121 // to continue to exclude this attribute from the signature scope.
122 // If you add new attribute names to this field, you must first deploy the update to this
123 // field to all readers in your host fleet before deploying the update to start writing
124 // with that new attribute.
125 //
126 // For this example, we currently authenticate all attributes. To make it easier to
127 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
128 const UNSIGNED_ATTR_PREFIX: &str = ":";
129
130 // 6. Create the DynamoDb Encryption configuration for the table we will be writing to.
131 // Note that this example creates one config/client combination for PUT, and another
132 // for GET. The PUT config uses the multi-keyring, while the GET config uses the
133 // raw AES keyring. This is solely done to demonstrate that a keyring included as
134 // a child of a multi-keyring can be used to decrypt data on its own.
135 let table_config = DynamoDbTableEncryptionConfig::builder()
136 .logical_table_name(ddb_table_name)
137 .partition_key_name("partition_key")
138 .sort_key_name("sort_key")
139 .attribute_actions_on_encrypt(attribute_actions_on_encrypt.clone())
140 .keyring(multi_keyring)
141 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
142 .build()?;
143
144 let table_configs = DynamoDbTablesEncryptionConfig::builder()
145 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
146 .build()?;
147
148 // 7. Create a new AWS SDK DynamoDb client using the config above
149 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
150 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
151 .interceptor(DbEsdkInterceptor::new(table_configs)?)
152 .build();
153 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
154
155 // 8. Put an item into our table using the above client.
156 // Before the item gets sent to DynamoDb, it will be encrypted
157 // client-side using the multi-keyring.
158 // The item will be encrypted with all wrapping keys in the keyring,
159 // so that it can be decrypted with any one of the keys.
160 let item = HashMap::from([
161 (
162 "partition_key".to_string(),
163 AttributeValue::S("multiKeyringItem".to_string()),
164 ),
165 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
166 (
167 "sensitive_data".to_string(),
168 AttributeValue::S("encrypt and sign me!".to_string()),
169 ),
170 ]);
171
172 ddb.put_item()
173 .table_name(ddb_table_name)
174 .set_item(Some(item.clone()))
175 .send()
176 .await?;
177
178 // 9. Get the item back from our table using the above client.
179 // The client will decrypt the item client-side using the AWS KMS
180 // keyring, and return back the original item.
181 // Since the generator key is the first available key in the keyring,
182 // that is the key that will be used to decrypt this item.
183 let key_to_get = HashMap::from([
184 (
185 "partition_key".to_string(),
186 AttributeValue::S("multiKeyringItem".to_string()),
187 ),
188 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
189 ]);
190
191 let resp = ddb
192 .get_item()
193 .table_name(ddb_table_name)
194 .set_key(Some(key_to_get.clone()))
195 .consistent_read(true)
196 .send()
197 .await?;
198
199 assert_eq!(resp.item, Some(item.clone()));
200
201 // 10. Create a new config and client with only the raw AES keyring to GET the item
202 // This is the same setup as above, except the config uses the `rawAesKeyring`.
203 let only_aes_table_config = DynamoDbTableEncryptionConfig::builder()
204 .logical_table_name(ddb_table_name)
205 .partition_key_name("partition_key")
206 .sort_key_name("sort_key")
207 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
208 .keyring(raw_aes_keyring)
209 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
210 .build()?;
211
212 let only_aes_table_configs = DynamoDbTablesEncryptionConfig::builder()
213 .table_encryption_configs(HashMap::from([(
214 ddb_table_name.to_string(),
215 only_aes_table_config,
216 )]))
217 .build()?;
218
219 let only_aes_dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
220 .interceptor(DbEsdkInterceptor::new(only_aes_table_configs)?)
221 .build();
222 let only_aes_ddb = aws_sdk_dynamodb::Client::from_conf(only_aes_dynamo_config);
223
224 // 11. Get the item back from our table using the client
225 // configured with only the raw AES keyring.
226 // The client will decrypt the item client-side using the raw
227 // AES keyring, and return back the original item.
228 let resp = only_aes_ddb
229 .get_item()
230 .table_name(ddb_table_name)
231 .set_key(Some(key_to_get.clone()))
232 .consistent_read(true)
233 .send()
234 .await?;
235
236 assert_eq!(resp.item, Some(item.clone()));
237
238 println!("multi_keyring successful.");
239 Ok(())
240}
Source§impl Client
impl Client
Sourcepub fn create_raw_aes_keyring(&self) -> CreateRawAesKeyringFluentBuilder
pub fn create_raw_aes_keyring(&self) -> CreateRawAesKeyringFluentBuilder
Constructs a fluent builder for the CreateRawAesKeyring
operation.
- The fluent builder is configurable:
key_name(impl Into<Option<::std::string::String>>)
/set_key_name(Option<::std::string::String>)
: (undocumented)key_namespace(impl Into<Option<::std::string::String>>)
/set_key_namespace(Option<::std::string::String>)
: (undocumented)wrapping_alg(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::AesWrappingAlg>>)
/set_wrapping_alg(Option<crate::deps::aws_cryptography_materialProviders::types::AesWrappingAlg>)
: (undocumented)wrapping_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_wrapping_key(Option<::aws_smithy_types::Blob>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateRawAesKeyringError>
Examples found in repository?
40pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
41 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
42 let aes_key_bytes = generate_aes_key_bytes();
43
44 // 1. Create the keyring.
45 // The DynamoDb encryption client uses this to encrypt and decrypt items.
46 let mpl_config = MaterialProvidersConfig::builder().build()?;
47 let mpl = mpl_client::Client::from_conf(mpl_config)?;
48 let raw_aes_keyring = mpl
49 .create_raw_aes_keyring()
50 .key_name("my-aes-key-name")
51 .key_namespace("my-key-namespace")
52 .wrapping_key(aes_key_bytes)
53 .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16)
54 .send()
55 .await?;
56
57 // 2. Configure which attributes are encrypted and/or signed when writing new items.
58 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
59 // we must explicitly configure how they should be treated during item encryption:
60 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
61 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
62 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
63 let attribute_actions_on_encrypt = HashMap::from([
64 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
65 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
66 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
67 ]);
68
69 // 3. Configure which attributes we expect to be included in the signature
70 // when reading items. There are two options for configuring this:
71 //
72 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
73 // When defining your DynamoDb schema and deciding on attribute names,
74 // choose a distinguishing prefix (such as ":") for all attributes that
75 // you do not want to include in the signature.
76 // This has two main benefits:
77 // - It is easier to reason about the security and authenticity of data within your item
78 // when all unauthenticated data is easily distinguishable by their attribute name.
79 // - If you need to add new unauthenticated attributes in the future,
80 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
81 // and immediately start writing to that new attribute, without
82 // any other configuration update needed.
83 // Once you configure this field, it is not safe to update it.
84 //
85 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
86 // a set of attributes that should be considered unauthenticated when encountered
87 // on read. Be careful if you use this configuration. Do not remove an attribute
88 // name from this configuration, even if you are no longer writing with that attribute,
89 // as old items may still include this attribute, and our configuration needs to know
90 // to continue to exclude this attribute from the signature scope.
91 // If you add new attribute names to this field, you must first deploy the update to this
92 // field to all readers in your host fleet before deploying the update to start writing
93 // with that new attribute.
94 //
95 // For this example, we currently authenticate all attributes. To make it easier to
96 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
97 const UNSIGNED_ATTR_PREFIX: &str = ":";
98
99 // 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
100 let table_config = DynamoDbTableEncryptionConfig::builder()
101 .logical_table_name(ddb_table_name)
102 .partition_key_name("partition_key")
103 .sort_key_name("sort_key")
104 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
105 .keyring(raw_aes_keyring)
106 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
107 .build()?;
108
109 let table_configs = DynamoDbTablesEncryptionConfig::builder()
110 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
111 .build()?;
112
113 // 5. Create a new AWS SDK DynamoDb client using the Config above
114 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
115 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
116 .interceptor(DbEsdkInterceptor::new(table_configs)?)
117 .build();
118 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
119
120 // 6. Put an item into our table using the above client.
121 // Before the item gets sent to DynamoDb, it will be encrypted
122 // client-side, according to our configuration.
123 let item = HashMap::from([
124 (
125 "partition_key".to_string(),
126 AttributeValue::S("rawAesKeyringItem".to_string()),
127 ),
128 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
129 (
130 "sensitive_data".to_string(),
131 AttributeValue::S("encrypt and sign me!".to_string()),
132 ),
133 ]);
134
135 ddb.put_item()
136 .table_name(ddb_table_name)
137 .set_item(Some(item.clone()))
138 .send()
139 .await?;
140
141 // 7. Get the item back from our table using the same client.
142 // The client will decrypt the item client-side, and return
143 // back the original item.
144 let key_to_get = HashMap::from([
145 (
146 "partition_key".to_string(),
147 AttributeValue::S("rawAesKeyringItem".to_string()),
148 ),
149 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
150 ]);
151
152 let resp = ddb
153 .get_item()
154 .table_name(ddb_table_name)
155 .set_key(Some(key_to_get))
156 // In this example we configure a strongly consistent read
157 // because we perform a read immediately after a write (for demonstrative purposes).
158 // By default, reads are only eventually consistent.
159 // Read our docs to determine which read consistency to use for your application:
160 // https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
161 .consistent_read(true)
162 .send()
163 .await?;
164
165 assert_eq!(resp.item, Some(item));
166
167 println!("raw_aes_keyring successful.");
168 Ok(())
169}
More examples
46pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
47 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
48 let key_arn = test_utils::TEST_KMS_KEY_ID;
49 let aes_key_bytes = generate_aes_key_bytes();
50
51 // 1. Create the raw AES keyring.
52 let mpl_config = MaterialProvidersConfig::builder().build()?;
53 let mpl = mpl_client::Client::from_conf(mpl_config)?;
54 let raw_aes_keyring = mpl
55 .create_raw_aes_keyring()
56 .key_name("my-aes-key-name")
57 .key_namespace("my-key-namespace")
58 .wrapping_key(aes_key_bytes)
59 .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16)
60 .send()
61 .await?;
62
63 // 2. Create the AWS KMS keyring.
64 // We create a MRK multi keyring, as this interface also supports
65 // single-region KMS keys (standard KMS keys),
66 // and creates the KMS client for us automatically.
67 let aws_kms_mrk_multi_keyring = mpl
68 .create_aws_kms_mrk_multi_keyring()
69 .generator(key_arn)
70 .send()
71 .await?;
72
73 // 3. Create the multi-keyring.
74 // We will label the AWS KMS keyring as the generator and the raw AES keyring as the
75 // only child keyring.
76 // You must provide a generator keyring to encrypt data.
77 // You may provide additional child keyrings. Each child keyring will be able to
78 // decrypt data encrypted with the multi-keyring on its own. It does not need
79 // knowledge of any other child keyrings or the generator keyring to decrypt.
80
81 let multi_keyring = mpl
82 .create_multi_keyring()
83 .generator(aws_kms_mrk_multi_keyring)
84 .child_keyrings(vec![raw_aes_keyring.clone()])
85 .send()
86 .await?;
87
88 // 4. Configure which attributes are encrypted and/or signed when writing new items.
89 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
90 // we must explicitly configure how they should be treated during item encryption:
91 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
92 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
93 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
94 let attribute_actions_on_encrypt = HashMap::from([
95 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
96 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
97 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
98 ]);
99
100 // 5. Configure which attributes we expect to be included in the signature
101 // when reading items. There are two options for configuring this:
102 //
103 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
104 // When defining your DynamoDb schema and deciding on attribute names,
105 // choose a distinguishing prefix (such as ":") for all attributes that
106 // you do not want to include in the signature.
107 // This has two main benefits:
108 // - It is easier to reason about the security and authenticity of data within your item
109 // when all unauthenticated data is easily distinguishable by their attribute name.
110 // - If you need to add new unauthenticated attributes in the future,
111 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
112 // and immediately start writing to that new attribute, without
113 // any other configuration update needed.
114 // Once you configure this field, it is not safe to update it.
115 //
116 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
117 // a set of attributes that should be considered unauthenticated when encountered
118 // on read. Be careful if you use this configuration. Do not remove an attribute
119 // name from this configuration, even if you are no longer writing with that attribute,
120 // as old items may still include this attribute, and our configuration needs to know
121 // to continue to exclude this attribute from the signature scope.
122 // If you add new attribute names to this field, you must first deploy the update to this
123 // field to all readers in your host fleet before deploying the update to start writing
124 // with that new attribute.
125 //
126 // For this example, we currently authenticate all attributes. To make it easier to
127 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
128 const UNSIGNED_ATTR_PREFIX: &str = ":";
129
130 // 6. Create the DynamoDb Encryption configuration for the table we will be writing to.
131 // Note that this example creates one config/client combination for PUT, and another
132 // for GET. The PUT config uses the multi-keyring, while the GET config uses the
133 // raw AES keyring. This is solely done to demonstrate that a keyring included as
134 // a child of a multi-keyring can be used to decrypt data on its own.
135 let table_config = DynamoDbTableEncryptionConfig::builder()
136 .logical_table_name(ddb_table_name)
137 .partition_key_name("partition_key")
138 .sort_key_name("sort_key")
139 .attribute_actions_on_encrypt(attribute_actions_on_encrypt.clone())
140 .keyring(multi_keyring)
141 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
142 .build()?;
143
144 let table_configs = DynamoDbTablesEncryptionConfig::builder()
145 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
146 .build()?;
147
148 // 7. Create a new AWS SDK DynamoDb client using the config above
149 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
150 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
151 .interceptor(DbEsdkInterceptor::new(table_configs)?)
152 .build();
153 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
154
155 // 8. Put an item into our table using the above client.
156 // Before the item gets sent to DynamoDb, it will be encrypted
157 // client-side using the multi-keyring.
158 // The item will be encrypted with all wrapping keys in the keyring,
159 // so that it can be decrypted with any one of the keys.
160 let item = HashMap::from([
161 (
162 "partition_key".to_string(),
163 AttributeValue::S("multiKeyringItem".to_string()),
164 ),
165 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
166 (
167 "sensitive_data".to_string(),
168 AttributeValue::S("encrypt and sign me!".to_string()),
169 ),
170 ]);
171
172 ddb.put_item()
173 .table_name(ddb_table_name)
174 .set_item(Some(item.clone()))
175 .send()
176 .await?;
177
178 // 9. Get the item back from our table using the above client.
179 // The client will decrypt the item client-side using the AWS KMS
180 // keyring, and return back the original item.
181 // Since the generator key is the first available key in the keyring,
182 // that is the key that will be used to decrypt this item.
183 let key_to_get = HashMap::from([
184 (
185 "partition_key".to_string(),
186 AttributeValue::S("multiKeyringItem".to_string()),
187 ),
188 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
189 ]);
190
191 let resp = ddb
192 .get_item()
193 .table_name(ddb_table_name)
194 .set_key(Some(key_to_get.clone()))
195 .consistent_read(true)
196 .send()
197 .await?;
198
199 assert_eq!(resp.item, Some(item.clone()));
200
201 // 10. Create a new config and client with only the raw AES keyring to GET the item
202 // This is the same setup as above, except the config uses the `rawAesKeyring`.
203 let only_aes_table_config = DynamoDbTableEncryptionConfig::builder()
204 .logical_table_name(ddb_table_name)
205 .partition_key_name("partition_key")
206 .sort_key_name("sort_key")
207 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
208 .keyring(raw_aes_keyring)
209 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
210 .build()?;
211
212 let only_aes_table_configs = DynamoDbTablesEncryptionConfig::builder()
213 .table_encryption_configs(HashMap::from([(
214 ddb_table_name.to_string(),
215 only_aes_table_config,
216 )]))
217 .build()?;
218
219 let only_aes_dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
220 .interceptor(DbEsdkInterceptor::new(only_aes_table_configs)?)
221 .build();
222 let only_aes_ddb = aws_sdk_dynamodb::Client::from_conf(only_aes_dynamo_config);
223
224 // 11. Get the item back from our table using the client
225 // configured with only the raw AES keyring.
226 // The client will decrypt the item client-side using the raw
227 // AES keyring, and return back the original item.
228 let resp = only_aes_ddb
229 .get_item()
230 .table_name(ddb_table_name)
231 .set_key(Some(key_to_get.clone()))
232 .consistent_read(true)
233 .send()
234 .await?;
235
236 assert_eq!(resp.item, Some(item.clone()));
237
238 println!("multi_keyring successful.");
239 Ok(())
240}
Source§impl Client
impl Client
Sourcepub fn create_raw_rsa_keyring(&self) -> CreateRawRsaKeyringFluentBuilder
pub fn create_raw_rsa_keyring(&self) -> CreateRawRsaKeyringFluentBuilder
Constructs a fluent builder for the CreateRawRsaKeyring
operation.
- The fluent builder is configurable:
key_name(impl Into<Option<::std::string::String>>)
/set_key_name(Option<::std::string::String>)
: (undocumented)key_namespace(impl Into<Option<::std::string::String>>)
/set_key_namespace(Option<::std::string::String>)
: (undocumented)padding_scheme(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::PaddingScheme>>)
/set_padding_scheme(Option<crate::deps::aws_cryptography_materialProviders::types::PaddingScheme>)
: (undocumented)private_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_private_key(Option<::aws_smithy_types::Blob>)
: (undocumented)public_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_public_key(Option<::aws_smithy_types::Blob>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateRawRsaKeyringError>
Examples found in repository?
57pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
58 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
59
60 // You may provide your own RSA key pair in the files located at
61 // - EXAMPLE_RSA_PRIVATE_KEY_FILENAME
62 // - EXAMPLE_RSA_PUBLIC_KEY_FILENAME
63 // If these files are not present, this will generate a pair for you
64 if should_generate_new_rsa_key_pair()? {
65 generate_rsa_key_pair()?;
66 }
67
68 // 1. Load key pair from UTF-8 encoded PEM files.
69 // You may provide your own PEM files to use here.
70 // If you do not, the main method in this class will generate PEM
71 // files for example use. Do not use these files for any other purpose.
72
73 let mut file = File::open(Path::new(EXAMPLE_RSA_PUBLIC_KEY_FILENAME))?;
74 let mut public_key_utf8_bytes = Vec::new();
75 file.read_to_end(&mut public_key_utf8_bytes)?;
76
77 let mut file = File::open(Path::new(EXAMPLE_RSA_PRIVATE_KEY_FILENAME))?;
78 let mut private_key_utf8_bytes = Vec::new();
79 file.read_to_end(&mut private_key_utf8_bytes)?;
80
81 // 2. Create the keyring.
82 // The DynamoDb encryption client uses this to encrypt and decrypt items.
83 let mpl_config = MaterialProvidersConfig::builder().build()?;
84 let mpl = mpl_client::Client::from_conf(mpl_config)?;
85 let raw_rsa_keyring = mpl
86 .create_raw_rsa_keyring()
87 .key_name("my-rsa-key-name")
88 .key_namespace("my-key-namespace")
89 .padding_scheme(PaddingScheme::OaepSha256Mgf1)
90 .public_key(public_key_utf8_bytes)
91 .private_key(private_key_utf8_bytes)
92 .send()
93 .await?;
94
95 // 3. Configure which attributes are encrypted and/or signed when writing new items.
96 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
97 // we must explicitly configure how they should be treated during item encryption:
98 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
99 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
100 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
101 let attribute_actions_on_encrypt = HashMap::from([
102 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
103 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
104 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
105 ]);
106
107 // 4. Configure which attributes we expect to be included in the signature
108 // when reading items. There are two options for configuring this:
109 //
110 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
111 // When defining your DynamoDb schema and deciding on attribute names,
112 // choose a distinguishing prefix (such as ":") for all attributes that
113 // you do not want to include in the signature.
114 // This has two main benefits:
115 // - It is easier to reason about the security and authenticity of data within your item
116 // when all unauthenticated data is easily distinguishable by their attribute name.
117 // - If you need to add new unauthenticated attributes in the future,
118 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
119 // and immediately start writing to that new attribute, without
120 // any other configuration update needed.
121 // Once you configure this field, it is not safe to update it.
122 //
123 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
124 // a set of attributes that should be considered unauthenticated when encountered
125 // on read. Be careful if you use this configuration. Do not remove an attribute
126 // name from this configuration, even if you are no longer writing with that attribute,
127 // as old items may still include this attribute, and our configuration needs to know
128 // to continue to exclude this attribute from the signature scope.
129 // If you add new attribute names to this field, you must first deploy the update to this
130 // field to all readers in your host fleet before deploying the update to start writing
131 // with that new attribute.
132 //
133 // For this example, we currently authenticate all attributes. To make it easier to
134 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
135 const UNSIGNED_ATTR_PREFIX: &str = ":";
136
137 // 5. Create the DynamoDb Encryption configuration for the table we will be writing to.
138 let table_config = DynamoDbTableEncryptionConfig::builder()
139 .logical_table_name(ddb_table_name)
140 .partition_key_name("partition_key")
141 .sort_key_name("sort_key")
142 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
143 .keyring(raw_rsa_keyring)
144 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
145 .build()?;
146
147 let table_configs = DynamoDbTablesEncryptionConfig::builder()
148 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
149 .build()?;
150
151 // 6. Create a new AWS SDK DynamoDb client using the config above
152 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
153 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
154 .interceptor(DbEsdkInterceptor::new(table_configs)?)
155 .build();
156 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
157
158 // 7. Put an item into our table using the above client.
159 // Before the item gets sent to DynamoDb, it will be encrypted
160 // client-side, according to our configuration.
161 let item = HashMap::from([
162 (
163 "partition_key".to_string(),
164 AttributeValue::S("rawRsaKeyringItem".to_string()),
165 ),
166 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
167 (
168 "sensitive_data".to_string(),
169 AttributeValue::S("encrypt and sign me!".to_string()),
170 ),
171 ]);
172
173 ddb.put_item()
174 .table_name(ddb_table_name)
175 .set_item(Some(item.clone()))
176 .send()
177 .await?;
178
179 // 8. Get the item back from our table using the same client.
180 // The client will decrypt the item client-side, and return
181 // back the original item.
182
183 let key_to_get = HashMap::from([
184 (
185 "partition_key".to_string(),
186 AttributeValue::S("rawRsaKeyringItem".to_string()),
187 ),
188 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
189 ]);
190
191 let resp = ddb
192 .get_item()
193 .table_name(ddb_table_name)
194 .set_key(Some(key_to_get))
195 // In this example we configure a strongly consistent read
196 // because we perform a read immediately after a write (for demonstrative purposes).
197 // By default, reads are only eventually consistent.
198 // Read our docs to determine which read consistency to use for your application:
199 // https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
200 .consistent_read(true)
201 .send()
202 .await?;
203
204 assert_eq!(resp.item, Some(item));
205 println!("raw_rsa_keyring successful.");
206 Ok(())
207}
Source§impl Client
impl Client
Sourcepub fn create_raw_ecdh_keyring(&self) -> CreateRawEcdhKeyringFluentBuilder
pub fn create_raw_ecdh_keyring(&self) -> CreateRawEcdhKeyringFluentBuilder
Constructs a fluent builder for the CreateRawEcdhKeyring
operation.
- The fluent builder is configurable:
key_agreement_scheme(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::RawEcdhStaticConfigurations>>)
/set_key_agreement_scheme(Option<crate::deps::aws_cryptography_materialProviders::types::RawEcdhStaticConfigurations>)
: (undocumented)curve_spec(impl Into<Option<crate::deps::aws_cryptography_primitives::types::EcdhCurveSpec>>)
/set_curve_spec(Option<crate::deps::aws_cryptography_primitives::types::EcdhCurveSpec>)
: (undocumented)
- On success, responds with
CreateKeyringOutput
with field(s): - On failure, responds with
SdkError<CreateRawEcdhKeyringError>
Source§impl Client
impl Client
Sourcepub fn create_default_cryptographic_materials_manager(
&self,
) -> CreateDefaultCryptographicMaterialsManagerFluentBuilder
pub fn create_default_cryptographic_materials_manager( &self, ) -> CreateDefaultCryptographicMaterialsManagerFluentBuilder
Constructs a fluent builder for the CreateDefaultCryptographicMaterialsManager
operation.
- The fluent builder is configurable:
- On success, responds with
CreateCryptographicMaterialsManagerOutput
with field(s): - On failure, responds with
SdkError<CreateDefaultCryptographicMaterialsManagerError>
Source§impl Client
impl Client
Sourcepub fn create_required_encryption_context_cmm(
&self,
) -> CreateRequiredEncryptionContextCmmFluentBuilder
pub fn create_required_encryption_context_cmm( &self, ) -> CreateRequiredEncryptionContextCmmFluentBuilder
Constructs a fluent builder for the CreateRequiredEncryptionContextCMM
operation.
- The fluent builder is configurable:
keyring(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::keyring::KeyringRef>>)
/set_keyring(Option<crate::deps::aws_cryptography_materialProviders::types::keyring::KeyringRef>)
: (undocumented)required_encryption_context_keys(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_required_encryption_context_keys(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)underlying_cmm(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::cryptographic_materials_manager::CryptographicMaterialsManagerRef>>)
/set_underlying_cmm(Option<crate::deps::aws_cryptography_materialProviders::types::cryptographic_materials_manager::CryptographicMaterialsManagerRef>)
: (undocumented)
- On success, responds with
CreateRequiredEncryptionContextCmmOutput
with field(s): - On failure, responds with
SdkError<CreateRequiredEncryptionContextCmmError>
Source§impl Client
impl Client
Sourcepub fn create_cryptographic_materials_cache(
&self,
) -> CreateCryptographicMaterialsCacheFluentBuilder
pub fn create_cryptographic_materials_cache( &self, ) -> CreateCryptographicMaterialsCacheFluentBuilder
Constructs a fluent builder for the CreateCryptographicMaterialsCache
operation.
- The fluent builder is configurable:
- On success, responds with
CreateCryptographicMaterialsCacheOutput
with field(s): - On failure, responds with
SdkError<CreateCryptographicMaterialsCacheError>
Source§impl Client
impl Client
Sourcepub fn create_default_client_supplier(
&self,
) -> CreateDefaultClientSupplierFluentBuilder
pub fn create_default_client_supplier( &self, ) -> CreateDefaultClientSupplierFluentBuilder
Constructs a fluent builder for the CreateDefaultClientSupplier
operation.
- The fluent builder is configurable:
- On success, responds with
CreateDefaultClientSupplierOutput
with field(s): - On failure, responds with
SdkError<CreateDefaultClientSupplierError>
Source§impl Client
impl Client
Sourcepub fn initialize_encryption_materials(
&self,
) -> InitializeEncryptionMaterialsFluentBuilder
pub fn initialize_encryption_materials( &self, ) -> InitializeEncryptionMaterialsFluentBuilder
Constructs a fluent builder for the InitializeEncryptionMaterials
operation.
- The fluent builder is configurable:
algorithm_suite_id(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>>)
/set_algorithm_suite_id(Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>)
: (undocumented)encryption_context(impl Into<Option<::std::collections::HashMap<::std::string::String, ::std::string::String>>>)
/set_encryption_context(Option<::std::collections::HashMap<::std::string::String, ::std::string::String>>)
: (undocumented)required_encryption_context_keys(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_required_encryption_context_keys(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)signing_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_signing_key(Option<::aws_smithy_types::Blob>)
: (undocumented)verification_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_verification_key(Option<::aws_smithy_types::Blob>)
: (undocumented)
- On success, responds with
EncryptionMaterials
with field(s):algorithm_suite(Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteInfo>)
: (undocumented)encrypted_data_keys(Option<::std::vec::Vec<crate::deps::aws_cryptography_materialProviders::types::EncryptedDataKey>>)
: (undocumented)encryption_context(Option<::std::collections::HashMap<::std::string::String, ::std::string::String>>)
: (undocumented)plaintext_data_key(Option<::aws_smithy_types::Blob>)
: (undocumented)required_encryption_context_keys(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)signing_key(Option<::aws_smithy_types::Blob>)
: (undocumented)symmetric_signing_keys(Option<::std::vec::Vec<::aws_smithy_types::Blob>>)
: (undocumented)
- On failure, responds with
SdkError<InitializeEncryptionMaterialsError>
Source§impl Client
impl Client
Sourcepub fn initialize_decryption_materials(
&self,
) -> InitializeDecryptionMaterialsFluentBuilder
pub fn initialize_decryption_materials( &self, ) -> InitializeDecryptionMaterialsFluentBuilder
Constructs a fluent builder for the InitializeDecryptionMaterials
operation.
- The fluent builder is configurable:
algorithm_suite_id(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>>)
/set_algorithm_suite_id(Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>)
: (undocumented)encryption_context(impl Into<Option<::std::collections::HashMap<::std::string::String, ::std::string::String>>>)
/set_encryption_context(Option<::std::collections::HashMap<::std::string::String, ::std::string::String>>)
: (undocumented)required_encryption_context_keys(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_required_encryption_context_keys(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)
- On success, responds with
DecryptionMaterials
with field(s):algorithm_suite(Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteInfo>)
: (undocumented)encryption_context(Option<::std::collections::HashMap<::std::string::String, ::std::string::String>>)
: (undocumented)plaintext_data_key(Option<::aws_smithy_types::Blob>)
: (undocumented)required_encryption_context_keys(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)symmetric_signing_key(Option<::aws_smithy_types::Blob>)
: (undocumented)verification_key(Option<::aws_smithy_types::Blob>)
: (undocumented)
- On failure, responds with
SdkError<InitializeDecryptionMaterialsError>
Source§impl Client
impl Client
Sourcepub fn valid_encryption_materials_transition(
&self,
) -> ValidEncryptionMaterialsTransitionFluentBuilder
pub fn valid_encryption_materials_transition( &self, ) -> ValidEncryptionMaterialsTransitionFluentBuilder
Constructs a fluent builder for the ValidEncryptionMaterialsTransition
operation.
- The fluent builder is configurable:
start(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::EncryptionMaterials>>)
/set_start(Option<crate::deps::aws_cryptography_materialProviders::types::EncryptionMaterials>)
: (undocumented)stop(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::EncryptionMaterials>>)
/set_stop(Option<crate::deps::aws_cryptography_materialProviders::types::EncryptionMaterials>)
: (undocumented)
- On success, responds with
Unit
with field(s): - On failure, responds with
SdkError<ValidEncryptionMaterialsTransitionError>
Source§impl Client
impl Client
Sourcepub fn valid_decryption_materials_transition(
&self,
) -> ValidDecryptionMaterialsTransitionFluentBuilder
pub fn valid_decryption_materials_transition( &self, ) -> ValidDecryptionMaterialsTransitionFluentBuilder
Constructs a fluent builder for the ValidDecryptionMaterialsTransition
operation.
- The fluent builder is configurable:
start(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::DecryptionMaterials>>)
/set_start(Option<crate::deps::aws_cryptography_materialProviders::types::DecryptionMaterials>)
: (undocumented)stop(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::DecryptionMaterials>>)
/set_stop(Option<crate::deps::aws_cryptography_materialProviders::types::DecryptionMaterials>)
: (undocumented)
- On success, responds with
Unit
with field(s): - On failure, responds with
SdkError<ValidDecryptionMaterialsTransitionError>
Source§impl Client
impl Client
Sourcepub fn encryption_materials_has_plaintext_data_key(
&self,
) -> EncryptionMaterialsHasPlaintextDataKeyFluentBuilder
pub fn encryption_materials_has_plaintext_data_key( &self, ) -> EncryptionMaterialsHasPlaintextDataKeyFluentBuilder
Constructs a fluent builder for the EncryptionMaterialsHasPlaintextDataKey
operation.
- The fluent builder is configurable:
algorithm_suite(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteInfo>>)
/set_algorithm_suite(Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteInfo>)
: (undocumented)encrypted_data_keys(impl Into<Option<::std::vec::Vec<crate::deps::aws_cryptography_materialProviders::types::EncryptedDataKey>>>)
/set_encrypted_data_keys(Option<::std::vec::Vec<crate::deps::aws_cryptography_materialProviders::types::EncryptedDataKey>>)
: (undocumented)encryption_context(impl Into<Option<::std::collections::HashMap<::std::string::String, ::std::string::String>>>)
/set_encryption_context(Option<::std::collections::HashMap<::std::string::String, ::std::string::String>>)
: (undocumented)plaintext_data_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_plaintext_data_key(Option<::aws_smithy_types::Blob>)
: (undocumented)required_encryption_context_keys(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_required_encryption_context_keys(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)signing_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_signing_key(Option<::aws_smithy_types::Blob>)
: (undocumented)symmetric_signing_keys(impl Into<Option<::std::vec::Vec<::aws_smithy_types::Blob>>>)
/set_symmetric_signing_keys(Option<::std::vec::Vec<::aws_smithy_types::Blob>>)
: (undocumented)
- On success, responds with
Unit
with field(s): - On failure, responds with
SdkError<EncryptionMaterialsHasPlaintextDataKeyError>
Source§impl Client
impl Client
Sourcepub fn decryption_materials_with_plaintext_data_key(
&self,
) -> DecryptionMaterialsWithPlaintextDataKeyFluentBuilder
pub fn decryption_materials_with_plaintext_data_key( &self, ) -> DecryptionMaterialsWithPlaintextDataKeyFluentBuilder
Constructs a fluent builder for the DecryptionMaterialsWithPlaintextDataKey
operation.
- The fluent builder is configurable:
algorithm_suite(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteInfo>>)
/set_algorithm_suite(Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteInfo>)
: (undocumented)encryption_context(impl Into<Option<::std::collections::HashMap<::std::string::String, ::std::string::String>>>)
/set_encryption_context(Option<::std::collections::HashMap<::std::string::String, ::std::string::String>>)
: (undocumented)plaintext_data_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_plaintext_data_key(Option<::aws_smithy_types::Blob>)
: (undocumented)required_encryption_context_keys(impl Into<Option<::std::vec::Vec<::std::string::String>>>)
/set_required_encryption_context_keys(Option<::std::vec::Vec<::std::string::String>>)
: (undocumented)symmetric_signing_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_symmetric_signing_key(Option<::aws_smithy_types::Blob>)
: (undocumented)verification_key(impl Into<Option<::aws_smithy_types::Blob>>)
/set_verification_key(Option<::aws_smithy_types::Blob>)
: (undocumented)
- On success, responds with
Unit
with field(s): - On failure, responds with
SdkError<DecryptionMaterialsWithPlaintextDataKeyError>
Source§impl Client
impl Client
Sourcepub fn get_algorithm_suite_info(&self) -> GetAlgorithmSuiteInfoFluentBuilder
pub fn get_algorithm_suite_info(&self) -> GetAlgorithmSuiteInfoFluentBuilder
Constructs a fluent builder for the GetAlgorithmSuiteInfo
operation.
- The fluent builder is configurable:
- On success, responds with
AlgorithmSuiteInfo
with field(s):binary_id(Option<::aws_smithy_types::Blob>)
: (undocumented)commitment(Option<crate::deps::aws_cryptography_materialProviders::types::DerivationAlgorithm>)
: (undocumented)edk_wrapping(Option<crate::deps::aws_cryptography_materialProviders::types::EdkWrappingAlgorithm>)
: (undocumented)encrypt(Option<crate::deps::aws_cryptography_materialProviders::types::Encrypt>)
: (undocumented)id(Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>)
: (undocumented)kdf(Option<crate::deps::aws_cryptography_materialProviders::types::DerivationAlgorithm>)
: (undocumented)message_version(Option<::std::primitive::i32>)
: (undocumented)signature(Option<crate::deps::aws_cryptography_materialProviders::types::SignatureAlgorithm>)
: (undocumented)symmetric_signature(Option<crate::deps::aws_cryptography_materialProviders::types::SymmetricSignatureAlgorithm>)
: (undocumented)
- On failure, responds with
SdkError<GetAlgorithmSuiteInfoError>
Source§impl Client
impl Client
Sourcepub fn valid_algorithm_suite_info(&self) -> ValidAlgorithmSuiteInfoFluentBuilder
pub fn valid_algorithm_suite_info(&self) -> ValidAlgorithmSuiteInfoFluentBuilder
Constructs a fluent builder for the ValidAlgorithmSuiteInfo
operation.
- The fluent builder is configurable:
binary_id(impl Into<Option<::aws_smithy_types::Blob>>)
/set_binary_id(Option<::aws_smithy_types::Blob>)
: (undocumented)commitment(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::DerivationAlgorithm>>)
/set_commitment(Option<crate::deps::aws_cryptography_materialProviders::types::DerivationAlgorithm>)
: (undocumented)edk_wrapping(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::EdkWrappingAlgorithm>>)
/set_edk_wrapping(Option<crate::deps::aws_cryptography_materialProviders::types::EdkWrappingAlgorithm>)
: (undocumented)encrypt(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::Encrypt>>)
/set_encrypt(Option<crate::deps::aws_cryptography_materialProviders::types::Encrypt>)
: (undocumented)id(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>>)
/set_id(Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>)
: (undocumented)kdf(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::DerivationAlgorithm>>)
/set_kdf(Option<crate::deps::aws_cryptography_materialProviders::types::DerivationAlgorithm>)
: (undocumented)message_version(impl Into<Option<::std::primitive::i32>>)
/set_message_version(Option<::std::primitive::i32>)
: (undocumented)signature(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::SignatureAlgorithm>>)
/set_signature(Option<crate::deps::aws_cryptography_materialProviders::types::SignatureAlgorithm>)
: (undocumented)symmetric_signature(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::SymmetricSignatureAlgorithm>>)
/set_symmetric_signature(Option<crate::deps::aws_cryptography_materialProviders::types::SymmetricSignatureAlgorithm>)
: (undocumented)
- On success, responds with
Unit
with field(s): - On failure, responds with
SdkError<ValidAlgorithmSuiteInfoError>
Source§impl Client
impl Client
Sourcepub fn validate_commitment_policy_on_encrypt(
&self,
) -> ValidateCommitmentPolicyOnEncryptFluentBuilder
pub fn validate_commitment_policy_on_encrypt( &self, ) -> ValidateCommitmentPolicyOnEncryptFluentBuilder
Constructs a fluent builder for the ValidateCommitmentPolicyOnEncrypt
operation.
- The fluent builder is configurable:
algorithm(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>>)
/set_algorithm(Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>)
: (undocumented)commitment_policy(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::CommitmentPolicy>>)
/set_commitment_policy(Option<crate::deps::aws_cryptography_materialProviders::types::CommitmentPolicy>)
: (undocumented)
- On success, responds with
Unit
with field(s): - On failure, responds with
SdkError<ValidateCommitmentPolicyOnEncryptError>
Source§impl Client
impl Client
Sourcepub fn validate_commitment_policy_on_decrypt(
&self,
) -> ValidateCommitmentPolicyOnDecryptFluentBuilder
pub fn validate_commitment_policy_on_decrypt( &self, ) -> ValidateCommitmentPolicyOnDecryptFluentBuilder
Constructs a fluent builder for the ValidateCommitmentPolicyOnDecrypt
operation.
- The fluent builder is configurable:
algorithm(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>>)
/set_algorithm(Option<crate::deps::aws_cryptography_materialProviders::types::AlgorithmSuiteId>)
: (undocumented)commitment_policy(impl Into<Option<crate::deps::aws_cryptography_materialProviders::types::CommitmentPolicy>>)
/set_commitment_policy(Option<crate::deps::aws_cryptography_materialProviders::types::CommitmentPolicy>)
: (undocumented)
- On success, responds with
Unit
with field(s): - On failure, responds with
SdkError<ValidateCommitmentPolicyOnDecryptError>
Source§impl Client
impl Client
Sourcepub fn from_conf(input: MaterialProvidersConfig) -> Result<Self, Error>
pub fn from_conf(input: MaterialProvidersConfig) -> Result<Self, Error>
Creates a new client from the service Config
.
Examples found in repository?
40pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
41 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
42 let aes_key_bytes = generate_aes_key_bytes();
43
44 // 1. Create the keyring.
45 // The DynamoDb encryption client uses this to encrypt and decrypt items.
46 let mpl_config = MaterialProvidersConfig::builder().build()?;
47 let mpl = mpl_client::Client::from_conf(mpl_config)?;
48 let raw_aes_keyring = mpl
49 .create_raw_aes_keyring()
50 .key_name("my-aes-key-name")
51 .key_namespace("my-key-namespace")
52 .wrapping_key(aes_key_bytes)
53 .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16)
54 .send()
55 .await?;
56
57 // 2. Configure which attributes are encrypted and/or signed when writing new items.
58 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
59 // we must explicitly configure how they should be treated during item encryption:
60 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
61 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
62 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
63 let attribute_actions_on_encrypt = HashMap::from([
64 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
65 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
66 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
67 ]);
68
69 // 3. Configure which attributes we expect to be included in the signature
70 // when reading items. There are two options for configuring this:
71 //
72 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
73 // When defining your DynamoDb schema and deciding on attribute names,
74 // choose a distinguishing prefix (such as ":") for all attributes that
75 // you do not want to include in the signature.
76 // This has two main benefits:
77 // - It is easier to reason about the security and authenticity of data within your item
78 // when all unauthenticated data is easily distinguishable by their attribute name.
79 // - If you need to add new unauthenticated attributes in the future,
80 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
81 // and immediately start writing to that new attribute, without
82 // any other configuration update needed.
83 // Once you configure this field, it is not safe to update it.
84 //
85 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
86 // a set of attributes that should be considered unauthenticated when encountered
87 // on read. Be careful if you use this configuration. Do not remove an attribute
88 // name from this configuration, even if you are no longer writing with that attribute,
89 // as old items may still include this attribute, and our configuration needs to know
90 // to continue to exclude this attribute from the signature scope.
91 // If you add new attribute names to this field, you must first deploy the update to this
92 // field to all readers in your host fleet before deploying the update to start writing
93 // with that new attribute.
94 //
95 // For this example, we currently authenticate all attributes. To make it easier to
96 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
97 const UNSIGNED_ATTR_PREFIX: &str = ":";
98
99 // 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
100 let table_config = DynamoDbTableEncryptionConfig::builder()
101 .logical_table_name(ddb_table_name)
102 .partition_key_name("partition_key")
103 .sort_key_name("sort_key")
104 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
105 .keyring(raw_aes_keyring)
106 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
107 .build()?;
108
109 let table_configs = DynamoDbTablesEncryptionConfig::builder()
110 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
111 .build()?;
112
113 // 5. Create a new AWS SDK DynamoDb client using the Config above
114 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
115 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
116 .interceptor(DbEsdkInterceptor::new(table_configs)?)
117 .build();
118 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
119
120 // 6. Put an item into our table using the above client.
121 // Before the item gets sent to DynamoDb, it will be encrypted
122 // client-side, according to our configuration.
123 let item = HashMap::from([
124 (
125 "partition_key".to_string(),
126 AttributeValue::S("rawAesKeyringItem".to_string()),
127 ),
128 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
129 (
130 "sensitive_data".to_string(),
131 AttributeValue::S("encrypt and sign me!".to_string()),
132 ),
133 ]);
134
135 ddb.put_item()
136 .table_name(ddb_table_name)
137 .set_item(Some(item.clone()))
138 .send()
139 .await?;
140
141 // 7. Get the item back from our table using the same client.
142 // The client will decrypt the item client-side, and return
143 // back the original item.
144 let key_to_get = HashMap::from([
145 (
146 "partition_key".to_string(),
147 AttributeValue::S("rawAesKeyringItem".to_string()),
148 ),
149 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
150 ]);
151
152 let resp = ddb
153 .get_item()
154 .table_name(ddb_table_name)
155 .set_key(Some(key_to_get))
156 // In this example we configure a strongly consistent read
157 // because we perform a read immediately after a write (for demonstrative purposes).
158 // By default, reads are only eventually consistent.
159 // Read our docs to determine which read consistency to use for your application:
160 // https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
161 .consistent_read(true)
162 .send()
163 .await?;
164
165 assert_eq!(resp.item, Some(item));
166
167 println!("raw_aes_keyring successful.");
168 Ok(())
169}
More examples
34pub async fn encrypt_decrypt() -> Result<(), crate::BoxError> {
35 let kms_key_id = test_utils::TEST_KMS_KEY_ID;
36 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
37
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 let provider_config = MaterialProvidersConfig::builder().build()?;
43 let mat_prov = mpl_client::Client::from_conf(provider_config)?;
44 let kms_keyring = mat_prov
45 .create_aws_kms_mrk_multi_keyring()
46 .generator(kms_key_id)
47 .send()
48 .await?;
49
50 // 2. Configure which attributes are encrypted and/or signed when writing new items.
51 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
52 // we must explicitly configure how they should be treated during item encryption:
53 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
54 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
55 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
56 let attribute_actions_on_encrypt = HashMap::from([
57 ("partition_key".to_string(), CryptoAction::SignOnly),
58 ("sort_key".to_string(), CryptoAction::SignOnly),
59 ("attribute1".to_string(), CryptoAction::EncryptAndSign),
60 ("attribute2".to_string(), CryptoAction::SignOnly),
61 (":attribute3".to_string(), CryptoAction::DoNothing),
62 ]);
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 const UNSIGNED_ATTR_PREFIX: &str = ":";
93
94 // 4. Create the configuration for the DynamoDb Item Encryptor
95 let config = DynamoDbItemEncryptorConfig::builder()
96 .logical_table_name(ddb_table_name)
97 .partition_key_name("partition_key")
98 .sort_key_name("sort_key")
99 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
100 .keyring(kms_keyring)
101 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
102 // Specifying an algorithm suite is not required,
103 // but is done here to demonstrate how to do so.
104 // We suggest using the
105 // `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite,
106 // which includes AES-GCM with key derivation, signing, and key commitment.
107 // This is also the default algorithm suite if one is not specified in this config.
108 // For more information on supported algorithm suites, see:
109 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html
110 .algorithm_suite_id(
111 DbeAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKeyEcdsaP384SymsigHmacSha384,
112 )
113 .build()?;
114
115 // 5. Create the DynamoDb Item Encryptor
116 let item_encryptor = enc_client::Client::from_conf(config)?;
117
118 // 6. Directly encrypt a DynamoDb item using the DynamoDb Item Encryptor
119 let original_item = HashMap::from([
120 (
121 "partition_key".to_string(),
122 AttributeValue::S("ItemEncryptDecryptExample".to_string()),
123 ),
124 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
125 (
126 "attribute1".to_string(),
127 AttributeValue::S("encrypt and sign me!".to_string()),
128 ),
129 (
130 "attribute2".to_string(),
131 AttributeValue::S("sign me!".to_string()),
132 ),
133 (
134 ":attribute3".to_string(),
135 AttributeValue::S("ignore me!".to_string()),
136 ),
137 ]);
138
139 let encrypted_item = item_encryptor
140 .encrypt_item()
141 .plaintext_item(original_item.clone())
142 .send()
143 .await?
144 .encrypted_item
145 .unwrap();
146
147 // Demonstrate that the item has been encrypted
148 assert_eq!(
149 encrypted_item["partition_key"],
150 AttributeValue::S("ItemEncryptDecryptExample".to_string())
151 );
152 assert_eq!(
153 encrypted_item["sort_key"],
154 AttributeValue::N("0".to_string())
155 );
156 assert!(encrypted_item["attribute1"].is_b());
157 assert!(!encrypted_item["attribute1"].is_s());
158
159 // 7. Directly decrypt the encrypted item using the DynamoDb Item Encryptor
160 let decrypted_item = item_encryptor
161 .decrypt_item()
162 .encrypted_item(encrypted_item)
163 .send()
164 .await?
165 .plaintext_item
166 .unwrap();
167
168 // Demonstrate that GetItem succeeded and returned the decrypted item
169 assert_eq!(decrypted_item, original_item);
170 println!("encrypt_decrypt successful.");
171 Ok(())
172}
57pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
58 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
59
60 // You may provide your own RSA key pair in the files located at
61 // - EXAMPLE_RSA_PRIVATE_KEY_FILENAME
62 // - EXAMPLE_RSA_PUBLIC_KEY_FILENAME
63 // If these files are not present, this will generate a pair for you
64 if should_generate_new_rsa_key_pair()? {
65 generate_rsa_key_pair()?;
66 }
67
68 // 1. Load key pair from UTF-8 encoded PEM files.
69 // You may provide your own PEM files to use here.
70 // If you do not, the main method in this class will generate PEM
71 // files for example use. Do not use these files for any other purpose.
72
73 let mut file = File::open(Path::new(EXAMPLE_RSA_PUBLIC_KEY_FILENAME))?;
74 let mut public_key_utf8_bytes = Vec::new();
75 file.read_to_end(&mut public_key_utf8_bytes)?;
76
77 let mut file = File::open(Path::new(EXAMPLE_RSA_PRIVATE_KEY_FILENAME))?;
78 let mut private_key_utf8_bytes = Vec::new();
79 file.read_to_end(&mut private_key_utf8_bytes)?;
80
81 // 2. Create the keyring.
82 // The DynamoDb encryption client uses this to encrypt and decrypt items.
83 let mpl_config = MaterialProvidersConfig::builder().build()?;
84 let mpl = mpl_client::Client::from_conf(mpl_config)?;
85 let raw_rsa_keyring = mpl
86 .create_raw_rsa_keyring()
87 .key_name("my-rsa-key-name")
88 .key_namespace("my-key-namespace")
89 .padding_scheme(PaddingScheme::OaepSha256Mgf1)
90 .public_key(public_key_utf8_bytes)
91 .private_key(private_key_utf8_bytes)
92 .send()
93 .await?;
94
95 // 3. Configure which attributes are encrypted and/or signed when writing new items.
96 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
97 // we must explicitly configure how they should be treated during item encryption:
98 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
99 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
100 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
101 let attribute_actions_on_encrypt = HashMap::from([
102 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
103 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
104 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
105 ]);
106
107 // 4. Configure which attributes we expect to be included in the signature
108 // when reading items. There are two options for configuring this:
109 //
110 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
111 // When defining your DynamoDb schema and deciding on attribute names,
112 // choose a distinguishing prefix (such as ":") for all attributes that
113 // you do not want to include in the signature.
114 // This has two main benefits:
115 // - It is easier to reason about the security and authenticity of data within your item
116 // when all unauthenticated data is easily distinguishable by their attribute name.
117 // - If you need to add new unauthenticated attributes in the future,
118 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
119 // and immediately start writing to that new attribute, without
120 // any other configuration update needed.
121 // Once you configure this field, it is not safe to update it.
122 //
123 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
124 // a set of attributes that should be considered unauthenticated when encountered
125 // on read. Be careful if you use this configuration. Do not remove an attribute
126 // name from this configuration, even if you are no longer writing with that attribute,
127 // as old items may still include this attribute, and our configuration needs to know
128 // to continue to exclude this attribute from the signature scope.
129 // If you add new attribute names to this field, you must first deploy the update to this
130 // field to all readers in your host fleet before deploying the update to start writing
131 // with that new attribute.
132 //
133 // For this example, we currently authenticate all attributes. To make it easier to
134 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
135 const UNSIGNED_ATTR_PREFIX: &str = ":";
136
137 // 5. Create the DynamoDb Encryption configuration for the table we will be writing to.
138 let table_config = DynamoDbTableEncryptionConfig::builder()
139 .logical_table_name(ddb_table_name)
140 .partition_key_name("partition_key")
141 .sort_key_name("sort_key")
142 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
143 .keyring(raw_rsa_keyring)
144 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
145 .build()?;
146
147 let table_configs = DynamoDbTablesEncryptionConfig::builder()
148 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
149 .build()?;
150
151 // 6. Create a new AWS SDK DynamoDb client using the config above
152 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
153 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
154 .interceptor(DbEsdkInterceptor::new(table_configs)?)
155 .build();
156 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
157
158 // 7. Put an item into our table using the above client.
159 // Before the item gets sent to DynamoDb, it will be encrypted
160 // client-side, according to our configuration.
161 let item = HashMap::from([
162 (
163 "partition_key".to_string(),
164 AttributeValue::S("rawRsaKeyringItem".to_string()),
165 ),
166 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
167 (
168 "sensitive_data".to_string(),
169 AttributeValue::S("encrypt and sign me!".to_string()),
170 ),
171 ]);
172
173 ddb.put_item()
174 .table_name(ddb_table_name)
175 .set_item(Some(item.clone()))
176 .send()
177 .await?;
178
179 // 8. Get the item back from our table using the same client.
180 // The client will decrypt the item client-side, and return
181 // back the original item.
182
183 let key_to_get = HashMap::from([
184 (
185 "partition_key".to_string(),
186 AttributeValue::S("rawRsaKeyringItem".to_string()),
187 ),
188 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
189 ]);
190
191 let resp = ddb
192 .get_item()
193 .table_name(ddb_table_name)
194 .set_key(Some(key_to_get))
195 // In this example we configure a strongly consistent read
196 // because we perform a read immediately after a write (for demonstrative purposes).
197 // By default, reads are only eventually consistent.
198 // Read our docs to determine which read consistency to use for your application:
199 // https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
200 .consistent_read(true)
201 .send()
202 .await?;
203
204 assert_eq!(resp.item, Some(item));
205 println!("raw_rsa_keyring successful.");
206 Ok(())
207}
31pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
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()?;
40 let mat_prov = client::Client::from_conf(provider_config)?;
41 let kms_keyring = mat_prov
42 .create_aws_kms_mrk_multi_keyring()
43 .generator(kms_key_id)
44 .send()
45 .await?;
46
47 // 2. Configure which attributes are encrypted and/or signed when writing new items.
48 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
49 // we must explicitly configure how they should be treated during item encryption:
50 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
51 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
52 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
53 let attribute_actions_on_encrypt = HashMap::from([
54 ("partition_key".to_string(), CryptoAction::SignOnly),
55 ("sort_key".to_string(), CryptoAction::SignOnly),
56 ("attribute1".to_string(), CryptoAction::EncryptAndSign),
57 ("attribute2".to_string(), CryptoAction::SignOnly),
58 (":attribute3".to_string(), CryptoAction::DoNothing),
59 ]);
60
61 // 3. Configure which attributes we expect to be included in the signature
62 // when reading items. There are two options for configuring this:
63 //
64 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
65 // When defining your DynamoDb schema and deciding on attribute names,
66 // choose a distinguishing prefix (such as ":") for all attributes that
67 // you do not want to include in the signature.
68 // This has two main benefits:
69 // - It is easier to reason about the security and authenticity of data within your item
70 // when all unauthenticated data is easily distinguishable by their attribute name.
71 // - If you need to add new unauthenticated attributes in the future,
72 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
73 // and immediately start writing to that new attribute, without
74 // any other configuration update needed.
75 // Once you configure this field, it is not safe to update it.
76 //
77 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
78 // a set of attributes that should be considered unauthenticated when encountered
79 // on read. Be careful if you use this configuration. Do not remove an attribute
80 // name from this configuration, even if you are no longer writing with that attribute,
81 // as old items may still include this attribute, and our configuration needs to know
82 // to continue to exclude this attribute from the signature scope.
83 // If you add new attribute names to this field, you must first deploy the update to this
84 // field to all readers in your host fleet before deploying the update to start writing
85 // with that new attribute.
86 //
87 // For this example, we have designed our DynamoDb table such that any attribute name with
88 // the ":" prefix should be considered unauthenticated.
89 const UNSIGNED_ATTR_PREFIX: &str = ":";
90
91 // 4. Create the DynamoDb Encryption configuration for the table we will be writing to.
92 let table_config = DynamoDbTableEncryptionConfig::builder()
93 .logical_table_name(ddb_table_name)
94 .partition_key_name("partition_key")
95 .sort_key_name("sort_key")
96 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
97 .keyring(kms_keyring)
98 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
99 // Specifying an algorithm suite is not required,
100 // but is done here to demonstrate how to do so.
101 // We suggest using the
102 // `ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384_SYMSIG_HMAC_SHA384` suite,
103 // which includes AES-GCM with key derivation, signing, and key commitment.
104 // This is also the default algorithm suite if one is not specified in this config.
105 // For more information on supported algorithm suites, see:
106 // https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/supported-algorithms.html
107 .algorithm_suite_id(
108 DbeAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKeyEcdsaP384SymsigHmacSha384,
109 )
110 .build()?;
111
112 let table_configs = DynamoDbTablesEncryptionConfig::builder()
113 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
114 .build()?;
115
116 // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs
117 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
118 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
119 .interceptor(DbEsdkInterceptor::new(table_configs)?)
120 .build();
121 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
122
123 // 6. Put an item into our table using the above client.
124 // Before the item gets sent to DynamoDb, it will be encrypted
125 // client-side, according to our configuration.
126 let item = HashMap::from([
127 (
128 "partition_key".to_string(),
129 AttributeValue::S("BasicPutGetExample".to_string()),
130 ),
131 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
132 (
133 "attribute1".to_string(),
134 AttributeValue::S("encrypt and sign me!".to_string()),
135 ),
136 (
137 "attribute2".to_string(),
138 AttributeValue::S("sign me!".to_string()),
139 ),
140 (
141 ":attribute3".to_string(),
142 AttributeValue::S("ignore me!".to_string()),
143 ),
144 ]);
145
146 ddb.put_item()
147 .table_name(ddb_table_name)
148 .set_item(Some(item.clone()))
149 .send()
150 .await?;
151
152 // 7. Get the item back from our table using the same client.
153 // The client will decrypt the item client-side, and return
154 // back the original item.
155 let key_to_get = HashMap::from([
156 (
157 "partition_key".to_string(),
158 AttributeValue::S("BasicPutGetExample".to_string()),
159 ),
160 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
161 ]);
162
163 let resp = ddb
164 .get_item()
165 .table_name(ddb_table_name)
166 .set_key(Some(key_to_get))
167 // In this example we configure a strongly consistent read
168 // because we perform a read immediately after a write (for demonstrative purposes).
169 // By default, reads are only eventually consistent.
170 // Read our docs to determine which read consistency to use for your application:
171 // https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
172 .consistent_read(true)
173 .send()
174 .await?;
175
176 assert_eq!(resp.item, Some(item));
177 println!("put_item_get_item successful.");
178 Ok(())
179}
47pub async fn put_item_get_item() -> Result<(), crate::BoxError> {
48 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
49 let rsa_key_arn = test_utils::TEST_KMS_RSA_KEY_ID;
50
51 // You may provide your own RSA public key at EXAMPLE_RSA_PUBLIC_KEY_FILENAME.
52 // This must be the public key for the RSA key represented at rsaKeyArn.
53 // If this file is not present, this will write a UTF-8 encoded PEM file for you.
54 if should_get_new_public_key(DEFAULT_EXAMPLE_RSA_PUBLIC_KEY_FILENAME) {
55 write_public_key_pem_for_rsa_key(
56 test_utils::TEST_KMS_RSA_KEY_ID,
57 DEFAULT_EXAMPLE_RSA_PUBLIC_KEY_FILENAME,
58 )
59 .await?;
60 }
61
62 // 1. Load UTF-8 encoded public key PEM file.
63 // You may have an RSA public key file already defined.
64 // If not, the main method in this class will call
65 // the KMS RSA key, retrieve its public key, and store it
66 // in a PEM file for example use.
67 let mut file = File::open(Path::new(DEFAULT_EXAMPLE_RSA_PUBLIC_KEY_FILENAME))?;
68 let mut public_key_utf8_bytes = Vec::new();
69 file.read_to_end(&mut public_key_utf8_bytes)?;
70
71 // 2. Create a KMS RSA keyring.
72 // This keyring takes in:
73 // - kmsClient
74 // - kmsKeyId: Must be an ARN representing a KMS RSA key
75 // - publicKey: A ByteBuffer of a UTF-8 encoded PEM file representing the public
76 // key for the key passed into kmsKeyId
77 // - encryptionAlgorithm: Must be either RSAES_OAEP_SHA_256 or RSAES_OAEP_SHA_1
78 let mpl_config = MaterialProvidersConfig::builder().build()?;
79 let mpl = mpl_client::Client::from_conf(mpl_config)?;
80 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
81 let kms_rsa_keyring = mpl
82 .create_aws_kms_rsa_keyring()
83 .kms_key_id(rsa_key_arn)
84 .public_key(public_key_utf8_bytes)
85 .encryption_algorithm(aws_sdk_kms::types::EncryptionAlgorithmSpec::RsaesOaepSha256)
86 .kms_client(aws_sdk_kms::Client::new(&sdk_config))
87 .send()
88 .await?;
89
90 // 3. Configure which attributes are encrypted and/or signed when writing new items.
91 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
92 // we must explicitly configure how they should be treated during item encryption:
93 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
94 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
95 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
96 let attribute_actions_on_encrypt = HashMap::from([
97 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
98 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
99 ("sensitive_data".to_string(), CryptoAction::EncryptAndSign),
100 ]);
101
102 // 4. Configure which attributes we expect to be included in the signature
103 // when reading items. There are two options for configuring this:
104 //
105 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
106 // When defining your DynamoDb schema and deciding on attribute names,
107 // choose a distinguishing prefix (such as ":") for all attributes that
108 // you do not want to include in the signature.
109 // This has two main benefits:
110 // - It is easier to reason about the security and authenticity of data within your item
111 // when all unauthenticated data is easily distinguishable by their attribute name.
112 // - If you need to add new unauthenticated attributes in the future,
113 // you can easily make the corresponding update to your `attributeActions`
114 // and immediately start writing to that new attribute, without
115 // any other configuration update needed.
116 // Once you configure this field, it is not safe to update it.
117 //
118 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
119 // a set of attributes that should be considered unauthenticated when encountered
120 // on read. Be careful if you use this configuration. Do not remove an attribute
121 // name from this configuration, even if you are no longer writing with that attribute,
122 // as old items may still include this attribute, and our configuration needs to know
123 // to continue to exclude this attribute from the signature scope.
124 // If you add new attribute names to this field, you must first deploy the update to this
125 // field to all readers in your host fleet before deploying the update to start writing
126 // with that new attribute.
127 //
128 // For this example, we currently authenticate all attributes. To make it easier to
129 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
130 const UNSIGNED_ATTR_PREFIX: &str = ":";
131
132 // 5. Create the DynamoDb Encryption configuration for the table we will be writing to.
133 // Note: To use the KMS RSA keyring, your table config must specify an algorithmSuite
134 // that does not use asymmetric signing.
135 let table_config = DynamoDbTableEncryptionConfig::builder()
136 .logical_table_name(ddb_table_name)
137 .partition_key_name("partition_key")
138 .sort_key_name("sort_key")
139 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
140 .keyring(kms_rsa_keyring)
141 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
142 // Specify algorithmSuite without asymmetric signing here
143 // As of v3.0.0, the only supported algorithmSuite without asymmetric signing is
144 // ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_SYMSIG_HMAC_SHA384.
145 .algorithm_suite_id(DbeAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKeySymsigHmacSha384)
146 .build()?;
147
148 let table_configs = DynamoDbTablesEncryptionConfig::builder()
149 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
150 .build()?;
151
152 // 6. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above
153 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
154 .interceptor(DbEsdkInterceptor::new(table_configs)?)
155 .build();
156 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
157
158 // 7. Put an item into our table using the above client.
159 // Before the item gets sent to DynamoDb, it will be encrypted
160 // client-side, according to our configuration.
161 let item = HashMap::from([
162 (
163 "partition_key".to_string(),
164 AttributeValue::S("awsKmsRsaKeyringItem".to_string()),
165 ),
166 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
167 (
168 "sensitive_data".to_string(),
169 AttributeValue::S("encrypt and sign me!".to_string()),
170 ),
171 ]);
172
173 ddb.put_item()
174 .table_name(ddb_table_name)
175 .set_item(Some(item.clone()))
176 .send()
177 .await?;
178
179 // 8. Get the item back from our table using the client.
180 // The client will decrypt the item client-side using the RSA keyring
181 // and return the original item.
182 let key_to_get = HashMap::from([
183 (
184 "partition_key".to_string(),
185 AttributeValue::S("awsKmsRsaKeyringItem".to_string()),
186 ),
187 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
188 ]);
189
190 let resp = ddb
191 .get_item()
192 .table_name(ddb_table_name)
193 .set_key(Some(key_to_get))
194 .consistent_read(true)
195 .send()
196 .await?;
197
198 assert_eq!(resp.item, Some(item));
199 println!("kms_rsa_keyring successful.");
200 Ok(())
201}
63pub async fn put_item_get_item(
64 tenant1_branch_key_id: &str,
65 tenant2_branch_key_id: &str,
66) -> Result<(), crate::BoxError> {
67 let ddb_table_name = test_utils::TEST_DDB_TABLE_NAME;
68
69 let keystore_table_name = test_utils::TEST_KEYSTORE_NAME;
70 let logical_keystore_name = test_utils::TEST_LOGICAL_KEYSTORE_NAME;
71 let kms_key_id = test_utils::TEST_KEYSTORE_KMS_KEY_ID;
72
73 // Initial KeyStore Setup: This example requires that you have already
74 // created your KeyStore, and have populated it with two new branch keys.
75 // See the "Create KeyStore Table Example" and "Create KeyStore Key Example"
76 // for an example of how to do this.
77
78 // 1. Configure your KeyStore resource.
79 // This SHOULD be the same configuration that you used
80 // to initially create and populate your KeyStore.
81 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
82 let key_store_config = KeyStoreConfig::builder()
83 .kms_client(aws_sdk_kms::Client::new(&sdk_config))
84 .ddb_client(aws_sdk_dynamodb::Client::new(&sdk_config))
85 .ddb_table_name(keystore_table_name)
86 .logical_key_store_name(logical_keystore_name)
87 .kms_configuration(KmsConfiguration::KmsKeyArn(kms_key_id.to_string()))
88 .build()?;
89
90 let key_store = keystore_client::Client::from_conf(key_store_config)?;
91
92 // 2. Create a Branch Key ID Supplier. See ExampleBranchKeyIdSupplier in this directory.
93 let dbesdk_config = DynamoDbEncryptionConfig::builder().build()?;
94 let dbesdk = dbesdk_client::Client::from_conf(dbesdk_config)?;
95 let supplier = ExampleBranchKeyIdSupplier::new(tenant1_branch_key_id, tenant2_branch_key_id);
96
97 let branch_key_id_supplier = dbesdk
98 .create_dynamo_db_encryption_branch_key_id_supplier()
99 .ddb_key_branch_key_id_supplier(supplier)
100 .send()
101 .await?
102 .branch_key_id_supplier
103 .unwrap();
104
105 // 3. Create the Hierarchical Keyring, using the Branch Key ID Supplier above.
106 // With this configuration, the AWS SDK Client ultimately configured will be capable
107 // of encrypting or decrypting items for either tenant (assuming correct KMS access).
108 // If you want to restrict the client to only encrypt or decrypt for a single tenant,
109 // configure this Hierarchical Keyring using `.branchKeyId(tenant1BranchKeyId)` instead
110 // of `.branchKeyIdSupplier(branchKeyIdSupplier)`.
111 let mpl_config = MaterialProvidersConfig::builder().build()?;
112 let mpl = mpl_client::Client::from_conf(mpl_config)?;
113
114 let hierarchical_keyring = mpl
115 .create_aws_kms_hierarchical_keyring()
116 .branch_key_id_supplier(branch_key_id_supplier)
117 .key_store(key_store)
118 .ttl_seconds(600)
119 .send()
120 .await?;
121
122 // 4. Configure which attributes are encrypted and/or signed when writing new items.
123 // For each attribute that may exist on the items we plan to write to our DynamoDbTable,
124 // we must explicitly configure how they should be treated during item encryption:
125 // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
126 // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
127 // - DO_NOTHING: The attribute is not encrypted and not included in the signature
128 let attribute_actions_on_encrypt = HashMap::from([
129 ("partition_key".to_string(), CryptoAction::SignOnly), // Our partition attribute must be SIGN_ONLY
130 ("sort_key".to_string(), CryptoAction::SignOnly), // Our sort attribute must be SIGN_ONLY
131 (
132 "tenant_sensitive_data".to_string(),
133 CryptoAction::EncryptAndSign,
134 ),
135 ]);
136
137 // 5. Configure which attributes we expect to be included in the signature
138 // when reading items. There are two options for configuring this:
139 //
140 // - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
141 // When defining your DynamoDb schema and deciding on attribute names,
142 // choose a distinguishing prefix (such as ":") for all attributes that
143 // you do not want to include in the signature.
144 // This has two main benefits:
145 // - It is easier to reason about the security and authenticity of data within your item
146 // when all unauthenticated data is easily distinguishable by their attribute name.
147 // - If you need to add new unauthenticated attributes in the future,
148 // you can easily make the corresponding update to your `attributeActionsOnEncrypt`
149 // and immediately start writing to that new attribute, without
150 // any other configuration update needed.
151 // Once you configure this field, it is not safe to update it.
152 //
153 // - Configure `allowedUnsignedAttributes`: You may also explicitly list
154 // a set of attributes that should be considered unauthenticated when encountered
155 // on read. Be careful if you use this configuration. Do not remove an attribute
156 // name from this configuration, even if you are no longer writing with that attribute,
157 // as old items may still include this attribute, and our configuration needs to know
158 // to continue to exclude this attribute from the signature scope.
159 // If you add new attribute names to this field, you must first deploy the update to this
160 // field to all readers in your host fleet before deploying the update to start writing
161 // with that new attribute.
162 //
163 // For this example, we currently authenticate all attributes. To make it easier to
164 // add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
165 const UNSIGNED_ATTR_PREFIX: &str = ":";
166
167 // 6. Create the DynamoDb Encryption configuration for the table we will be writing to.
168 let table_config = DynamoDbTableEncryptionConfig::builder()
169 .logical_table_name(ddb_table_name)
170 .partition_key_name("partition_key")
171 .sort_key_name("sort_key")
172 .attribute_actions_on_encrypt(attribute_actions_on_encrypt)
173 .keyring(hierarchical_keyring)
174 .allowed_unsigned_attribute_prefix(UNSIGNED_ATTR_PREFIX)
175 .build()?;
176
177 let table_configs = DynamoDbTablesEncryptionConfig::builder()
178 .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)]))
179 .build()?;
180
181 // 7. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above
182 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
183 let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
184 .interceptor(DbEsdkInterceptor::new(table_configs)?)
185 .build();
186 let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
187
188 // 8. Put an item into our table using the above client.
189 // Before the item gets sent to DynamoDb, it will be encrypted
190 // client-side, according to our configuration.
191 // Because the item we are writing uses "tenantId1" as our partition value,
192 // based on the code we wrote in the ExampleBranchKeySupplier,
193 // `tenant1BranchKeyId` will be used to encrypt this item.
194 let item = HashMap::from([
195 (
196 "partition_key".to_string(),
197 AttributeValue::S("tenant1Id".to_string()),
198 ),
199 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
200 (
201 "tenant_sensitive_data".to_string(),
202 AttributeValue::S("encrypt and sign me!".to_string()),
203 ),
204 ]);
205
206 ddb.put_item()
207 .table_name(ddb_table_name)
208 .set_item(Some(item.clone()))
209 .send()
210 .await?;
211
212 // 9. Get the item back from our table using the same client.
213 // The client will decrypt the item client-side, and return
214 // back the original item.
215 // Because the returned item's partition value is "tenantId1",
216 // based on the code we wrote in the ExampleBranchKeySupplier,
217 // `tenant1BranchKeyId` will be used to decrypt this item.
218 let key_to_get = HashMap::from([
219 (
220 "partition_key".to_string(),
221 AttributeValue::S("tenant1Id".to_string()),
222 ),
223 ("sort_key".to_string(), AttributeValue::N("0".to_string())),
224 ]);
225
226 let resp = ddb
227 .get_item()
228 .table_name(ddb_table_name)
229 .set_key(Some(key_to_get))
230 .consistent_read(true)
231 .send()
232 .await?;
233
234 assert_eq!(resp.item, Some(item));
235 println!("hierarchical_keyring successful.");
236 Ok(())
237}
- examples/keyring/mrk_discovery_multi_keyring.rs
- examples/keyring/multi_keyring.rs
- examples/multi_get_put_example.rs
- examples/clientsupplier/client_supplier_example.rs
- examples/keyring/multi_mrk_keyring.rs
- examples/searchableencryption/compound_beacon_searchable_encryption.rs
- examples/searchableencryption/beacon_styles_searchable_encryption.rs
- examples/searchableencryption/virtual_beacon_searchable_encryption.rs
- examples/searchableencryption/basic_searchable_encryption.rs
- examples/searchableencryption/complexexample/beacon_config.rs
Trait Implementations§
impl StructuralPartialEq for Client
Auto Trait Implementations§
impl Freeze for Client
impl !RefUnwindSafe for Client
impl Send for Client
impl Sync for Client
impl Unpin for Client
impl !UnwindSafe for Client
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more