Skip to content

Commit 35662f0

Browse files
committed
m
1 parent 5f48deb commit 35662f0

File tree

9 files changed

+258
-11
lines changed

9 files changed

+258
-11
lines changed

DynamoDbEncryption/runtimes/rust/Cargo.toml

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

DynamoDbEncryption/runtimes/rust/src/concurrent_call.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![deny(warnings, unconditional_panic)]
55
#![deny(nonstandard_style)]
66
#![deny(clippy::all)]
7+
#![allow(dead_code)]
78

89
#[allow(non_snake_case)]
910
pub mod ConcurrentCall {

DynamoDbEncryption/runtimes/rust/src/ecdh.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![deny(warnings, unconditional_panic)]
55
#![deny(nonstandard_style)]
66
#![deny(clippy::all)]
7+
#![allow(dead_code)]
78

89
#[allow(non_snake_case)]
910
pub mod ECDH {

DynamoDbEncryption/runtimes/rust/src/intercept.rs

+25-9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ static dafny_tokio_runtime: LazyLock<tokio::runtime::Runtime> = LazyLock::new(||
1818
.unwrap()
1919
});
2020

21+
2122
#[macro_export]
2223
macro_rules! modify_request {
2324
($cfg:ident,$request:ident,$self:ident,$transform:ident) => {
@@ -26,14 +27,23 @@ macro_rules! modify_request {
2627
$cfg.interceptor_state().store_put(OriginalRequest(Input::erase($request.clone())));
2728

2829
// transform the request
29-
*$request = dafny_tokio_runtime.block_on($self.client
30-
.$transform()
31-
.sdk_input($request.clone())
32-
.send()).unwrap().transformed_input.unwrap();
30+
*$request = tokio::task::block_in_place(|| {
31+
tokio::runtime::Handle::current().block_on(async {
32+
$self.client
33+
.$transform()
34+
.sdk_input($request.clone())
35+
.send()
36+
.await
37+
.unwrap().transformed_input.unwrap()
38+
})
39+
})
3340
}
3441
};
3542
}
3643

44+
45+
46+
3747
#[macro_export]
3848
macro_rules! modify_response {
3949
($cfg:ident,$type:ty,$response:ident,$self:ident,$transform:ident) => {
@@ -48,11 +58,17 @@ macro_rules! modify_response {
4858
.expect("we know this type corresponds to the output type");
4959

5060
// transform the response
51-
*$response = dafny_tokio_runtime.block_on($self.client
52-
.$transform()
53-
.original_input(original.clone())
54-
.sdk_output($response.clone())
55-
.send()).unwrap().transformed_output.unwrap();
61+
*$response = tokio::task::block_in_place(|| {
62+
tokio::runtime::Handle::current().block_on(async {
63+
$self.client
64+
.$transform()
65+
.original_input(original.clone())
66+
.sdk_output($response.clone())
67+
.send()
68+
.await
69+
.unwrap().transformed_output.unwrap()
70+
})
71+
})
5672
}
5773
};
5874
}

DynamoDbEncryption/runtimes/rust/src/kms.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,28 @@ impl crate::r#software::amazon::cryptography::services::kms::internaldafny::_def
1313
#[allow(non_snake_case)]
1414
pub fn KMSClientForRegion(region: &::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>) -> ::std::rc::Rc<crate::r#_Wrappers_Compile::Result<::dafny_runtime::Object<dyn crate::software::amazon::cryptography::services::kms::internaldafny::types::IKMSClient>, ::std::rc::Rc<crate::software::amazon::cryptography::services::kms::internaldafny::types::Error>>>{
1515
let region = dafny_runtime::dafny_runtime_conversions::unicode_chars_false::dafny_string_to_string(region);
16-
let shared_config = dafny_tokio_runtime().block_on(aws_config::load_defaults(aws_config::BehaviorVersion::v2024_03_28()));
16+
// let shared_config = dafny_tokio_runtime().block_on(aws_config::load_defaults(aws_config::BehaviorVersion::v2024_03_28()));
17+
let shared_config = tokio::task::block_in_place(|| {
18+
tokio::runtime::Handle::current().block_on(async {
19+
aws_config::load_defaults(aws_config::BehaviorVersion::v2024_03_28()).await
20+
})
21+
});
22+
1723
let shared_config = shared_config.to_builder().region(Region::new(region)).build();
1824
let inner = aws_sdk_kms::Client::new(&shared_config);
1925
let client = crate::deps::com_amazonaws_kms::client::Client { inner };
2026
let dafny_client = ::dafny_runtime::upcast_object()(::dafny_runtime::object::new(client));
2127
std::rc::Rc::new(crate::r#_Wrappers_Compile::Result::Success { value: dafny_client })
2228
}
29+
/*
30+
let res = task::spawn_blocking(move || {
31+
// Stand-in for compute-heavy work or using synchronous APIs
32+
v.push_str("world");
33+
// Pass ownership of the value back to the asynchronous context
34+
v
35+
}).await?;
36+
37+
*/
2338

2439
#[allow(non_snake_case)]
2540
pub fn KMSClient() -> ::std::rc::Rc<crate::r#_Wrappers_Compile::Result<::dafny_runtime::Object<dyn crate::software::amazon::cryptography::services::kms::internaldafny::types::IKMSClient>, ::std::rc::Rc<crate::software::amazon::cryptography::services::kms::internaldafny::types::Error>>>{

submodules/smithy-dafny

0 commit comments

Comments
 (0)