Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG-rust.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog

## 1.3.0 (2026-07-01)

### Features -- All Languages

- **dafny:** Add bucket beacon support ([#1943](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1943)) ([5c53d59](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/5c53d5983a90f93cf5d85d1399704eda0d16d63a))

### Fixes -- All Languages

- validate attribute name length by UTF-8 byte count ([#2329](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/2329)) ([1520838](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/1520838e319868cdce363f30beca8a221cddc128))

### Maintenance -- All Languages

- **dafny:** add length check validation for beacon key condition expressions ([#2348](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/2348)) ([0b21d4b](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/0b21d4b0115a09ce5159129b4f0b84faa1354764))
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Note: Starting April 20, 2026, all runtime-specific changes are tracked in separ

- Java: [CHANGELOG-java.md](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/CHANGELOG-java.md)
- .NET: [CHANGELOG-net.md](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/CHANGELOG-net.md)
- Rust: [CHANGELOG-rust.md](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/CHANGELOG-rust.md)

If a runtime-specific changelog is not present, there has been no new release for that runtime after April 20, 2026.

Expand Down
2 changes: 1 addition & 1 deletion DynamoDbEncryption/runtimes/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aws-db-esdk"
version = "1.2.4"
version = "1.3.0"
edition = "2021"
keywords = ["cryptography", "security", "dynamodb", "encryption", "client-side"]
license = "ISC AND (Apache-2.0 OR ISC)"
Expand Down
9 changes: 5 additions & 4 deletions releases/rust/db_esdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aws-db-esdk"
version = "1.2.4"
version = "1.3.0"
edition = "2021"
keywords = ["cryptography", "security", "dynamodb", "encryption", "client-side"]
license = "ISC AND (Apache-2.0 OR ISC)"
Expand All @@ -16,15 +16,16 @@ readme = "README.md"

[dependencies]
aws-config = "1.8.12"
aws-lc-rs = {version = "1.15.4"}
aws-lc-sys = { version = "0.39", optional = true }
aws-lc-fips-sys = { version = "0.13", optional = true }
aws-lc-rs = {version = "1.17.0"}
aws-lc-sys = { version = "0.42", optional = true }
aws-lc-fips-sys = { version = "0.13.1", optional = true }
aws-sdk-dynamodb = "1.103.0"
aws-sdk-kms = "1.98.0"
aws-smithy-runtime-api = {version = "1.10.0", features = ["client"] }
# aws-smithy-types has a coherence conflict (E0119) with time >= 0.3.37 on rustc >= 1.80.
# time 0.3.37+ introduces ModifierValue associated type impls that conflict with
# CanDisable<T>'s blanket `impl<T> From<T>`. Pin time until upstream fixes it.
# See: https://github.com/time-rs/time/issues/720
time = ">=0.3.0, <0.3.37"
aws-smithy-types = "1.3.6"
chrono = "0.4.43"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,11 @@ pub async fn put_and_query_with_beacon(branch_key_id: &str) -> Result<(), crate:
// values are uniformly distributed across its range of possible values.
// In many use cases, the prefix of an identifier encodes some information
// about that identifier (e.g. zipcode and SSN prefixes encode geographic
// information), while the suffix does not and is more uniformly distributed.
// information), while the suffix does not encode such structure and tends
// to be closer to uniformly distributed, though not perfectly uniform.
// We will assume that the inspector ID field matches a similar use case.
// So for this example, we only store and use the last
// 4 digits of the inspector ID, which we assume is uniformly distributed.
// For this example, we use only the last four digits of the inspector ID and apply one partitions,
// assuming the suffix has sufficient uniformity for this purpose.
// Since the full ID's range is divisible by the range of the last 4 digits,
// then the last 4 digits of the inspector ID are uniformly distributed
// over the range from 0 to 9,999.
Expand Down Expand Up @@ -114,12 +115,14 @@ pub async fn put_and_query_with_beacon(branch_key_id: &str) -> Result<(), crate:
let last4_beacon = StandardBeacon::builder()
.name("inspector_id_last4")
.length(10)
.number_of_partitions(1)
.build()?;

// The configured DDB table has a GSI on the `aws_dbe_b_unit` AttributeName.
// This field holds a unit serial number.
// For this example, this is a 12-digit integer from 0 to 999,999,999,999 (10^12 possible values).
// We will assume values for this attribute are uniformly distributed across this range.
// We will assume values for this attribute are uniformly distributed across this range
// using one partitions.
// A single unit serial number may be assigned to multiple `work_id`s.
//
// This link provides guidance for choosing a beacon length:
Expand All @@ -142,7 +145,11 @@ pub async fn put_and_query_with_beacon(branch_key_id: &str) -> Result<(), crate:
// With a sufficiently large number of well-distributed inspector IDs,
// for a particular beacon we expect (10^12/2^30) ~= 931.3 unit serial numbers
// sharing that beacon value.
let unit_beacon = StandardBeacon::builder().name("unit").length(30).build()?;
let unit_beacon = StandardBeacon::builder()
.name("unit")
.length(30)
.number_of_partitions(1)
.build()?;

let standard_beacon_list = vec![last4_beacon, unit_beacon];

Expand All @@ -169,6 +176,8 @@ pub async fn put_and_query_with_beacon(branch_key_id: &str) -> Result<(), crate:
// 3. Create BeaconVersion.
// The BeaconVersion inside the list holds the list of beacons on the table.
// The BeaconVersion also stores information about the keystore.
// The BeaconVersion parameter specifies the maximumNumberOfPartitions associated with a given beacon configuration.
// "maximumNumberOfPartitions" must be greater than "numberOfPartitions"; we set it to 8 to allow room for future expansion.
// BeaconVersion must be provided:
// - keyStore: The keystore configured in step 2.
// - keySource: A configuration for the key source.
Expand All @@ -182,6 +191,8 @@ pub async fn put_and_query_with_beacon(branch_key_id: &str) -> Result<(), crate:
let beacon_version = BeaconVersion::builder()
.standard_beacons(standard_beacon_list)
.version(1) // MUST be 1
.maximum_number_of_partitions(8)
.default_number_of_partitions(1) //For beacons that do not require partitioning, only a single partition is used.
.key_store(key_store.clone())
.key_source(BeaconKeySource::Single(
SingleKeyStore::builder()
Expand Down Expand Up @@ -305,54 +316,71 @@ pub async fn put_and_query_with_beacon(branch_key_id: &str) -> Result<(), crate:
// This procedure is internal to the client and is abstracted away from the user;
// e.g. the user will only see "123456789012" and never
// "098765432109", though the actual query returned both.

let mut all_results: Vec<HashMap<String, AttributeValue>> = Vec::new();

let expression_attributes_names = HashMap::from([
("#last4".to_string(), "inspector_id_last4".to_string()),
("#unit".to_string(), "unit".to_string()),
]);

let expression_attribute_values = HashMap::from([
(":last4".to_string(), AttributeValue::S("4321".to_string())),
(
":unit".to_string(),
AttributeValue::S("123456789012".to_string()),
),
]);
// In this simple example, we know there are one buckets,
// but in general the number can be obtained using transformClient.getNumberOfQueries(query)
let num_queries = 1;

//We need to query for all possible parttions

for partition in 0..num_queries {
let expression_attribute_values = HashMap::from([
(":last4".to_string(), AttributeValue::S("4321".to_string())),
(
":unit".to_string(),
AttributeValue::S("123456789012".to_string()),
),
(
":aws_dbe_partition".to_string(),
AttributeValue::N(partition.to_string()),
),
]);

// GSIs do not update instantly
// so if the results come back empty
// we retry after a short sleep
for _i in 0..10 {
let query_response = ddb
.query()
.table_name(ddb_table_name)
.index_name(GSI_NAME)
.key_condition_expression("#last4 = :last4 and #unit = :unit")
.set_expression_attribute_names(Some(expression_attributes_names.clone()))
.set_expression_attribute_values(Some(expression_attribute_values.clone()))
.send()
.await?;

if let Some(items) = query_response.items {
if !items.is_empty() {
all_results.extend(items.clone());
break;
}
}

// GSIs do not update instantly
// so if the results come back empty
// we retry after a short sleep
for _i in 0..10 {
let query_response = ddb
.query()
.table_name(ddb_table_name)
.index_name(GSI_NAME)
.key_condition_expression("#last4 = :last4 and #unit = :unit")
.set_expression_attribute_names(Some(expression_attributes_names.clone()))
.set_expression_attribute_values(Some(expression_attribute_values.clone()))
.send()
.await?;

// if no results, sleep and try again
if query_response.items.is_none() || query_response.items.as_ref().unwrap().is_empty() {
std::thread::sleep(std::time::Duration::from_millis(20));
continue;
}

let attribute_values = query_response.items.unwrap();
// Validate only 1 item was returned: the item we just put
assert_eq!(attribute_values.len(), 1);
let returned_item = &attribute_values[0];
// Validate the item has the expected attributes
assert_eq!(
returned_item["inspector_id_last4"],
AttributeValue::S("4321".to_string())
);
assert_eq!(
returned_item["unit"],
AttributeValue::S("123456789012".to_string())
);
break;
}
let attribute_values = all_results;
// Validate only 1 item was returned: the item we just put
assert_eq!(attribute_values.len(), 1);
let returned_item = &attribute_values[0];
// Validate the item has the expected attributes
assert_eq!(
returned_item["inspector_id_last4"],
AttributeValue::S("4321".to_string())
);
assert_eq!(
returned_item["unit"],
AttributeValue::S("123456789012".to_string())
);

println!("basic_searchable_encryption successful.");
Ok(())
};
Expand Down
2 changes: 2 additions & 0 deletions releases/rust/db_esdk/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,5 @@ mod execute_transaction_input_transform;
mod execute_transaction_output_transform;

mod resolve_attributes;

mod get_number_of_queries;
19 changes: 19 additions & 0 deletions releases/rust/db_esdk/src/client/get_number_of_queries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Do not modify this file. This file is machine generated, and any changes to it will be overwritten.
impl crate::client::Client {
/// Constructs a fluent builder for the [`GetNumberOfQueries`](crate::operation::get_number_of_queries::builders::GetNumberOfQueriesFluentBuilder) operation.
///
/// - The fluent builder is configurable:
/// - [`input(impl Into<Option<aws_sdk_dynamodb::operation::query::QueryInput>>)`](crate::operation::get_number_of_queries::builders::GetNumberOfQueriesFluentBuilder::input) / [`set_input(Option<aws_sdk_dynamodb::operation::query::QueryInput>)`](crate::operation::get_number_of_queries::builders::GetNumberOfQueriesFluentBuilder::set_input): (undocumented)<br>
/// - On success, responds with [`GetNumberOfQueriesOutput`](crate::operation::get_number_of_queries::GetNumberOfQueriesOutput) with field(s):
/// - [`number_of_queries(Option<::std::primitive::i32>)`](crate::operation::get_number_of_queries::GetNumberOfQueriesOutput::number_of_queries): (undocumented)
/// - On failure, responds with [`SdkError<GetNumberOfQueriesError>`](crate::operation::get_number_of_queries::GetNumberOfQueriesError)
pub fn get_number_of_queries(
&self,
) -> crate::operation::get_number_of_queries::builders::GetNumberOfQueriesFluentBuilder {
crate::operation::get_number_of_queries::builders::GetNumberOfQueriesFluentBuilder::new(
self.clone(),
)
}
}
6 changes: 6 additions & 0 deletions releases/rust/db_esdk/src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ pub mod get_item_output_transform_input;

pub mod get_item_output_transform_output;

pub mod get_number_of_queries;

pub mod get_number_of_queries_input;

pub mod get_number_of_queries_output;

pub mod put_item_input_transform;

pub mod put_item_input_transform_input;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Do not modify this file. This file is machine generated, and any changes to it will be overwritten.

pub mod _get_number_of_queries_input;

pub mod _get_number_of_queries_output;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Do not modify this file. This file is machine generated, and any changes to it will be overwritten.
#[allow(dead_code)]
pub fn to_dafny(
value: crate::operation::get_number_of_queries::GetNumberOfQueriesInput,
) -> ::dafny_runtime::Rc<
crate::r#software::amazon::cryptography::dbencryptionsdk::dynamodb::transforms::internaldafny::types::GetNumberOfQueriesInput,
>{
::dafny_runtime::Rc::new(crate::r#software::amazon::cryptography::dbencryptionsdk::dynamodb::transforms::internaldafny::types::GetNumberOfQueriesInput::GetNumberOfQueriesInput {
input: crate::deps::com_amazonaws_dynamodb::conversions::query::_query_request::to_dafny(&value.input.clone().unwrap())
,
})
}
#[allow(dead_code)]
pub fn from_dafny(
dafny_value: ::dafny_runtime::Rc<
crate::r#software::amazon::cryptography::dbencryptionsdk::dynamodb::transforms::internaldafny::types::GetNumberOfQueriesInput,
>,
) -> crate::operation::get_number_of_queries::GetNumberOfQueriesInput {
crate::operation::get_number_of_queries::GetNumberOfQueriesInput::builder()
.set_input(Some(
crate::deps::com_amazonaws_dynamodb::conversions::query::_query_request::from_dafny(
dafny_value.input().clone(),
),
))
.build()
.unwrap()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Do not modify this file. This file is machine generated, and any changes to it will be overwritten.
#[allow(dead_code)]
pub fn to_dafny(
value: crate::operation::get_number_of_queries::GetNumberOfQueriesOutput,
) -> ::dafny_runtime::Rc<
crate::r#software::amazon::cryptography::dbencryptionsdk::dynamodb::transforms::internaldafny::types::GetNumberOfQueriesOutput,
>{
::dafny_runtime::Rc::new(crate::r#software::amazon::cryptography::dbencryptionsdk::dynamodb::transforms::internaldafny::types::GetNumberOfQueriesOutput::GetNumberOfQueriesOutput {
numberOfQueries: value.number_of_queries.clone().unwrap(),
})
}
#[allow(dead_code)]
pub fn from_dafny(
dafny_value: ::dafny_runtime::Rc<
crate::r#software::amazon::cryptography::dbencryptionsdk::dynamodb::transforms::internaldafny::types::GetNumberOfQueriesOutput,
>,
) -> crate::operation::get_number_of_queries::GetNumberOfQueriesOutput {
crate::operation::get_number_of_queries::GetNumberOfQueriesOutput::builder()
.set_number_of_queries(Some(dafny_value.numberOfQueries().clone()))
.build()
.unwrap()
}
Loading
Loading