#[non_exhaustive]pub struct EncryptedPart {
pub name: Option<String>,
pub prefix: Option<String>,
}
Expand description
A part of a Compound Beacon that contains a beacon over encrypted data.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional
Struct { .. }
syntax; cannot be matched against without a wildcard ..
; and struct update syntax will not work.name: Option<String>
The name of the Standard Beacon, whose value this Part will hold.
prefix: Option<String>
The prefix that is written with this Encrypted Part.
Implementations§
Source§impl EncryptedPart
impl EncryptedPart
Source§impl EncryptedPart
impl EncryptedPart
Sourcepub fn builder() -> EncryptedPartBuilder
pub fn builder() -> EncryptedPartBuilder
Creates a new builder-style object to manufacture EncryptedPart
.
Examples found in repository?
examples/searchableencryption/compound_beacon_searchable_encryption.rs (line 99)
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}
More examples
examples/searchableencryption/beacon_styles_searchable_encryption.rs (line 110)
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}
examples/searchableencryption/complexexample/beacon_config.rs (line 138)
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}
Trait Implementations§
Source§impl Clone for EncryptedPart
impl Clone for EncryptedPart
Source§fn clone(&self) -> EncryptedPart
fn clone(&self) -> EncryptedPart
Returns a duplicate of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source
. Read moreSource§impl Debug for EncryptedPart
impl Debug for EncryptedPart
Source§impl PartialEq for EncryptedPart
impl PartialEq for EncryptedPart
impl StructuralPartialEq for EncryptedPart
Auto Trait Implementations§
impl Freeze for EncryptedPart
impl RefUnwindSafe for EncryptedPart
impl Send for EncryptedPart
impl Sync for EncryptedPart
impl Unpin for EncryptedPart
impl UnwindSafe for EncryptedPart
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
Mutably borrows from an owned value. Read more
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>
Converts
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>
Converts
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 moreCreates a shared type from an unshared type.